Spaces:
Sleeping
Sleeping
File size: 7,147 Bytes
f5b0cd7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | import logging
from typing import Optional
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from agents import Runner
from simple_agents.aagents import Triage_Agent
from models.user_context import UserContext
from pydantic import BaseModel
from services.rag import RAGService
from data.vector_store import VectorStore
# Initialize services globally but handle initialization errors gracefully
try:
vector_store = VectorStore()
rag_service = RAGService()
rag_service.set_vector_store(vector_store)
except Exception as e:
logging.error(f"Failed to initialize services: {e}")
vector_store = None
rag_service = None
app = FastAPI()
# CORS middleware for Vercel deployment
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://muhammedsuhaib.github.io",
"http://localhost:3000",
"http://localhost:8080",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ---------------------------
# Pydantic Models for Frontend Requests
# ---------------------------
# Matches the payload for the general chat endpoint (/api/query)
class QueryRequest(BaseModel):
query: str
user_context: Optional[dict] = None
# Matches the payload for the selection endpoint (/api/selection)
class SelectionRequest(BaseModel):
selected_text: str
question: str
user_context: Optional[dict] = None
# Matches the payload for the translation endpoint (/api/translate-text)
class TranslationRequest(BaseModel):
text: str
target_language: str
# ---------------------------
# FastAPI Endpoints (Matching React expectations)
# ---------------------------
@app.get("/")
def read_root():
return {"message": "Python Assistant Backend is running."}
@app.post("/api/query")
async def handle_query(req: QueryRequest):
"""Handles general chat queries from the React component."""
logging.info(f"Received general query: {req.query}")
# Check if services are properly initialized
if not rag_service or not vector_store:
logging.error("RAG service not initialized")
return {
"answer": "Service temporarily unavailable",
"sources": []
}
# Create user context from request data
user_context_data = req.user_context or {}
user_context = UserContext(
name=user_context_data.get('name', 'User'),
uid=user_context_data.get('uid'),
email=user_context_data.get('email'),
personalization_data=user_context_data.get('personalization_data'),
session_id=user_context_data.get('session_id')
)
# Use global RAG service to get context from Qdrant
# Get relevant context from Qdrant
try:
rag_result = await rag_service.query(req.query)
print(rag_result)
print(rag_result.sources)
context = rag_result.answer if rag_result.answer != "I don't know" else ""
except Exception as e:
logging.error(f"RAG query failed: {e}")
# Fallback to no context if RAG fails
rag_result = None
context = ""
# Include context in the agent's query if available
if context and context != "I don't know":
enhanced_query = f"Based on the following context: {context}\n\nQuestion: {req.query}"
else:
enhanced_query = req.query
# Run the main agent with the enhanced query and user context
result = await Runner.run(
Triage_Agent,
enhanced_query,
context=user_context
)
# CRITICAL: Response structure must match React component: {"answer": "...", "sources": []}
return {
"answer": result.final_output,
"sources": rag_result.sources if rag_result and hasattr(rag_result, 'sources') else [] # Must be included, even if empty
}
@app.post("/api/selection")
async def handle_selection(req: SelectionRequest):
"""Handles queries based on selected text (RAG context)."""
logging.info(f"Received selection query. Question: {req.question}")
# Create user context from request data
user_context_data = req.user_context or {}
user_context = UserContext(
name=user_context_data.get('name', 'User'),
uid=user_context_data.get('uid'),
email=user_context_data.get('email'),
personalization_data=user_context_data.get('personalization_data'),
session_id=user_context_data.get('session_id')
)
# Check if services are properly initialized
if not rag_service or not vector_store:
logging.error("RAG service not initialized")
return {
"answer": "Service temporarily unavailable",
"sources": []
}
# Use global RAG service to get additional context from Qdrant
# Get relevant context from Qdrant based on the question
try:
rag_result = await rag_service.query(req.question)
additional_context = rag_result.answer if rag_result.answer != "I don't know" else ""
except Exception as e:
logging.error(f"RAG query failed: {e}")
# Fallback to no context if RAG fails
rag_result = None
additional_context = ""
# Construct a RAG-style prompt for the agent
if additional_context and additional_context != "I don't know":
prompt = (
f"Based *only* on the following context, answer the user's question. "
f"If the context does not contain the answer, state that. "
f"Context: \"{req.selected_text}\"\n\nAdditional context from knowledge base: {additional_context} "
f"Question: {req.question}"
)
else:
prompt = (
f"Based *only* on the following context, answer the user's question. "
f"If the context does not contain the answer, state that. "
f"Context: \"{req.selected_text}\" "
f"Question: {req.question}"
)
# Run the agent with the context-aware prompt and user context
result = await Runner.run(
Triage_Agent,
prompt,
context=user_context
)
# CRITICAL: Response structure must match React component: {"answer": "...", "sources": []}
return {
"answer": result.final_output,
"sources": rag_result.sources if rag_result and hasattr(rag_result, 'sources') else [] # Must be included, even if empty
}
@app.get("/health")
def health_check():
"""Health check endpoint for Vercel deployment."""
return {"status": "healthy", "message": "Backend is running"}
@app.post("/api/translate-text")
async def translate_text(req: TranslationRequest):
"""Translates text to the specified target language."""
from deep_translator import GoogleTranslator
try:
# Validate target language
if req.target_language != 'ur':
return {"error": "Currently only Urdu (ur) translation is supported"}
# Perform translation
translated = GoogleTranslator(source='en', target=req.target_language).translate(req.text)
return {"translated_text": translated}
except Exception as e:
logging.error(f"Translation error: {e}")
return {"error": str(e)}
|