omUniyal commited on
Commit
7b50136
Β·
verified Β·
1 Parent(s): e7a3876

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +126 -57
app.py CHANGED
@@ -2,56 +2,100 @@
2
  Gradio UI β€” RAG Document Q&A
3
  Deployed on HuggingFace Spaces.
4
 
5
- Pre-ingested documents:
6
- - Attention Is All You Need (Vaswani et al., 2017)
7
- - RAG for Knowledge-Intensive NLP Tasks (Lewis et al., 2020)
8
  """
9
 
10
  import os
 
11
  from pathlib import Path
12
  import gradio as gr
13
  from src.generation.rag_chain import RAGChain
14
  from src.retrieval.vector_store import VectorStore
15
- from src.ingestion.pdf_loader import load_pdfs_from_dir
16
  from src.ingestion.chunker import chunk_pages
17
  from src.retrieval.embedder import Embedder
18
  from src.utils.logger import logger
19
 
20
 
21
- def ensure_ingested():
22
- """
23
- Auto-ingest PDFs on first startup if vector store is empty.
24
- On HuggingFace Spaces, the chroma_db doesn't persist between restarts
25
- so we re-ingest from data/raw/ every cold start.
26
- """
27
  store = VectorStore()
28
  if store.collection.count() > 0:
29
- logger.info(f"Vector store already has {store.collection.count()} chunks β€” skipping ingestion")
30
- return
 
 
 
 
31
 
32
- logger.info("Vector store empty β€” ingesting PDFs from data/raw/...")
33
  pages = load_pdfs_from_dir("data/raw")
34
  if not pages:
35
- logger.error("No PDFs found in data/raw/ β€” app will not work correctly")
36
- return
37
 
38
  chunks = chunk_pages(pages)
39
  embedder = Embedder()
40
- texts = [c["text"] for c in chunks]
41
- embeddings = embedder.embed_texts(texts)
42
  store.add_chunks(chunks, embeddings)
43
- logger.info(f"Ingestion complete β€” {len(chunks)} chunks stored")
 
44
 
45
 
46
- # Run ingestion on startup
47
- ensure_ingested()
48
-
49
- # Initialise RAG chain
50
  chain = RAGChain()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
 
 
 
 
 
 
 
 
52
 
53
  def answer_question(question: str) -> tuple[str, str]:
54
- """Gradio callback: takes question, returns (answer, sources_markdown)."""
55
  if not question.strip():
56
  return "Please enter a question.", ""
57
 
@@ -71,49 +115,74 @@ def answer_question(question: str) -> tuple[str, str]:
71
  # Gradio UI
72
  # ------------------------------------------------------------
73
 
74
- with gr.Blocks(
75
- title="RAG Document Q&A",
76
- theme=gr.themes.Soft(),
77
- ) as demo:
78
  gr.Markdown("""
79
  # RAG Document Q&A
80
- Ask questions about the **Attention Is All You Need** and **RAG** papers.
81
- Answers are grounded only in the retrieved document passages β€” no hallucination from training data.
 
 
 
82
  """)
83
 
84
- with gr.Row():
85
- question_box = gr.Textbox(
86
- label="Your question",
87
- placeholder="e.g. What is the attention mechanism? How does RAG work?",
88
- lines=2,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  )
90
 
91
- submit_btn = gr.Button("Ask", variant="primary", size="lg")
 
 
 
 
92
 
93
- with gr.Row():
94
- answer_box = gr.Textbox(
95
- label="Answer",
96
- lines=8,
97
- interactive=False,
 
 
 
 
 
 
 
 
98
  )
99
- sources_box = gr.Markdown(label="Sources")
100
-
101
- gr.Examples(
102
- examples=[
103
- "What is the attention mechanism in transformers?",
104
- "What is multi-head attention?",
105
- "How does RAG combine retrieval and generation?",
106
- "What datasets were used to evaluate RAG?",
107
- "What is the encoder-decoder architecture?",
108
- ],
109
- inputs=question_box,
110
- )
111
 
112
- submit_btn.click(
113
- fn=answer_question,
114
- inputs=[question_box],
115
- outputs=[answer_box, sources_box],
116
- )
117
 
118
  gr.Markdown(
119
  "_Built with sentence-transformers, ChromaDB, and Groq (Llama 3.1 8B). "
 
2
  Gradio UI β€” RAG Document Q&A
3
  Deployed on HuggingFace Spaces.
4
 
5
+ Supports:
6
+ - Pre-loaded demo documents (Attention Is All You Need, RAG paper)
7
+ - User PDF uploads β€” upload any PDF and query it instantly
8
  """
9
 
10
  import os
11
+ import tempfile
12
  from pathlib import Path
13
  import gradio as gr
14
  from src.generation.rag_chain import RAGChain
15
  from src.retrieval.vector_store import VectorStore
16
+ from src.ingestion.pdf_loader import load_pdf
17
  from src.ingestion.chunker import chunk_pages
18
  from src.retrieval.embedder import Embedder
19
  from src.utils.logger import logger
20
 
21
 
22
+ # ------------------------------------------------------------
23
+ # Startup β€” ingest demo documents if store is empty
24
+ # ------------------------------------------------------------
25
+
26
+ def ensure_demo_ingested():
 
27
  store = VectorStore()
28
  if store.collection.count() > 0:
