Tripagra
Add api/index.py for Vercel serverless function and update frontend endpoint
6aa1546
Raw
History Blame Contribute Delete
4.84 kB
"""
Vercel Serverless Function - Main API endpoint
Handles: POST /api
"""
from http.server import BaseHTTPRequestHandler
import json
import sys
import os
# Add parent directory to path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.rag import RAGSystem
from src.escalation import EscalationEngine
from src.llm_client import LLMClient
from src.config import Settings
# Global components (cached across invocations)
_settings = None
_rag_system = None
_llm_client = None
_escalation_engine = None
def get_components():
"""Initialize and return components (cached)."""
global _settings, _rag_system, _llm_client, _escalation_engine
if _rag_system is None:
_settings = Settings()
articles_dir = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"data",
"articles"
)
_rag_system = RAGSystem(articles_dir=articles_dir)
_llm_client = LLMClient(api_key=_settings.google_api_key, model=_settings.llm_model)
_escalation_engine = EscalationEngine(
llm_client=_llm_client,
relevance_threshold=_settings.relevance_threshold
)
return _settings, _rag_system, _llm_client, _escalation_engine
class handler(BaseHTTPRequestHandler):
"""Vercel serverless function handler."""
def do_POST(self):
"""Handle POST requests."""
try:
settings, rag_system, llm_client, escalation_engine = get_components()
# Read request body
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length).decode('utf-8')
# Parse JSON
try:
data = json.loads(body)
question = data.get('question', '').strip()
except json.JSONDecodeError:
self.send_error_response(400, "Invalid JSON")
return
# Validate question
if not question:
self.send_error_response(400, "Question cannot be empty")
return
# Retrieve relevant chunks
chunks = rag_system.retrieve(question, top_k=settings.top_k_chunks)
# Decide whether to answer or escalate
decision = escalation_engine.process_question(question, chunks)
# Send success response
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
response = {
"response_type": decision.action,
"message": decision.message,
"confidence_explanation": decision.confidence_explanation,
"sources": decision.sources
}
self.wfile.write(json.dumps(response).encode('utf-8'))
except Exception as e:
print(f"Error processing question: {str(e)}")
import traceback
traceback.print_exc()
self.send_error_response(500, "Internal server error")
def do_OPTIONS(self):
"""Handle OPTIONS requests for CORS."""
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
def do_GET(self):
"""Handle GET requests (health check)."""
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
response = {
"status": "healthy",
"message": "Smart Escalation API is running",
"vector_store_loaded": _rag_system is not None
}
self.wfile.write(json.dumps(response).encode('utf-8'))
def send_error_response(self, status_code, message):
"""Send error response."""
self.send_response(status_code)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
response = {
"response_type": "error",
"message": message,
"confidence_explanation": "Request error",
"sources": None
}
self.wfile.write(json.dumps(response).encode('utf-8'))