SohaAyub's picture
Update app.py
c5baecb verified
import os
import gradio as gr
import numpy as np
import faiss
from groq import Groq
from pypdf import PdfReader
from sentence_transformers import SentenceTransformer
from langchain_text_splitters import RecursiveCharacterTextSplitter
# =====================================================
# Configuration
# =====================================================
RELEVANCE_THRESHOLD = 1.2 # lower = stricter relevance
# =====================================================
# Initialize Groq Client
# =====================================================
client = Groq(api_key=os.environ.get("RAG-GROQ"))
# =====================================================
# Load Embedding Model
# =====================================================
embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
# =====================================================
# Global Vector Store
# =====================================================
vector_store = None
stored_chunks = []
# =====================================================
# PDF Processing Function
# =====================================================
def process_pdf(pdf_file):
global vector_store, stored_chunks
reader = PdfReader(pdf_file)
full_text = ""
for page in reader.pages:
if page.extract_text():
full_text += page.extract_text() + "\n"
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100
)
chunks = splitter.split_text(full_text)
embeddings = embedding_model.encode(chunks)
dimension = embeddings.shape[1]
vector_store = faiss.IndexFlatL2(dimension)
vector_store.add(np.array(embeddings))
stored_chunks = chunks
return "✅ PDF processed successfully. You can now ask questions."
# =====================================================
# Question Answering Function
# =====================================================
def answer_question(question):
if vector_store is None:
return "⚠️ Please upload and process a PDF first."
question_embedding = embedding_model.encode([question])
distances, indices = vector_store.search(
np.array(question_embedding), k=3
)
avg_distance = distances[0].mean()
context = ""
for idx in indices[0]:
context += stored_chunks[idx] + "\n"
# Relevance feedback
if avg_distance > RELEVANCE_THRESHOLD:
relevance_note = (
"⚠️ **Note:** This question is not directly answered in the document.\n"
"The response below is based on loosely related context.\n\n"
)
else:
relevance_note = ""
prompt = f"""
You are an honest and careful AI assistant.
Instructions:
- Answer ONLY using the provided context.
- If the answer is not explicitly stated, say:
"This is not directly mentioned in the document, but based on related context..."
Context:
{context}
Question:
{question}
"""
response = client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[
{"role": "user", "content": prompt}
]
)
return relevance_note + response.choices[0].message.content
# =====================================================
# Gradio UI
# =====================================================
with gr.Blocks() as app:
gr.Markdown("## 📄 RAG-based PDF Question Answering (Groq + FAISS)")
gr.Markdown(
"Upload a PDF and ask questions. "
"The system will clearly tell you if an answer is not directly mentioned."
)
pdf_file = gr.File(label="Upload PDF", file_types=[".pdf"])
process_btn = gr.Button("Process PDF")
status_box = gr.Textbox(label="Status", interactive=False)
question_box = gr.Textbox(label="Ask a Question")
answer_box = gr.Textbox(label="Answer", lines=8)
process_btn.click(
process_pdf,
inputs=pdf_file,
outputs=status_box
)
question_box.submit(
answer_question,
inputs=question_box,
outputs=answer_box
)
app.launch()