pythonprincess commited on
Commit
389406b
·
verified ·
1 Parent(s): 8a5d8cb

Delete router.py

Browse files
Files changed (1) hide show
  1. router.py +0 -802
router.py DELETED
@@ -1,802 +0,0 @@
1
- """
2
- 🚦 PENNY Request Router - Enhanced for Azure ML Production
3
- Routes incoming requests to appropriate agents and tools based on intent classification.
4
- Integrates with enhanced logging, location detection, and intent classification.
5
-
6
- Mission: Ensure every resident request reaches the right civic service with proper tracking.
7
- """
8
-
9
- import logging
10
- import time
11
- import asyncio
12
- import os
13
- from typing import Dict, Any, Optional, List
14
- from pathlib import Path
15
- from fastapi import APIRouter, HTTPException
16
- from fastapi.responses import JSONResponse
17
-
18
- from app.model_loader import ModelLoader
19
- from app.tool_agent import handle_tool_request
20
- from app.weather_agent import (
21
- get_weather_for_location,
22
- weather_to_event_recommendations,
23
- recommend_outfit
24
- )
25
- from app.intents import classify_intent_detailed, IntentType
26
- from app.event_weather import get_event_recommendations_with_weather
27
- from app.location_utils import (
28
- detect_location_from_text,
29
- get_city_info,
30
- validate_coordinates
31
- )
32
- from app.logging_utils import log_interaction, sanitize_for_logging
33
-
34
- logger = logging.getLogger(__name__)
35
-
36
- # Initialize FastAPI router
37
- router = APIRouter(prefix="/api", tags=["Penny API"])
38
-
39
- # Initialize model loader
40
- models = ModelLoader()
41
-
42
- # Supported languages for translation routing
43
- SUPPORTED_LANGUAGES = [
44
- "arabic", "french", "german", "hindi", "mandarin",
45
- "portuguese", "russian", "spanish", "swahili",
46
- "tagalog", "urdu", "vietnamese", "translate", "translation"
47
- ]
48
-
49
- def validate_request_payload(payload: dict) -> tuple[bool, Optional[str]]:
50
- """
51
- Validate incoming request payload for required fields and data types.
52
-
53
- Args:
54
- payload: Request payload dictionary
55
-
56
- Returns:
57
- Tuple of (is_valid, error_message)
58
- """
59
- if not isinstance(payload, dict):
60
- return False, "Payload must be a dictionary"
61
-
62
- # Check for required input field
63
- if "input" not in payload:
64
- return False, "Missing required field: 'input'"
65
-
66
- user_input = payload.get("input")
67
- if not isinstance(user_input, str):
68
- return False, "Field 'input' must be a string"
69
-
70
- if not user_input.strip():
71
- return False, "Input cannot be empty"
72
-
73
- # Validate coordinates if provided
74
- lat = payload.get("lat")
75
- lon = payload.get("lon")
76
-
77
- if lat is not None or lon is not None:
78
- if lat is None or lon is None:
79
- return False, "Both 'lat' and 'lon' must be provided together"
80
-
81
- try:
82
- lat = float(lat)
83
- lon = float(lon)
84
- is_valid, error = validate_coordinates(lat, lon)
85
- if not is_valid:
86
- return False, f"Invalid coordinates: {error}"
87
- except (ValueError, TypeError):
88
- return False, "Coordinates must be numeric values"
89
-
90
- # Validate tenant_id if provided
91
- tenant_id = payload.get("tenant_id")
92
- if tenant_id is not None:
93
- if not isinstance(tenant_id, str):
94
- return False, "Field 'tenant_id' must be a string"
95
- if not tenant_id.strip():
96
- return False, "Field 'tenant_id' cannot be empty"
97
-
98
- return True, None
99
-
100
-
101
- def extract_location_info(payload: dict, user_input: str) -> Dict[str, Any]:
102
- """
103
- Extract and validate location information from payload or user input.
104
-
105
- Args:
106
- payload: Request payload
107
- user_input: User's input text
108
-
109
- Returns:
110
- Dictionary with location info: {lat, lon, tenant_id, city_info, location_source}
111
- """
112
- location_info = {
113
- "lat": payload.get("lat"),
114
- "lon": payload.get("lon"),
115
- "tenant_id": payload.get("tenant_id", "default"),
116
- "city_info": None,
117
- "location_source": "none"
118
- }
119
-
120
- try:
121
- # Try to get location from coordinates
122
- if location_info["lat"] is not None and location_info["lon"] is not None:
123
- location_info["location_source"] = "coordinates"
124
-
125
- # Try to map coordinates to a tenant city
126
- if location_info["tenant_id"] == "default":
127
- city_info = get_city_info(location_info["tenant_id"])
128
- if city_info:
129
- location_info["city_info"] = city_info
130
-
131
- # Try to detect location from text if not provided
132
- elif "near me" in user_input.lower() or any(
133
- keyword in user_input.lower()
134
- for keyword in ["in", "at", "near", "around"]
135
- ):
136
- detected = detect_location_from_text(user_input)
137
- if detected.get("found"):
138
- location_info["tenant_id"] = detected.get("tenant_id", "default")
139
- location_info["city_info"] = detected.get("city_info")
140
- location_info["location_source"] = "text_detection"
141
- logger.info(
142
- f"Detected location from text: {location_info['tenant_id']}"
143
- )
144
-
145
- # Get city info for tenant_id if we have it
146
- if not location_info["city_info"] and location_info["tenant_id"] != "default":
147
- location_info["city_info"] = get_city_info(location_info["tenant_id"])
148
-
149
- except Exception as e:
150
- logger.warning(f"Error extracting location info: {e}")
151
-
152
- return location_info
153
-
154
-
155
- def route_request(payload: dict) -> dict:
156
- """
157
- Main routing function for PENNY requests.
158
- Routes requests to appropriate agents based on intent classification.
159
-
160
- Args:
161
- payload: Request payload with user input and metadata
162
-
163
- Returns:
164
- Response dictionary with agent output and metadata
165
- """
166
- start_time = time.time()
167
-
168
- try:
169
- # Validate request payload
170
- is_valid, error_msg = validate_request_payload(payload)
171
- if not is_valid:
172
- logger.warning(f"Invalid request payload: {error_msg}")
173
- return {
174
- "error": "Oops! I couldn't understand that request. " + error_msg,
175
- "status": "validation_error",
176
- "response_time_ms": round((time.time() - start_time) * 1000)
177
- }
178
-
179
- # Extract basic request info
180
- user_input = payload.get("input", "").strip()
181
- role = payload.get("role", "unknown")
182
-
183
- # Sanitize input for logging (remove PII)
184
- sanitized_input = sanitize_for_logging(user_input)
185
-
186
- # Extract location information
187
- location_info = extract_location_info(payload, user_input)
188
- tenant_id = location_info["tenant_id"]
189
- lat = location_info["lat"]
190
- lon = location_info["lon"]
191
-
192
- logger.info(
193
- f"Routing request from tenant '{tenant_id}', role '{role}', "
194
- f"location_source: {location_info['location_source']}"
195
- )
196
-
197
- # Classify intent using enhanced intent classifier
198
- try:
199
- intent_result = classify_intent_detailed(user_input)
200
- intent = intent_result["intent"]
201
- confidence = intent_result["confidence"]
202
- is_compound = intent_result["is_compound"]
203
-
204
- logger.info(
205
- f"Intent classified: {intent} (confidence: {confidence:.2f}, "
206
- f"compound: {is_compound})"
207
- )
208
-
209
- except Exception as e:
210
- logger.error(f"Intent classification failed: {e}")
211
- intent = IntentType.GENERAL
212
- confidence = 0.0
213
- is_compound = False
214
-
215
- # EMERGENCY ROUTING - Highest priority
216
- if intent == IntentType.EMERGENCY:
217
- logger.critical(
218
- f"EMERGENCY intent detected from tenant '{tenant_id}'. "
219
- f"Routing to safety protocols."
220
- )
221
-
222
- # Log emergency interaction for compliance
223
- log_interaction(
224
- tenant_id=tenant_id,
225
- interaction_type="emergency",
226
- intent="emergency",
227
- response_time_ms=round((time.time() - start_time) * 1000),
228
- success=True,
229
- metadata={
230
- "sanitized_input": sanitized_input,
231
- "requires_followup": True,
232
- "escalation_level": "critical"
233
- }
234
- )
235
-
236
- return {
237
- "response": (
238
- "I can see you might need urgent help. Please contact:\n\n"
239
- "🚨 **Emergency Services**: 911\n"
240
- "💚 **National Crisis Hotline**: 988\n"
241
- "💬 **Crisis Text Line**: Text HOME to 741741\n\n"
242
- "You're not alone, and help is available 24/7."
243
- ),
244
- "intent": "emergency",
245
- "model_id": "safety-agent",
246
- "tenant_id": tenant_id,
247
- "user_role": role,
248
- "response_time_ms": round((time.time() - start_time) * 1000),
249
- "escalation_required": True
250
- }
251
-
252
- # WEATHER ROUTING
253
- if intent == IntentType.WEATHER:
254
- return handle_weather_request(
255
- user_input, lat, lon, tenant_id, role, start_time
256
- )
257
-
258
- # WEATHER + EVENTS ROUTING (compound intent)
259
- if intent == IntentType.WEATHER_EVENTS or (
260
- is_compound and "weather" in intent_result.get("components", [])
261
- ):
262
- return handle_weather_events_request(
263
- user_input, lat, lon, tenant_id, role, start_time
264
- )
265
-
266
- # EVENTS ROUTING
267
- if intent == IntentType.EVENTS:
268
- return handle_events_request(
269
- user_input, tenant_id, role, start_time
270
- )
271
-
272
- # TOOL-BASED ROUTING (transit, alerts, resources, etc.)
273
- if intent in [
274
- IntentType.TRANSIT, IntentType.ALERTS, IntentType.RESOURCES,
275
- IntentType.PUBLIC_WORKS
276
- ]:
277
- return handle_tool_based_request(
278
- user_input, intent, tenant_id, role, start_time
279
- )
280
-
281
- # TRANSLATION ROUTING
282
- if intent == IntentType.TRANSLATION or any(
283
- lang in user_input.lower() for lang in SUPPORTED_LANGUAGES
284
- ):
285
- return handle_translation_request(
286
- user_input, tenant_id, role, start_time
287
- )
288
-
289
- # DOCUMENT/PDF ROUTING
290
- if any(term in user_input.lower() for term in ["form", "upload", "document", "pdf"]):
291
- return handle_document_request(
292
- user_input, tenant_id, role, start_time
293
- )
294
-
295
- # SENTIMENT ANALYSIS ROUTING
296
- if any(term in user_input.lower() for term in ["angry", "sentiment", "how do i feel"]):
297
- return handle_sentiment_request(
298
- user_input, tenant_id, role, start_time
299
- )
300
-
301
- # BIAS DETECTION ROUTING
302
- if any(term in user_input.lower() for term in ["bias", "is this fair", "offensive"]):
303
- return handle_bias_request(
304
- user_input, tenant_id, role, start_time
305
- )
306
-
307
- # GENERAL/FALLBACK ROUTING
308
- return handle_general_request(
309
- user_input, tenant_id, role, start_time
310
- )
311
-
312
- except Exception as e:
313
- logger.error(f"Unexpected error in route_request: {e}", exc_info=True)
314
-
315
- return {
316
- "error": (
317
- "I'm having trouble processing that right now. "
318
- "Could you try rephrasing your question? 💛"
319
- ),
320
- "status": "server_error",
321
- "response_time_ms": round((time.time() - start_time) * 1000)
322
- }
323
-
324
-
325
- def handle_weather_request(
326
- user_input: str, lat: Optional[float], lon: Optional[float],
327
- tenant_id: str, role: str, start_time: float
328
- ) -> dict:
329
- """Handle weather-specific requests."""
330
- try:
331
- if lat is None or lon is None:
332
- return {
333
- "response": (
334
- "I'd love to help with the weather! To give you accurate info, "
335
- "I need your location. Can you share your coordinates or tell me "
336
- "what city you're in? 🌤️"
337
- ),
338
- "intent": "weather",
339
- "model_id": "weather-agent",
340
- "tenant_id": tenant_id,
341
- "user_role": role,
342
- "response_time_ms": round((time.time() - start_time) * 1000),
343
- "location_required": True
344
- }
345
-
346
- # Get weather data
347
- weather = asyncio.run(get_weather_for_location(lat, lon))
348
-
349
- # Generate recommendations
350
- recs = weather_to_event_recommendations(weather)
351
- outfit = recommend_outfit(
352
- weather.get("temperature", {}).get("value"),
353
- weather.get("phrase", "")
354
- )
355
-
356
- end_time = time.time()
357
- response_time = round((end_time - start_time) * 1000)
358
-
359
- # Log successful interaction
360
- log_interaction(
361
- tenant_id=tenant_id,
362
- interaction_type="weather",
363
- intent="weather",
364
- response_time_ms=response_time,
365
- success=True
366
- )
367
-
368
- return {
369
- "response": {
370
- "weather": weather,
371
- "recommendations": recs,
372
- "outfit": outfit
373
- },
374
- "intent": "weather",
375
- "model_id": "weather-agent",
376
- "tenant_id": tenant_id,
377
- "user_role": role,
378
- "response_time_ms": response_time
379
- }
380
-
381
- except Exception as e:
382
- logger.error(f"Error handling weather request: {e}")
383
-
384
- return {
385
- "response": (
386
- "I'm having trouble getting the weather right now. "
387
- "The weather service might be down. Want to try again in a moment? 🌦️"
388
- ),
389
- "intent": "weather",
390
- "model_id": "weather-agent",
391
- "tenant_id": tenant_id,
392
- "user_role": role,
393
- "response_time_ms": round((time.time() - start_time) * 1000),
394
- "error": "weather_service_unavailable"
395
- }
396
-
397
-
398
- def handle_weather_events_request(
399
- user_input: str, lat: Optional[float], lon: Optional[float],
400
- tenant_id: str, role: str, start_time: float
401
- ) -> dict:
402
- """Handle combined weather and events requests."""
403
- try:
404
- if lat is None or lon is None:
405
- return {
406
- "response": (
407
- "I can suggest events based on the weather! "
408
- "To do that, I need your location. Can you share your coordinates "
409
- "or tell me what city you're in? 🎉☀️"
410
- ),
411
- "intent": "weather_events",
412
- "model_id": "event-weather-agent",
413
- "tenant_id": tenant_id,
414
- "user_role": role,
415
- "response_time_ms": round((time.time() - start_time) * 1000),
416
- "location_required": True
417
- }
418
-
419
- # Get combined weather and event recommendations
420
- combined = asyncio.run(
421
- get_event_recommendations_with_weather(tenant_id, lat, lon)
422
- )
423
-
424
- end_time = time.time()
425
- response_time = round((end_time - start_time) * 1000)
426
-
427
- # Log successful interaction
428
- log_interaction(
429
- tenant_id=tenant_id,
430
- interaction_type="weather_events",
431
- intent="weather_events",
432
- response_time_ms=response_time,
433
- success=True
434
- )
435
-
436
- return {
437
- "response": combined,
438
- "intent": "weather_events",
439
- "model_id": "event-weather-agent",
440
- "tenant_id": tenant_id,
441
- "user_role": role,
442
- "response_time_ms": response_time
443
- }
444
-
445
- except Exception as e:
446
- logger.error(f"Error handling weather_events request: {e}")
447
-
448
- return {
449
- "response": (
450
- "I'm having trouble combining weather and events right now. "
451
- "Let me try to help you with just one or the other! 🤔"
452
- ),
453
- "intent": "weather_events",
454
- "model_id": "event-weather-agent",
455
- "tenant_id": tenant_id,
456
- "user_role": role,
457
- "response_time_ms": round((time.time() - start_time) * 1000),
458
- "error": "combined_service_unavailable"
459
- }
460
-
461
-
462
- def handle_events_request(
463
- user_input: str, tenant_id: str, role: str, start_time: float
464
- ) -> dict:
465
- """Handle events-only requests."""
466
- try:
467
- tool_response = handle_tool_request(user_input, role, tenant_id, "events")
468
- end_time = time.time()
469
-
470
- return {
471
- "response": tool_response.get("response"),
472
- "intent": "events",
473
- "model_id": "event-agent",
474
- "tenant_id": tool_response.get("city", tenant_id),
475
- "user_role": role,
476
- "response_time_ms": round((end_time - start_time) * 1000)
477
- }
478
-
479
- except Exception as e:
480
- logger.error(f"Error handling events request: {e}")
481
-
482
- return {
483
- "response": (
484
- "I'm having trouble finding events right now. "
485
- "Let me know what you're interested in and I'll do my best! 🎭"
486
- ),
487
- "intent": "events",
488
- "model_id": "event-agent",
489
- "tenant_id": tenant_id,
490
- "user_role": role,
491
- "response_time_ms": round((time.time() - start_time) * 1000),
492
- "error": "events_service_unavailable"
493
- }
494
-
495
-
496
- def handle_tool_based_request(
497
- user_input: str, intent: str, tenant_id: str, role: str, start_time: float
498
- ) -> dict:
499
- """Handle tool-based requests (transit, alerts, resources, etc.)."""
500
- try:
501
- tool_response = handle_tool_request(user_input, role, tenant_id, intent)
502
- end_time = time.time()
503
-
504
- return {
505
- "response": tool_response.get("response"),
506
- "intent": str(intent),
507
- "model_id": tool_response.get("tool", "tool-agent"),
508
- "tenant_id": tool_response.get("city", tenant_id),
509
- "user_role": role,
510
- "response_time_ms": round((end_time - start_time) * 1000)
511
- }
512
-
513
- except Exception as e:
514
- logger.error(f"Error handling tool request for {intent}: {e}")
515
-
516
- return {
517
- "response": (
518
- f"I'm having trouble with that {intent} request right now. "
519
- "Could you try again or ask me something else? 💛"
520
- ),
521
- "intent": str(intent),
522
- "model_id": "tool-agent",
523
- "tenant_id": tenant_id,
524
- "user_role": role,
525
- "response_time_ms": round((time.time() - start_time) * 1000),
526
- "error": f"{intent}_service_unavailable"
527
- }
528
-
529
-
530
- def handle_translation_request(
531
- user_input: str, tenant_id: str, role: str, start_time: float
532
- ) -> dict:
533
- """Handle translation requests."""
534
- model_id = "penny-translate-agent"
535
-
536
- try:
537
- model = models.get(model_id)
538
- if not model:
539
- raise ValueError(f"Translation model not found: {model_id}")
540
-
541
- result = model.predict(user_input)
542
- end_time = time.time()
543
-
544
- return {
545
- "response": result,
546
- "intent": "translation",
547
- "model_id": model_id,
548
- "tenant_id": tenant_id,
549
- "user_role": role,
550
- "response_time_ms": round((end_time - start_time) * 1000)
551
- }
552
-
553
- except Exception as e:
554
- logger.error(f"Error handling translation request: {e}")
555
-
556
- return {
557
- "response": (
558
- "I'm having trouble with translation right now. "
559
- "Which language would you like help with? 🌍"
560
- ),
561
- "intent": "translation",
562
- "model_id": model_id,
563
- "tenant_id": tenant_id,
564
- "user_role": role,
565
- "response_time_ms": round((time.time() - start_time) * 1000),
566
- "error": "translation_service_unavailable"
567
- }
568
-
569
-
570
- def handle_document_request(
571
- user_input: str, tenant_id: str, role: str, start_time: float
572
- ) -> dict:
573
- """Handle document/PDF processing requests."""
574
- model_id = "penny-doc-agent"
575
-
576
- try:
577
- model = models.get(model_id)
578
- if not model:
579
- raise ValueError(f"Document model not found: {model_id}")
580
-
581
- result = model.predict(user_input)
582
- end_time = time.time()
583
-
584
- return {
585
- "response": result,
586
- "intent": "document",
587
- "model_id": model_id,
588
- "tenant_id": tenant_id,
589
- "user_role": role,
590
- "response_time_ms": round((end_time - start_time) * 1000)
591
- }
592
-
593
- except Exception as e:
594
- logger.error(f"Error handling document request: {e}")
595
-
596
- return {
597
- "response": (
598
- "I'm having trouble processing documents right now. "
599
- "What kind of form or document do you need help with? 📄"
600
- ),
601
- "intent": "document",
602
- "model_id": model_id,
603
- "tenant_id": tenant_id,
604
- "user_role": role,
605
- "response_time_ms": round((time.time() - start_time) * 1000),
606
- "error": "document_service_unavailable"
607
- }
608
-
609
-
610
- def handle_sentiment_request(
611
- user_input: str, tenant_id: str, role: str, start_time: float
612
- ) -> dict:
613
- """Handle sentiment analysis requests."""
614
- model_id = "penny-sentiment-agent"
615
-
616
- try:
617
- model = models.get(model_id)
618
- if not model:
619
- raise ValueError(f"Sentiment model not found: {model_id}")
620
-
621
- result = model.predict(user_input)
622
- end_time = time.time()
623
-
624
- return {
625
- "response": result,
626
- "intent": "sentiment",
627
- "model_id": model_id,
628
- "tenant_id": tenant_id,
629
- "user_role": role,
630
- "response_time_ms": round((end_time - start_time) * 1000)
631
- }
632
-
633
- except Exception as e:
634
- logger.error(f"Error handling sentiment request: {e}")
635
-
636
- return {
637
- "response": (
638
- "I'm having trouble analyzing sentiment right now. "
639
- "How are you feeling about things? 💭"
640
- ),
641
- "intent": "sentiment",
642
- "model_id": model_id,
643
- "tenant_id": tenant_id,
644
- "user_role": role,
645
- "response_time_ms": round((time.time() - start_time) * 1000),
646
- "error": "sentiment_service_unavailable"
647
- }
648
-
649
-
650
- def handle_bias_request(
651
- user_input: str, tenant_id: str, role: str, start_time: float
652
- ) -> dict:
653
- """Handle bias detection requests."""
654
- model_id = "penny-bias-checker"
655
-
656
- try:
657
- model = models.get(model_id)
658
- if not model:
659
- raise ValueError(f"Bias model not found: {model_id}")
660
-
661
- result = model.predict(user_input)
662
- end_time = time.time()
663
-
664
- return {
665
- "response": result,
666
- "intent": "bias_check",
667
- "model_id": model_id,
668
- "tenant_id": tenant_id,
669
- "user_role": role,
670
- "response_time_ms": round((end_time - start_time) * 1000)
671
- }
672
-
673
- except Exception as e:
674
- logger.error(f"Error handling bias request: {e}")
675
-
676
- return {
677
- "response": (
678
- "I'm having trouble checking for bias right now. "
679
- "What content would you like me to review? ⚖️"
680
- ),
681
- "intent": "bias_check",
682
- "model_id": model_id,
683
- "tenant_id": tenant_id,
684
- "user_role": role,
685
- "response_time_ms": round((time.time() - start_time) * 1000),
686
- "error": "bias_service_unavailable"
687
- }
688
-
689
-
690
- def handle_general_request(
691
- user_input: str, tenant_id: str, role: str, start_time: float
692
- ) -> dict:
693
- """Handle general/fallback requests."""
694
- model_id = "penny-core-agent"
695
-
696
- try:
697
- model = models.get(model_id)
698
- if not model:
699
- raise ValueError(f"Core model not found: {model_id}")
700
-
701
- result = model.predict(user_input)
702
- end_time = time.time()
703
-
704
- return {
705
- "response": result,
706
- "intent": "general",
707
- "model_id": model_id,
708
- "tenant_id": tenant_id,
709
- "user_role": role,
710
- "response_time_ms": round((end_time - start_time) * 1000)
711
- }
712
-
713
- except Exception as e:
714
- logger.error(f"Error handling general request: {e}")
715
-
716
- return {
717
- "response": (
718
- "I'm having some technical difficulties right now. "
719
- "Can you try asking your question in a different way? "
720
- "Or let me know if you need help with weather, events, or services! 💛"
721
- ),
722
- "intent": "general",
723
- "model_id": model_id,
724
- "tenant_id": tenant_id,
725
- "user_role": role,
726
- "response_time_ms": round((time.time() - start_time) * 1000),
727
- "error": "general_service_unavailable"
728
- }
729
-
730
-
731
- @router.post("/chat", response_model=Dict[str, Any])
732
- async def chat_endpoint(payload: Dict[str, Any]) -> JSONResponse:
733
- """
734
- 💬 Main chat endpoint for Penny.
735
-
736
- Processes user requests and routes them to appropriate handlers.
737
-
738
- Args:
739
- payload: Request payload with 'input', 'tenant_id', 'lat', 'lon', etc.
740
-
741
- Returns:
742
- JSONResponse with Penny's response
743
- """
744
- try:
745
- result = route_request(payload)
746
- return JSONResponse(status_code=200, content=result)
747
- except Exception as e:
748
- logger.error(f"Error in chat endpoint: {e}", exc_info=True)
749
- return JSONResponse(
750
- status_code=500,
751
- content={
752
- "error": "I'm having trouble processing that right now. Please try again! 💛",
753
- "detail": str(e) if os.getenv("DEBUG_MODE", "false").lower() == "true" else None
754
- }
755
- )
756
-
757
-
758
- @router.get("/health/router", response_model=Dict[str, Any])
759
- async def router_health_endpoint() -> JSONResponse:
760
- """
761
- 📊 Router health check endpoint.
762
-
763
- Returns:
764
- Health status of the router component
765
- """
766
- try:
767
- health = get_router_health()
768
- return JSONResponse(status_code=200, content=health)
769
- except Exception as e:
770
- logger.error(f"Router health check failed: {e}")
771
- return JSONResponse(
772
- status_code=500,
773
- content={
774
- "status": "degraded",
775
- "error": str(e)
776
- }
777
- )
778
-
779
-
780
- def get_router_health() -> dict:
781
- """
782
- Check router health status.
783
-
784
- Returns:
785
- Health status dictionary
786
- """
787
- try:
788
- return {
789
- "status": "operational",
790
- "model_loader": "initialized" if models else "not_initialized",
791
- "supported_languages": len(SUPPORTED_LANGUAGES),
792
- "routing_capabilities": [
793
- "weather", "events", "weather_events", "translation",
794
- "documents", "sentiment", "bias_detection", "general"
795
- ]
796
- }
797
- except Exception as e:
798
- logger.error(f"Router health check failed: {e}")
799
- return {
800
- "status": "degraded",
801
- "error": str(e)
802
- }