File size: 4,837 Bytes
6aa1546
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
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'))