decodingdatascience commited on
Commit
7598214
·
verified ·
1 Parent(s): a33cda8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -87
app.py CHANGED
@@ -1,128 +1,103 @@
 
 
1
  import os
2
  import logging
3
- import tempfile
4
- from typing import List
5
-
6
  import gradio as gr
7
 
8
- # ---- LlamaIndex / Pinecone ----
9
  from pinecone import Pinecone, ServerlessSpec
10
  from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext, Settings
11
  from llama_index.vector_stores.pinecone import PineconeVectorStore
12
  from llama_index.embeddings.openai import OpenAIEmbedding
13
  from llama_index.llms.openai import OpenAI
14
 
15
- # ---- Config ----
16
  PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
17
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
18
 
19
- # You can override these via Space "Variables" (Secrets)
20
- PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME", "dds-demo-index")
21
- PINECONE_REGION = os.getenv("PINECONE_REGION", "us-east-1") # keep in sync with ServerlessSpec
22
- PINECONE_CLOUD = os.getenv("PINECONE_CLOUD", "aws")
23
- EMBED_MODEL = os.getenv("EMBED_MODEL", "text-embedding-3-small")
24
- LLM_MODEL = os.getenv("LLM_MODEL", "gpt-4o-mini")
 
 
25
 
26
  if not PINECONE_API_KEY:
27
- raise RuntimeError("Missing PINECONE_API_KEY. Add it in your Space settings (Secrets).")
28
  if not OPENAI_API_KEY:
29
- raise RuntimeError("Missing OPENAI_API_KEY. Add it in your Space settings (Secrets).")
30
 
31
  logging.basicConfig(level=logging.INFO)
32
- logger = logging.getLogger("dds-space")
 
 
 
 
 
33
 
34
- # ---- Pinecone client & index bootstrap ----
35
  pc = Pinecone(api_key=PINECONE_API_KEY)
36
 
37
- # Create index if it doesn't exist.
38
- def _ensure_index(index_name: str, dimension: int = 1536):
39
- existing = [idx["name"] for idx in pc.list_indexes()]
40
- if index_name not in existing:
41
- logger.info(f"Creating Pinecone index '{index_name}' (dim={dimension})...")
42
  pc.create_index(
43
- name=index_name,
44
- dimension=dimension,
45
  metric="cosine",
46
  spec=ServerlessSpec(cloud=PINECONE_CLOUD, region=PINECONE_REGION),
47
  )
48
- return pc.Index(index_name)
49
 
50
- pinecone_index = _ensure_index(PINECONE_INDEX_NAME, dimension=1536)
 
51
 
52
- # ---- LlamaIndex settings ----
53
- # Set global settings for LlamaIndex (embeddings + LLM)
54
- Settings.embed_model = OpenAIEmbedding(model=EMBED_MODEL, api_key=OPENAI_API_KEY)
55
- Settings.llm = OpenAI(model=LLM_MODEL, api_key=OPENAI_API_KEY)
 
 
 
56
 
57
- # Vector store wrapper
58
- vector_store = PineconeVectorStore(pinecone_index=pinecone_index)
59
 
60
- def build_or_update_index(files: List[gr.File]) -> str:
61
- """
62
- Load the uploaded files, chunk them with LlamaIndex, and upsert into Pinecone.
63
- """
64
- if not files:
65
- return "Please upload at least one file."
66
- with tempfile.TemporaryDirectory() as tmpdir:
67
- paths = []
68
- for f in files:
69
- # Gradio File object -> save to temp path
70
- dst = os.path.join(tmpdir, os.path.basename(f.name))
71
- with open(f.name, "rb") as src, open(dst, "wb") as out:
72
- out.write(src.read())
73
- paths.append(dst)
74
-
75
- docs = SimpleDirectoryReader(input_files=paths).load_data()
76
- storage_context = StorageContext.from_defaults(vector_store=vector_store)
77
-
78
- # Build a new index (will upsert into Pinecone via the vector_store)
79
- _ = VectorStoreIndex.from_documents(
80
- docs,
81
- storage_context=storage_context,
82
- show_progress=True,
83
- )
84
 
