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