Spaces:
Runtime error
Runtime error
Upload app.py with huggingface_hub
Browse files
app.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import gradio as gr
|
| 3 |
+
from langchain_community.document_loaders import PyPDFLoader
|
| 4 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 5 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 6 |
+
from langchain_community.vectorstores import FAISS
|
| 7 |
+
from huggingface_hub import InferenceClient
|
| 8 |
+
import tempfile
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
# Initialize embedding model (runs on CPU, small enough for free tier)
|
| 12 |
+
embedding_model = HuggingFaceEmbeddings(
|
| 13 |
+
model_name="sentence-transformers/all-MiniLM-L6-v2",
|
| 14 |
+
model_kwargs={'device': 'cpu'}
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
# Initialize Inference Client (uses free API)
|
| 18 |
+
client = InferenceClient(model="HuggingFaceH4/zephyr-7b-beta")
|
| 19 |
+
|
| 20 |
+
# Global variable to store vectorstore
|
| 21 |
+
vectorstore = None
|
| 22 |
+
|
| 23 |
+
def process_pdf(pdf_file):
|
| 24 |
+
"""Process uploaded PDF and create vector store."""
|
| 25 |
+
global vectorstore
|
| 26 |
+
|
| 27 |
+
if pdf_file is None:
|
| 28 |
+
return "Please upload a PDF file."
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
# Load PDF
|
| 32 |
+
loader = PyPDFLoader(pdf_file.name)
|
| 33 |
+
documents = loader.load()
|
| 34 |
+
|
| 35 |
+
# Split into chunks
|
| 36 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
| 37 |
+
chunk_size=1000,
|
| 38 |
+
chunk_overlap=200,
|
| 39 |
+
)
|
| 40 |
+
chunks = text_splitter.split_documents(documents)
|
| 41 |
+
|
| 42 |
+
# Create vector store
|
| 43 |
+
vectorstore = FAISS.from_documents(
|
| 44 |
+
documents=chunks,
|
| 45 |
+
embedding=embedding_model
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
return f"β
Successfully processed {len(documents)} pages into {len(chunks)} chunks. You can now ask questions!"
|
| 49 |
+
|
| 50 |
+
except Exception as e:
|
| 51 |
+
return f"β Error processing PDF: {str(e)}"
|
| 52 |
+
|
| 53 |
+
def answer_question(question):
|
| 54 |
+
"""Answer question using RAG."""
|
| 55 |
+
global vectorstore
|
| 56 |
+
|
| 57 |
+
if vectorstore is None:
|
| 58 |
+
return "Please upload and process a PDF first.", ""
|
| 59 |
+
|
| 60 |
+
if not question.strip():
|
| 61 |
+
return "Please enter a question.", ""
|
| 62 |
+
|
| 63 |
+
try:
|
| 64 |
+
# Retrieve relevant chunks
|
| 65 |
+
docs = vectorstore.similarity_search(question, k=3)
|
| 66 |
+
|
| 67 |
+
# Format context
|
| 68 |
+
context = "\n\n".join([doc.page_content for doc in docs])
|
| 69 |
+
|
| 70 |
+
# Create prompt
|
| 71 |
+
prompt = f"""<|system|>
|
| 72 |
+
You are a helpful assistant that answers questions based on the provided context. Only use information from the context to answer. If the answer is not in the context, say "I cannot find this information in the document."
|
| 73 |
+
</s>
|
| 74 |
+
<|user|>
|
| 75 |
+
Context:
|
| 76 |
+
{context}
|
| 77 |
+
|
| 78 |
+
Question: {question}
|
| 79 |
+
</s>
|
| 80 |
+
<|assistant|>"""
|
| 81 |
+
|
| 82 |
+
# Call Inference API
|
| 83 |
+
response = client.text_generation(
|
| 84 |
+
prompt,
|
| 85 |
+
max_new_tokens=512,
|
| 86 |
+
temperature=0.7,
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
# Format sources
|
| 90 |
+
sources = []
|
| 91 |
+
for i, doc in enumerate(docs, 1):
|
| 92 |
+
page = doc.metadata.get('page', 'N/A')
|
| 93 |
+
if isinstance(page, int):
|
| 94 |
+
page += 1
|
| 95 |
+
preview = doc.page_content[:150].replace('\n', ' ')
|
| 96 |
+
sources.append(f"{i}. Page {page}: {preview}...")
|
| 97 |
+
|
| 98 |
+
sources_text = "\n".join(sources)
|
| 99 |
+
|
| 100 |
+
return response, sources_text
|
| 101 |
+
|
| 102 |
+
except Exception as e:
|
| 103 |
+
return f"β Error: {str(e)}", ""
|
| 104 |
+
|
| 105 |
+
# Create Gradio interface
|
| 106 |
+
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 107 |
+
gr.Markdown("""
|
| 108 |
+
# π RAG Document Q&A System
|
| 109 |
+
|
| 110 |
+
Upload a PDF document and ask questions about its content.
|
| 111 |
+
|
| 112 |
+
**How it works:**
|
| 113 |
+
1. Upload a PDF file
|
| 114 |
+
2. Click "Process PDF" to analyze the document
|
| 115 |
+
3. Ask questions about the document content
|
| 116 |
+
""")
|
| 117 |
+
|
| 118 |
+
with gr.Row():
|
| 119 |
+
with gr.Column(scale=1):
|
| 120 |
+
pdf_input = gr.File(label="Upload PDF", file_types=[".pdf"])
|
| 121 |
+
process_btn = gr.Button("π Process PDF", variant="primary")
|
| 122 |
+
status_output = gr.Textbox(label="Status", interactive=False)
|
| 123 |
+
|
| 124 |
+
with gr.Column(scale=2):
|
| 125 |
+
question_input = gr.Textbox(
|
| 126 |
+
label="Your Question",
|
| 127 |
+
placeholder="What is this document about?",
|
| 128 |
+
lines=2
|
| 129 |
+
)
|
| 130 |
+
ask_btn = gr.Button("π Ask Question", variant="primary")
|
| 131 |
+
answer_output = gr.Textbox(label="Answer", lines=6, interactive=False)
|
| 132 |
+
sources_output = gr.Textbox(label="Sources", lines=4, interactive=False)
|
| 133 |
+
|
| 134 |
+
gr.Markdown("""
|
| 135 |
+
---
|
| 136 |
+
### π οΈ Technical Details
|
| 137 |
+
|
| 138 |
+
| Component | Technology |
|
| 139 |
+
|-----------|------------|
|
| 140 |
+
| Embeddings | sentence-transformers/all-MiniLM-L6-v2 |
|
| 141 |
+
| Vector Store | FAISS |
|
| 142 |
+
| LLM | Zephyr-7B via Inference API |
|
| 143 |
+
| Chunking | 1000 chars, 200 overlap |
|
| 144 |
+
|
| 145 |
+
### β οΈ Development Challenges Documented
|
| 146 |
+
|
| 147 |
+
This project encountered several technical challenges:
|
| 148 |
+
- **LangChain API changes**: Package restructuring required updated imports
|
| 149 |
+
- **PDF parsing issues**: Required proper HTTP headers for downloads
|
| 150 |
+
- **LLM response quality**: FLAN-T5 produced short responses; switched to Zephyr-7B
|
| 151 |
+
- **Memory management**: Required explicit GPU cleanup between model loads
|
| 152 |
+
|
| 153 |
+
Built by [Nav772](https://huggingface.co/Nav772) as part of AI Engineering portfolio.
|
| 154 |
+
""")
|
| 155 |
+
|
| 156 |
+
# Connect buttons to functions
|
| 157 |
+
process_btn.click(
|
| 158 |
+
fn=process_pdf,
|
| 159 |
+
inputs=[pdf_input],
|
| 160 |
+
outputs=[status_output]
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
ask_btn.click(
|
| 164 |
+
fn=answer_question,
|
| 165 |
+
inputs=[question_input],
|
| 166 |
+
outputs=[answer_output, sources_output]
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
demo.launch()
|