85
- return f"Indexed {len(files)} file(s) into Pinecone index: {PINECONE_INDEX_NAME}."
 
86
 
 
87
  def answer(query: str, top_k: int = 4) -> str:
88
- if not query or not query.strip():
89
- return "Ask a question about your uploaded knowledge."
90
- # Re-build a lightweight index wrapper that reads from the existing vector store
91
  index = VectorStoreIndex.from_vector_store(vector_store)
92
- qe = index.as_query_engine(similarity_top_k=top_k)
93
- resp = qe.query(query)
94
  return str(resp)
95
 
96
- # ---- UI ----
97
- INTRO = (
98
- "Upload PDFs/TXT/Docs to build a Pinecone vector index (1536-d). "
99
- "Then ask questions to retrieve & summarize with LlamaIndex + OpenAI."
100
- )
101
-
102
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
103
  gr.Markdown(
104
- "<h1 style='text-align:center;'>📚 RAG with LlamaIndex + Pinecone</h1>"
105
- "<p style='text-align:center;'>Omantel/DDS demo Space minimal, production-friendly layout</p>"
106
  )
 
 
 
 
107
 
108
- with gr.Row():
109
- with gr.Column(scale=1):
110
- gr.Markdown("### 1) Upload & Index")
111
- file_uploader = gr.File(label="Upload documents", file_count="multiple", type="filepath")
112
- index_btn = gr.Button("Build / Update Index")
113
- index_status = gr.Markdown()
114
-
115
- with gr.Column(scale=1):
116
- gr.Markdown("### 2) Ask a Question")
117
- query = gr.Textbox(label="Your question", placeholder="e.g., What is the refund policy?")
118
- topk = gr.Slider(1, 10, value=4, step=1, label="Top-K")
119
- ask_btn = gr.Button("Ask")
120
- answer_box = gr.Markdown()
121
-
122
- gr.Markdown(f"**How it works:** {INTRO}")
123
-
124
- index_btn.click(build_or_update_index, inputs=[file_uploader], outputs=[index_status])
125
- ask_btn.click(answer, inputs=[query, topk], outputs=[answer_box])
126
 
127
  if __name__ == "__main__":
128
  demo.launch()
 
1
+ # app.py — Minimal RAG over ./data/insurance.pdf with LlamaIndex + Pinecone
2
+
3
  import os
4
  import logging
 
 
 
5
  import gradio as gr
6
 
7
+ # ---- Vector + LLM stack ----
8
  from pinecone import Pinecone, ServerlessSpec
9
  from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext, Settings
10
  from llama_index.vector_stores.pinecone import PineconeVectorStore
11
  from llama_index.embeddings.openai import OpenAIEmbedding
12
  from llama_index.llms.openai import OpenAI
13
 
14
+ # ========== CONFIG ==========
15
  PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
16
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
17
 
18
+ # Optional overrides via Space Variables
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).")
29
  if not OPENAI_API_KEY:
30
+ raise RuntimeError("Missing OPENAI_API_KEY (set it in your Space Settings → Variables).")
31
 
32
  logging.basicConfig(level=logging.INFO)
33
+ log = logging.getLogger("dds-space")
34
+
35
+ # ========== CLIENTS / GLOBALS ==========
36
+ # LlamaIndex global settings
37
+ Settings.embed_model = OpenAIEmbedding(model=EMBED_MODEL, api_key=OPENAI_API_KEY)
38
+ Settings.llm = OpenAI(model=LLM_MODEL, api_key=OPENAI_API_KEY)
39
 
40
+ # Pinecone
41
  pc = Pinecone(api_key=PINECONE_API_KEY)
42
 
43
+ def ensure_index(name: str, dim: int = 1536):
44
+ names = [i["name"] for i in pc.list_indexes()]
45
+ if name not in names:
46
+ log.info(f"Creating Pinecone index '{name}' (dim={dim})...")
 
47
  pc.create_index(
48
+ name=name,
49
+ dimension=dim,
50
  metric="cosine",
51
  spec=ServerlessSpec(cloud=PINECONE_CLOUD, region=PINECONE_REGION),
52
  )
53
+ return pc.Index(name)
54
 
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()