prathamesh-bot / app.py
PrathameshRaut's picture
Update app.py
98c3a0b verified
Raw
History Blame Contribute Delete
6.23 kB
import os
from typing import List, Optional
from fastapi import FastAPI, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel
from pinecone import Pinecone
from sentence_transformers import SentenceTransformer
from openai import OpenAI
# ==============================
# 🔐 CONFIG (Use HF Secrets)
# ==============================
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
SARVAM_API_KEY = os.getenv("SARVAM_API_KEY")
API_SECRET_KEY = os.getenv("API_SECRET_KEY")
PINECONE_INDEX = "prathamesh-portfolio"
EMBED_MODEL = "BAAI/bge-m3"
TOP_K = 5
SARVAM_MODEL = "sarvam-m"
SARVAM_BASE_URL = "https://api.sarvam.ai/v1"
# Validate secrets
if not PINECONE_API_KEY:
raise ValueError("PINECONE_API_KEY not set in environment variables.")
if not SARVAM_API_KEY:
raise ValueError("SARVAM_API_KEY not set in environment variables.")
if not API_SECRET_KEY:
raise ValueError("API_SECRET_KEY must be set in environment variables.")
# ==============================
# 🚀 INITIALIZE COMPONENTS
# ==============================
pc = Pinecone(api_key=PINECONE_API_KEY)
index = pc.Index(PINECONE_INDEX)
embedder = SentenceTransformer(EMBED_MODEL)
sarvam_client = OpenAI(
api_key=SARVAM_API_KEY,
base_url=SARVAM_BASE_URL
)
# ==============================
# 🏗 FASTAPI APP
# ==============================
app = FastAPI(
title="Prathamesh Portfolio Chatbot",
description="RAG-powered chatbot API with secret key authentication"
)
# Enable CORS (adjust origin in production)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
security = HTTPBearer()
# ==============================
# 📦 REQUEST MODEL
# ==============================
class ChatRequest(BaseModel):
query: str
tag_filter: Optional[List[str]] = None
# ==============================
# 🔎 RETRIEVAL
# ==============================
def retrieve(query: str, top_k: int = TOP_K, tag_filter: Optional[List[str]] = None):
query_embedding = embedder.encode(
query,
normalize_embeddings=True
).tolist()
pinecone_filter = None
if tag_filter:
pinecone_filter = {"tags": {"$in": tag_filter}}
results = index.query(
vector=query_embedding,
top_k=top_k,
include_metadata=True,
filter=pinecone_filter
)
retrieved = []
for match in results.get("matches", []):
retrieved.append({
"chunk_id": match["id"],
"score": round(match["score"], 4),
"text": match["metadata"]["text"],
"tags": match["metadata"].get("tags", []),
})
return retrieved
# ==============================
# 🧠 PROMPT BUILDER
# ==============================
def build_prompt(query: str, retrieved_chunks: list) -> str:
context_blocks = []
for i, chunk in enumerate(retrieved_chunks, 1):
context_blocks.append(
f"[Context {i} | Relevance: {chunk['score']} | Type: {', '.join(chunk['tags'])}]\n{chunk['text']}"
)
context_str = "\n\n".join(context_blocks)
return f"""
You are a helpful and professional AI assistant for Prathamesh Raut's portfolio chatbot.
Use ONLY the context provided below to answer.
If the answer isn't present, say:
"I don't have specific information about that in Prathamesh's profile."
Be concise and accurate.
---
CONTEXT:
{context_str}
---
QUESTION: {query}
ANSWER:
"""
# ==============================
# 🏷 TAG DETECTION
# ==============================
def detect_tags(query: str) -> Optional[List[str]]:
query_lower = query.lower()
tag_keywords = {
"project": ["project", "built", "developed", "created", "worked on"],
"skills": ["skill", "technologies", "tools", "tech stack"],
"work_experience": ["work", "job", "company", "role", "position"],
"education": ["college", "degree", "university", "cgpa", "gpa"],
"publication": ["publish", "paper", "research", "ieee"],
"certification": ["certified", "harvard", "databricks", "cisco"],
"personal": ["hobbies", "interests", "language"],
}
detected = []
for tag, keywords in tag_keywords.items():
if any(keyword in query_lower for keyword in keywords):
detected.append(tag)
return detected if detected else None
# ==============================
# 🤖 ANSWER GENERATION
# ==============================
def answer(query: str, tag_filter: Optional[List[str]] = None) -> str:
chunks = retrieve(query, tag_filter=tag_filter)
if not chunks:
return "I couldn't find relevant information in Prathamesh's knowledge base."
prompt = build_prompt(query, chunks)
response = sarvam_client.chat.completions.create(
model=SARVAM_MODEL,
messages=[
{
"role": "system",
"content": (
"You are a professional portfolio assistant for Prathamesh Raut. "
"Answer only using the provided context."
),
},
{"role": "user", "content": prompt},
],
temperature=0.3,
max_tokens=600,
)
return response.choices[0].message.content.strip()
# ==============================
# 🔐 AUTHENTICATED ENDPOINT
# ==============================
@app.post("/chat")
async def chat_endpoint(
request: ChatRequest,
credentials: HTTPAuthorizationCredentials = Depends(security),
):
# Validate secret key
if credentials.credentials != API_SECRET_KEY:
raise HTTPException(status_code=401, detail="Invalid secret key.")
try:
tags = request.tag_filter or detect_tags(request.query)
response = answer(request.query, tag_filter=tags)
return {"answer": response}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ==============================
# ❤️ HEALTH CHECK
# ==============================
@app.get("/")
async def root():
return {"message": "Prathamesh Portfolio Chatbot API is running!"}