""" Vercel Serverless Function for handling customer support questions. Endpoint: POST /api/ask """ from http.server import BaseHTTPRequestHandler import json import sys import os # Add parent directory to path to import src modules 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 # Initialize components (cached across invocations) settings = None rag_system = None llm_client = None escalation_engine = None def initialize_components(): """Initialize RAG system, LLM client, and escalation engine.""" 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 ) class handler(BaseHTTPRequestHandler): """Vercel serverless function handler.""" def do_POST(self): """Handle POST requests.""" try: # Initialize components on first request initialize_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)}") 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", "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'))