Files
ewoooc/services/event_router.py
ogt ba86f98514
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
feat: integrate Elephant Alpha ecosystem with full ADR-012/013 compliance
- Add ElephantService, AutonomousEngine, Orchestrator, DecisionRouter (EA 4-file stack)
- Fix 10 bugs: URL typo, SQL schema mismatches (price_records JOIN), enum mapping,
  metadata_json, NemoTron PriceThreat dispatch, async/await mismatch, broken imports
- Wire ADR-012 Agent Action Ladder: EventRouter L2 → EA first + AIOrch fallback;
  all decisions dual-write DB + triaged_alert Telegram; momo: callback prefix
- Wire ADR-013 AutoHeal: resource_optimization trigger → AutoHealService
- Add W3 guards: connection cache 300s TTL, $5/hr cost hard limit
- Add W4 persistence: routing decisions + agent performance snapshots → ai_insights
- Add Migration 015: confidence + created_by columns on ai_insights
- Fix run_scheduler.py broken imports (DecisionTracker service didn't exist)
- Fix verify_elephant_integration.py: check_status() → check_connection()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 04:28:26 +08:00

121 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# services/event_router.py
#
# ADR-012 §③: 單一入口 dispatch(event) — L0/L1/L2 分流
# W2-C: L2 優先走 Elephant Alpha OrchestratorEA 不可用時 fallback AIOrchestrator
#
import asyncio
import logging
from typing import Any, Dict, Optional
from services.ai_orchestrator import AIOrchestrator
from services.telegram_templates import alert
from database.manager import get_session
logger = logging.getLogger(__name__)
async def _handle_l1(event: Dict[str, Any], session_id: str) -> Dict[str, Any]:
"""L1: semantic translation + reason analysis (Hermes)."""
orchestrator = AIOrchestrator()
return await orchestrator.handle_l1(event, session_id)
async def _handle_l2(event: Dict[str, Any], session_id: str) -> Dict[str, Any]:
"""
L2: W2-C — EA Orchestrator 優先(動態路由 256K ctx
EA 不可用API key 未設或連線失敗)時 fallback AIOrchestrator。
ADR-012: audit trail 由 EA._log_decision + triaged_alert 雙寫保證。
"""
try:
from services.elephant_service import elephant_service
from services.elephant_alpha_orchestrator import elephant_orchestrator
# 護欄EA API key 未設定則直接 fallback不嘗試連線
if not elephant_service.api_key:
raise RuntimeError("OPENROUTER_API_KEY not configured, using fallback")
# 護欄連線快取確認W3-A cache 300s不會每次都 ping
if not elephant_service.check_connection():
raise RuntimeError("EA connection unavailable, using fallback")
decision = await elephant_orchestrator.analyze_and_coordinate({
"event": event,
"tier": "L2",
"session_id": session_id,
"urgency": "high",
"complexity": "medium",
"task_type": event.get("event_type", "general_analysis"),
})
return {
"source": "elephant_alpha",
"priority": decision.priority,
"confidence": decision.confidence,
"execution_plan": decision.execution_plan,
"agents_required": decision.agents_required,
"reasoning": decision.reasoning,
}
except Exception as e:
logger.warning(f"[EventRouter] EA L2 failed ({e}), fallback → AIOrchestrator")
orchestrator = AIOrchestrator()
return await orchestrator.handle_l2(event, session_id)
async def _handle_l0(event: Dict[str, Any]) -> Dict[str, Any]:
"""L0: return raw event (compatibility/monitoring)."""
return {"status": "ok", "echo": event.get("event_type")}
async def dispatch(event: Dict[str, Any], admin_chat_ids: Optional[list] = None) -> Dict[str, Any]:
"""
Main event routing entry (ADR-012 §③ — 唯一入口).
Output format compatible with routes/bot_api_routes.
"""
tier = _classify(event)
session_id = f"evt:{event.get('event_type')}:{event.get('source', 'unknown')}"
try:
if tier == "L0":
result = await _handle_l0(event)
elif tier == "L1":
result = await _handle_l1(event, session_id)
elif tier == "L2":
result = await _handle_l2(event, session_id)
else:
result = await _handle_l0(event)
return {
"tier": tier,
"sent": 1,
"errors": [],
"latency_ms": 0,
"payload": result,
}
except Exception as e:
logger.exception(f"[EventRouter] dispatch failed: {e}")
return {
"tier": tier,
"sent": 0,
"errors": [str(e)],
"latency_ms": 0,
"payload": None,
}
def _classify(event: Dict[str, Any]) -> str:
sev = event.get("severity", "info")
has_trace = bool(event.get("trace"))
event_type = event.get("event_type", "")
if sev in ("info", "success"):
return "L0"
if sev == "warning":
return "L1" if has_trace else "L0"
if sev == "alert":
if event_type in {"price_threat", "db_connection_error", "crawler_timeout",
"nim_quota_exhausted", "embedding_failure"}:
return "L2"
return "L1"
return "L0"