29
+ logger.info(f"Vector store has {store.collection.count()} chunks β€” skipping demo ingestion")
30
+ return store.collection.count()
31
+
32
+ logger.info("Ingesting demo documents...")
33
+ from src.ingestion.pdf_loader import load_pdfs_from_dir
34
+ from src.ingestion.chunker import chunk_pages
35
 
 
36
  pages = load_pdfs_from_dir("data/raw")
37
  if not pages:
38
+ logger.warning("No demo PDFs found in data/raw/")
39
+ return 0
40
 
41
  chunks = chunk_pages(pages)
42
  embedder = Embedder()
43
+ embeddings = embedder.embed_texts([c["text"] for c in chunks])
 
44
  store.add_chunks(chunks, embeddings)
45
+ logger.info(f"Demo ingestion complete β€” {len(chunks)} chunks")
46
+ return len(chunks)
47
 
48
 
49
+ demo_chunks = ensure_demo_ingested()
 
 
 
50
  chain = RAGChain()
51
+ embedder = Embedder()
52
+
53
+
54
+ # ------------------------------------------------------------
55
+ # PDF upload handler
56
+ # ------------------------------------------------------------
57
+
58
+ def ingest_pdf(file) -> str:
59
+ """
60
+ Ingest a user-uploaded PDF into the vector store.
61
+ Adds to existing chunks β€” doesn't reset the store.
62
+ """
63
+ if file is None:
64
+ return "No file uploaded."
65
+
66
+ try:
67
+ path = Path(file.name)
68
+ logger.info(f"User uploaded: {path.name}")
69
+
70
+ pages = load_pdf(path)
71
+ if not pages:
72
+ return f"Could not extract text from {path.name}. Is it a scanned PDF?"
73
+
74
+ chunks = chunk_pages(pages)
75
+ embeddings = embedder.embed_texts([c["text"] for c in chunks])
76
+
77
+ store = VectorStore()
78
+ store.add_chunks(chunks, embeddings)
79
+
80
+ total = store.collection.count()
81
+ return (
82
+ f"βœ… **{path.name}** ingested successfully!\n\n"
83
+ f"- Pages extracted: {len(pages)}\n"
84
+ f"- Chunks created: {len(chunks)}\n"
85
+ f"- Total chunks in store: {total}\n\n"
86
+ f"You can now ask questions about this document."
87
+ )
88
 
89
+ except Exception as e:
90
+ logger.error(f"Ingestion error: {e}")
91
+ return f"❌ Error ingesting file: {str(e)}"
92
+
93
+
94
+ # ------------------------------------------------------------
95
+ # Query handler
96
+ # ------------------------------------------------------------
97
 
98
  def answer_question(question: str) -> tuple[str, str]:
 
99
  if not question.strip():
100
  return "Please enter a question.", ""
101
 
 
115
  # Gradio UI
116
  # ------------------------------------------------------------
117
 
118
+ with gr.Blocks(title="RAG Document Q&A", theme=gr.themes.Soft()) as demo:
119
+
 
 
120
  gr.Markdown("""
121
  # RAG Document Q&A
122
+ Ask questions about documents using Retrieval-Augmented Generation.
123
+
124
+ **Pre-loaded:** Attention Is All You Need + RAG paper (Lewis et al., 2020)
125
+
126
+ **Or upload your own PDF** and query it instantly.
127
  """)
128
 
129
+ with gr.Tab("Ask a question"):
130
+ with gr.Row():
131
+ question_box = gr.Textbox(
132
+ label="Your question",
133
+ placeholder="e.g. What is the attention mechanism? How does RAG work?",
134
+ lines=2,
135
+ )
136
+
137
+ submit_btn = gr.Button("Ask", variant="primary", size="lg")
138
+
139
+ with gr.Row():
140
+ answer_box = gr.Textbox(
141
+ label="Answer",
142
+ lines=8,
143
+ interactive=False,
144
+ )
145
+ sources_box = gr.Markdown(label="Sources")
146
+
147
+ gr.Examples(
148
+ examples=[
149
+ "What is the attention mechanism in transformers?",
150
+ "What is multi-head attention?",
151
+ "How does RAG combine retrieval and generation?",
152
+ "What datasets were used to evaluate RAG?",
153
+ "What is the encoder-decoder architecture?",
154
+ ],
155
+ inputs=question_box,
156
  )
157
 
158
+ submit_btn.click(
159
+ fn=answer_question,
160
+ inputs=[question_box],
161
+ outputs=[answer_box, sources_box],
162
+ )
163
 
164
+ with gr.Tab("Upload your PDF"):
165
+ gr.Markdown("""
166
+ ### Upload a PDF to query
167
+ Upload any PDF document and it will be ingested into the vector store.
168
+ You can then ask questions about it in the **Ask a question** tab.
169
+
170
+ **Note:** Uploaded documents are added to the existing store alongside the demo papers.
171
+ Scanned PDFs (image-only) are not supported β€” the PDF must have extractable text.
172
+ """)
173
+
174
+ file_upload = gr.File(
175
+ label="Upload PDF",
176
+ file_types=[".pdf"],
177
  )
178
+ upload_btn = gr.Button("Ingest PDF", variant="primary")
179
+ upload_status = gr.Markdown(label="Status")
 
 
 
 
 
 
 
 
 
 
180
 
181
+ upload_btn.click(
182
+ fn=ingest_pdf,
183
+ inputs=[file_upload],
184
+ outputs=[upload_status],
185
+ )
186
 
187
  gr.Markdown(
188
  "_Built with sentence-transformers, ChromaDB, and Groq (Llama 3.1 8B). "