decodingdatascience commited on
Commit
9a1b219
·
verified ·
1 Parent(s): 35f725f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +102 -30
app.py CHANGED
@@ -1,4 +1,5 @@
1
- # app.py — Minimal RAG over ./data/insurance.pdf with LlamaIndex + Pinecone
 
2
 
3
  import os
4
  import logging
@@ -19,10 +20,17 @@ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
19
  PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME", "dds-insurance-index")
20
  PINECONE_REGION = os.getenv("PINECONE_REGION", "us-east-1")
21
  PINECONE_CLOUD = os.getenv("PINECONE_CLOUD", "aws")
22
- EMBED_MODEL = os.getenv("EMBED_MODEL", "text-embedding-3-small") # 1536 dims
23
  LLM_MODEL = os.getenv("LLM_MODEL", "gpt-4o-mini")
24
 
25
- DATA_DIR = "data" # place insurance.pdf inside this folder
 
 
 
 
 
 
 
26
 
27
  if not PINECONE_API_KEY:
28
  raise RuntimeError("Missing PINECONE_API_KEY (set it in your Space → Settings → Variables).")
@@ -55,49 +63,113 @@ def ensure_index(name: str, dim: int = 1536):
55
  pinecone_index = ensure_index(PINECONE_INDEX_NAME, dim=1536)
56
  vector_store = PineconeVectorStore(pinecone_index=pinecone_index)
57
 
58
- # Build once on startup if index is empty (idempotent — safe to re-run)
59
  def bootstrap_index():
60
- # If you want a quick “is empty” check, you can skip or keep this; many set-ups
61
- # just upsert blindly (Pinecone dedup keys if you supply your own ids).
62
- log.info("Loading documents from ./data ...")
63
  if not os.path.isdir(DATA_DIR):
64
- raise RuntimeError("No 'data/' directory found. Create it and add insurance.pdf.")
65
 
66
- # Read everything in ./data (PDF/TXT/DOCX supported by LlamaIndex readers)
67
  docs = SimpleDirectoryReader(DATA_DIR).load_data()
 
 
68
 
69
  log.info(f"Docs loaded: {len(docs)}. Upserting into Pinecone…")
70
  storage_ctx = StorageContext.from_defaults(vector_store=vector_store)
71
-
72
- # Creates a VectorStoreIndex that writes directly to Pinecone
73
  VectorStoreIndex.from_documents(docs, storage_context=storage_ctx, show_progress=True)
74
  log.info("Index upsert complete.")
75
 
76
- # Initialize the index once at app start
77
  bootstrap_index()
78
 
79
- # Lightweight query function (wraps the existing vector store)
80
- def answer(query: str, top_k: int = 4) -> str:
81
- if not query.strip():
82
- return "Please enter a question about the insurance document."
83
  index = VectorStoreIndex.from_vector_store(vector_store)
84
- engine = index.as_query_engine(similarity_top_k=top_k)
85
  resp = engine.query(query)
86
  return str(resp)
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  # ========== UI ==========
89
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
90
- gr.Markdown("<h1 style='text-align:center;'>Insurance Q&A (RAG)</h1>")
91
- gr.Markdown(
92
- "This app indexes the file(s) in <code>./data</code> (e.g., <b>insurance.pdf</b>) "
93
- "into Pinecone, then answers questions using LlamaIndex + OpenAI."
94
- )
95
- q = gr.Textbox(label="Ask a question", placeholder="e.g., What is covered under outpatient benefits?")
96
- topk = gr.Slider(1, 10, value=4, step=1, label="Top-K matches")
97
- btn = gr.Button("Ask")
98
- out = gr.Markdown()
99
-
100
- btn.click(answer, inputs=[q, topk], outputs=[out])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  if __name__ == "__main__":
103
- demo.launch()
 
1
+ # app.py — Insurance Q&A (RAG) with Omantel branding, FAQ dropdown, no Top-K control
2
+ # Hugging Face Spaces (Gradio) – uses Pinecone + LlamaIndex + OpenAI
3
 
4
  import os
5
  import logging
 
20
  PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME", "dds-insurance-index")
21
  PINECONE_REGION = os.getenv("PINECONE_REGION", "us-east-1")
22
  PINECONE_CLOUD = os.getenv("PINECONE_CLOUD", "aws")
23
+ EMBED_MODEL = os.getenv("EMBED_MODEL", "text-embedding-3-small") # 1536-dim
24
  LLM_MODEL = os.getenv("LLM_MODEL", "gpt-4o-mini")
25
 
26
+ DATA_DIR = "data" # Place documents (e.g., insurance.pdf) here
27
+ DEFAULT_TOP_K = 4 # Internal similarity_top_k (no UI slider)
28
+
29
+ # Omantel branding (remote image is fine for Spaces UI)
30
+ LOGO_URL = os.getenv(
31
+ "LOGO_URL",
32
+ "https://raw.githubusercontent.com/Decoding-Data-Science/Omantel/main/download%20(36).png"
33
+ )
34
 
