docu-chat-app / src /streamlit_app.py
SpiffGreen's picture
Remove need for hf request
b19a260 verified
Raw
History Blame Contribute Delete
4.77 kB
from dotenv import load_dotenv
from google import genai
import streamlit as st
from pypdf import PdfReader
from sentence_transformers import SentenceTransformer
import chromadb
import os
from chromadb.utils import embedding_functions
from huggingface_hub import InferenceClient
# ------------------------
# Setup
# ------------------------
st.set_page_config(page_title="Simple RAG", layout="wide")
# Load the environment variables from the .env file
load_dotenv()
# Initialize session state with API keys
if "gemini_key" not in st.session_state:
st.session_state.gemini_key = os.getenv("GEMINI_API_KEY", "")
st.title("📄 DocuChat")
model = SentenceTransformer("all-MiniLM-L6-v2")
# Show forms only if keys are missing
if not st.session_state.gemini_key:
st.warning("⚠️ Gemini API Key is not set")
gemini_input = st.text_input("Enter your Gemini API Key", type="password")
if gemini_input:
st.session_state.gemini_key = gemini_input
st.rerun()
GEMINI_API_KEY = st.session_state.gemini_key
if not GEMINI_API_KEY:
st.error("❌ Please provide your Gemini API Key to continue")
st.stop()
genai_client = genai.Client(api_key=GEMINI_API_KEY)
chroma_client = chromadb.Client()
# Delete and recreate collection to ensure consistent embedding dimensions
try:
chroma_client.delete_collection(name="docs")
except:
pass
collection = chroma_client.get_or_create_collection(name="docs")
# ------------------------
# Helpers
# ------------------------
def extract_text(file):
if file.type == "application/pdf":
reader = PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text() or ""
return text
else:
return file.read().decode("utf-8")
def chunk_text(text, chunk_size=500, overlap=50):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start += chunk_size - overlap
return chunks
def query_embedding_api(texts):
"""Helper function to fetch embeddings using SentenceTransformer."""
try:
embeddings = model.encode(texts)
# Convert to list if needed
if hasattr(embeddings, 'tolist'):
embeddings = embeddings.tolist()
return embeddings
except Exception as e:
st.error(f"Error generating embeddings: {e}")
return None
def embed_and_store(chunks):
# embeddings = model.encode(chunks).tolist()
embeddings = query_embedding_api(chunks)
if embeddings is None:
st.error("Failed to generate embeddings")
return
for i, chunk in enumerate(chunks):
collection.add(
documents=[chunk],
embeddings=[embeddings[i]],
ids=[f"id_{i}"]
)
def retrieve(query, k=5):
# Fetch embedding for a single query string
api_response = query_embedding_api([query])
if api_response is None:
return []
query_embedding = api_response[0]
results = collection.query(
query_embeddings=[query_embedding],
n_results=k
)
return results["documents"][0]
# ------------------------
# UI
# ------------------------
tab1, tab2 = st.tabs(["Upload", "Chat"])
# ------------------------
# Upload Page
# ------------------------
with tab1:
st.header("Upload Document")
file = st.file_uploader("Upload PDF or TXT", type=["pdf", "txt"])
if file:
text = extract_text(file)
chunks = chunk_text(text)
embed_and_store(chunks)
st.success(f"Stored {len(chunks)} chunks!")
# ------------------------
# Chat Page
# ------------------------
with tab2:
st.header("Ask Questions")
query = st.text_input("Enter your question")
if query:
docs = retrieve(query)
context = "\n\n".join(docs)
# 2. Construct a clean system instruction and prompt for the LLM
prompt = f"""
You are a helpful assistant. Answer the question based ONLY on the provided context below.
If the answer cannot be found in the context, say "I cannot find the answer in the provided documents."
Context:
{context}
Question:
{query}
"""
# 3. Call Gemini using the smallest, high-speed model
with st.spinner("Thinking..."):
try:
response = genai_client.models.generate_content(
model='gemini-2.5-flash',
contents=prompt,
)
st.markdown("## Response")
st.markdown(response.text)
except Exception as e:
st.error(f"Gemini API Error: {e}")