martina3435's picture
Update rag.py
052467c verified
Raw
History Blame Contribute Delete
5.41 kB
import os
import logging
import signal
import sys
import zipfile
import gdown
import time
from google import genai
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
# ==============================
# Logging
# ==============================
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# ==============================
# Gemini Client
# ==============================
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
if not GEMINI_API_KEY:
raise ValueError("GEMINI_API_KEY is missing")
client = genai.Client(api_key=GEMINI_API_KEY)
MODEL_NAME = "models/gemini-2.5-flash-lite"
# ==============================
# Gemini Call (with retry)
# ==============================
def call_gemini(prompt: str, retries: int = 3):
for attempt in range(retries):
try:
response = client.models.generate_content(
model=MODEL_NAME,
contents=prompt
)
if response and response.text:
return response.text
return "Empty response from model."
except Exception as e:
msg = str(e)
# handling quota error
if "429" in msg or "RESOURCE_EXHAUSTED" in msg:
wait_time = 5 * (attempt + 1)
logger.warning(f"Quota hit. Retrying in {wait_time}s...")
time.sleep(wait_time)
continue
logger.error(f"Gemini error: {e}")
break
return "Model is temporarily unavailable. Please try again later."
# ==============================
# Download Vector DB
# ==============================
if not os.path.exists("chroma_db"):
print("Downloading vector DB...")
url = "https://drive.google.com/uc?id=1yelCLmiRD2Qds-_8EsnKzUNY7uPT5xMg"
gdown.download(url, "chroma_db.zip", quiet=False)
print("Extracting DB...")
with zipfile.ZipFile("chroma_db.zip", "r") as zip_ref:
zip_ref.extractall(".")
print("Vector DB Ready ✅")
# ==============================
# Load Vector DB
# ==============================
embedding = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vector_db = Chroma(
persist_directory="./chroma_db",
embedding_function=embedding
)
print("Vector DB Loaded ✅")
# ==============================
# Conversation Memory
# ==============================
class Conversation:
def __init__(self):
self.history = []
def add_user_message(self, message):
self.history.append({"role": "user", "content": message})
def add_bot_message(self, message):
self.history.append({"role": "assistant", "content": message})
def get_history(self):
return self.history
def clear_history(self):
self.history = []
conversation = Conversation()
# ==============================
# Get Context
# ==============================
def get_relevant_context_from_db(query):
try:
results = vector_db.similarity_search(query, k=2)
context = "\n\n".join([
doc.page_content[:1200]
for doc in results
])
sources = list(set([
doc.metadata.get("source", "")
for doc in results
]))
return context, sources
except Exception as e:
logger.error(f"Vector DB error: {e}")
return "", []
# ==============================
# Prompt Builder
# ==============================
def build_prompt(query, context, history):
history_text = "\n".join([
f"{msg['role']}: {msg['content']}"
for msg in history[-3:]
]) if history else ""
prompt = f"""
You are an expert nutritionist and dietitian.
RULES:
- Answer ONLY the user question
- Keep answers clean and organized
- Use bullet points when helpful
- Do NOT repeat sentences
- Do NOT mention Ramadan unless user asks
- If user asks for diet:
give breakfast, lunch, dinner, snacks
- If user asks for foods:
include protein/calories if possible
- If user writes Arabic:
answer in Arabic
- Keep answers practical and realistic
Conversation:
{history_text}
Context:
{context}
User Question:
{query}
Answer:
"""
return prompt
# ==============================
# Main Function
# ==============================
def generate_chat_answer(query, history=[]):
try:
logger.info(f"Query: {query}")
context, sources = get_relevant_context_from_db(query)
prompt = build_prompt(
query,
context,
history
)
answer = call_gemini(prompt).strip()
answer = answer.replace("**", "").replace("##", "")
if not answer:
answer = "Sorry, no response generated."
# memory
conversation.add_user_message(query)
conversation.add_bot_message(answer)
# add sources
if sources:
answer += "\n\nSources:\n" + "\n".join(
[f"- {s}" for s in sources if s]
)
return answer
except Exception as e:
logger.error(f"Error: {e}", exc_info=True)
return "Something went wrong."
# ==============================
# Graceful shutdown
# ==============================
def signal_handler(sig, frame):
print("\nShutting down...")
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)