35
  if not PINECONE_API_KEY:
36
  raise RuntimeError("Missing PINECONE_API_KEY (set it in your Space → Settings → Variables).")
 
63
  pinecone_index = ensure_index(PINECONE_INDEX_NAME, dim=1536)
64
  vector_store = PineconeVectorStore(pinecone_index=pinecone_index)
65
 
 
66
  def bootstrap_index():
67
+ """Index all files in ./data into Pinecone (idempotent safe)."""
 
 
68
  if not os.path.isdir(DATA_DIR):
69
+ raise RuntimeError("No 'data/' directory found. Commit your documents to data/ in the Space repo.")
70
 
71
+ log.info("Loading documents from ./data ...")
72
  docs = SimpleDirectoryReader(DATA_DIR).load_data()
73
+ if not docs:
74
+ raise RuntimeError("No documents found in data/. Add e.g., data/insurance.pdf")
75
 
76
  log.info(f"Docs loaded: {len(docs)}. Upserting into Pinecone…")
77
  storage_ctx = StorageContext.from_defaults(vector_store=vector_store)
 
 
78
  VectorStoreIndex.from_documents(docs, storage_context=storage_ctx, show_progress=True)
79
  log.info("Index upsert complete.")
80
 
81
+ # Build once at startup
82
  bootstrap_index()
83
 
84
+ def answer(query: str) -> str:
85
+ """Query the existing vector store and return an answer string."""
86
+ if not query or not query.strip():
87
+ return "Please enter a question (or select one from the FAQ list)."
88
  index = VectorStoreIndex.from_vector_store(vector_store)
89
+ engine = index.as_query_engine(similarity_top_k=DEFAULT_TOP_K)
90
  resp = engine.query(query)
91
  return str(resp)
92
 
93
+ # ---- Frequently Asked Questions (edit to your document) ----
94
+ FAQS = [
95
+ "",
96
+ "What benefits are covered under the policy?",
97
+ "How do I file a claim and what documents are required?",
98
+ "What are the exclusions and limitations?",
99
+ "Is pre-authorization needed for hospitalization?",
100
+ "What is the reimbursement timeline?",
101
+ "How are outpatient vs inpatient services handled?",
102
+ "How can I check my network hospitals/clinics?",
103
+ "What is the co-pay or deductible policy?",
104
+ ]
105
+
106
+ def use_faq(selected_faq: str, free_text: str):
107
+ """
108
+ If a FAQ is selected, prefer it; otherwise use free_text.
109
+ Returns the chosen prompt (echo in UI) and the model answer.
110
+ """
111
+ prompt = (selected_faq or "").strip() or (free_text or "").strip()
112
+ if not prompt:
113
+ return "", "Please select a FAQ or type your question."
114
+ return prompt, answer(prompt)
115
+
116
  # ========== UI ==========
117
+ CSS = """
118
+ .header {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 12px;
122
+ justify-content: center;
123
+ margin-top: 8px;
124
+ }
125
+ .header img {
126
+ height: 42px;
127
+ }
128
+ .header h1 {
129
+ margin: 0;
130
+ font-weight: 700;
131
+ font-size: 1.4rem;
132
+ }
133
+ .subnote {
134
+ text-align: center;
135
+ margin-top: -6px;
136
+ opacity: 0.8;
137
+ }
138
+ """
139
+
140
+ with gr.Blocks(css=CSS, theme=gr.themes.Soft()) as demo:
141
+ # Header with logo + centered title
142
+ with gr.Row():
143
+ gr.Markdown(
144
+ f"""
145
+ <div class="header">
146
+ <img src="{LOGO_URL}" alt="Omantel logo" />
147
+ <h1>Omantel Insurance Q&A — RAG Assistant</h1>
148
+ </div>
149
+ <p class="subnote">Ask about coverage, claims, exclusions, and more — powered by LlamaIndex + Pinecone</p>
150
+ """,
151
+ elem_id="header_md"
152
+ )
153
+
154
+ with gr.Row():
155
+ with gr.Column(scale=1):
156
+ gr.Markdown("### Ask from Frequently Asked Questions")
157
+ faq = gr.Dropdown(choices=FAQS, value=FAQS[0], label="Select a common question")
158
+
159
+ gr.Markdown("### Or type your question")
160
+ user_q = gr.Textbox(
161
+ label="Your question",
162
+ placeholder="e.g., What is covered under outpatient benefits?",
163
+ lines=2
164
+ )
165
+
166
+ ask_btn = gr.Button("Ask", variant="primary")
167
+
168
+ with gr.Column(scale=1):
169
+ chosen_prompt = gr.Textbox(label="Query sent", interactive=False)
170
+ answer_box = gr.Markdown()
171
+
172
+ ask_btn.click(use_faq, inputs=[faq, user_q], outputs=[chosen_prompt, answer_box])
173
 
174
  if __name__ == "__main__":
175
+ demo.launch()