AI-notes-agent / src /core /rag.py
Jaleed02's picture
Initial commit: Multi-User AI Study Agent
2d25973
Raw
History Blame Contribute Delete
3.59 kB
import os
import uuid
import chromadb
from chromadb.config import Settings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from google import genai
from google.genai import types
import streamlit as st
# Initialize ChromaDB persistent client locally
CHROMA_DB_DIR = "./chroma_db"
os.makedirs(CHROMA_DB_DIR, exist_ok=True)
@st.cache_resource
def get_chroma_client():
return chromadb.PersistentClient(path=CHROMA_DB_DIR)
def get_gemini_embedding(text):
"""Hits the Gemini API to mathematically embed a text chunk into vectors."""
client = genai.Client()
response = client.models.embed_content(
model='gemini-embedding-001',
contents=text,
)
return response.embeddings[0].values
def chunk_text(text):
"""Splits massive PDFs into smaller, 1000-character overlapping chunks for precision RAG."""
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
is_separator_regex=False,
)
return text_splitter.split_text(text)
def embed_and_store_document(user_id, document_id, pdf_text, filename):
"""Chunks a raw PDF, calculates vectors for each chunk, and saves to the user's Chroma collection."""
client = get_chroma_client()
collection = client.get_or_create_collection(name="user_libraries")
# Check if this document was already embedded to skip duplicate processing
existing = collection.get(where={"document_id": document_id})
if existing and existing['ids']:
return # Already indexed
chunks = chunk_text(pdf_text)
ids = []
embeddings = []
documents = []
metadatas = []
for i, chunk in enumerate(chunks):
if not chunk.strip():
continue
try:
emb = get_gemini_embedding(chunk)
ids.append(f"{document_id}_chunk_{i}")
embeddings.append(emb)
documents.append(chunk)
metadatas.append({"user_id": user_id, "document_id": document_id, "filename": filename})
except Exception as e:
print(f"Failed to embed chunk {i}: {e}")
if ids:
collection.add(
embeddings=embeddings,
documents=documents,
metadatas=metadatas,
ids=ids
)
def query_relevant_chunks(user_id, query, n_results=5):
"""Full Library Search: Searches ALL textbooks uploaded by the user to find the best 5 paragraphs."""
client = get_chroma_client()
collection = client.get_or_create_collection(name="user_libraries")
if collection.count() == 0:
return ""
try:
query_embedding = get_gemini_embedding(query)
results = collection.query(
query_embeddings=[query_embedding],
n_results=n_results,
where={"user_id": user_id} # Filter exclusively to this user's entire library!
)
if not results['documents'] or not results['documents'][0]:
return ""
# Compile all matching text chunks into a unified context string, tagging the source book
context_pieces = []
for i, text in enumerate(results['documents'][0]):
source_file = results['metadatas'][0][i].get("filename", "Unknown Document")
context_pieces.append(f"[Excerpt from {source_file}]:\n{text}")
return "\n\n---\n\n".join(context_pieces)
except Exception as e:
print(f"Semantic search failed: {e}")
return ""