11from __future__ import annotations
22
3+ import hashlib
34import json
45import logging
56import secrets
1415from router .observability import (
1516 AuditLogger ,
1617 PromptReviewLogger ,
18+ RouteCounters ,
1719 configure_logging ,
1820 log_route_complete ,
1921 log_route_error ,
@@ -81,6 +83,7 @@ def create_app(
8183 max_chars = settings .prompt_log_max_chars ,
8284 )
8385 audit_metadata = route_audit_metadata (settings )
86+ route_counters = RouteCounters ()
8487
8588 app = FastAPI (title = "IntentMux" )
8689
@@ -117,6 +120,11 @@ def require_inbound_auth(request: Request) -> JSONResponse | None:
117120 headers = {"www-authenticate" : "Bearer" },
118121 )
119122
123+ def require_diagnostic_auth (request : Request ) -> JSONResponse | None :
124+ if settings .cloud_mode or settings .inbound_api_keys :
125+ return require_inbound_auth (request )
126+ return None
127+
120128 @app .get ("/v1/models" )
121129 async def models (request : Request ) -> JSONResponse :
122130 auth_error = require_inbound_auth (request )
@@ -145,6 +153,20 @@ async def ready(request: Request) -> JSONResponse:
145153 status_code = 200 if report .ready else 503 ,
146154 )
147155
156+ @app .get ("/v1/intentmux/status" )
157+ async def runtime_status (request : Request ) -> JSONResponse :
158+ auth_error = require_diagnostic_auth (request )
159+ if auth_error is not None :
160+ return auth_error
161+ return JSONResponse (runtime_status_payload (settings ))
162+
163+ @app .get ("/v1/intentmux/counters" )
164+ async def runtime_counters (request : Request ) -> JSONResponse :
165+ auth_error = require_diagnostic_auth (request )
166+ if auth_error is not None :
167+ return auth_error
168+ return JSONResponse (route_counters .snapshot ())
169+
148170 @app .post ("/v1/intentmux/decision" )
149171 async def route_decision (request : Request ) -> Any :
150172 auth_error = require_inbound_auth (request )
@@ -235,6 +257,7 @@ async def chat_completions(request: Request) -> Response:
235257 format_signals = format_signals ,
236258 audit_metadata = audit_metadata ,
237259 audit_logger = audit_logger ,
260+ route_counters = route_counters ,
238261 )
239262 return upstream_error_response (
240263 request_id = request_id ,
@@ -261,6 +284,7 @@ async def chat_completions(request: Request) -> Response:
261284 format_signals = format_signals ,
262285 audit_metadata = audit_metadata ,
263286 audit_logger = audit_logger ,
287+ route_counters = route_counters ,
264288 )
265289 await stream_context .__aexit__ (None , None , None )
266290 return upstream_error_response (
@@ -288,6 +312,7 @@ async def chat_completions(request: Request) -> Response:
288312 format_signals = format_signals ,
289313 audit_metadata = audit_metadata ,
290314 audit_logger = audit_logger ,
315+ route_counters = route_counters ,
291316 ),
292317 status_code = upstream .status_code ,
293318 headers = headers ,
@@ -314,6 +339,7 @@ async def chat_completions(request: Request) -> Response:
314339 format_signals = format_signals ,
315340 audit_metadata = audit_metadata ,
316341 audit_logger = audit_logger ,
342+ route_counters = route_counters ,
317343 )
318344 return upstream_error_response (
319345 request_id = request_id ,
@@ -339,6 +365,7 @@ async def chat_completions(request: Request) -> Response:
339365 format_signals = format_signals ,
340366 audit_metadata = audit_metadata ,
341367 audit_logger = audit_logger ,
368+ route_counters = route_counters ,
342369 )
343370 return upstream_error_response (
344371 request_id = request_id ,
@@ -364,6 +391,7 @@ async def chat_completions(request: Request) -> Response:
364391 audit_metadata = audit_metadata ,
365392 usage = usage_from_response_content (upstream .content ),
366393 audit_logger = audit_logger ,
394+ route_counters = route_counters ,
367395 )
368396 return Response (
369397 content = upstream .content ,
@@ -401,6 +429,117 @@ def route_audit_metadata(settings: RouterSettings) -> dict[str, str]:
401429 return metadata
402430
403431
432+ def runtime_status_payload (settings : RouterSettings ) -> dict [str , Any ]:
433+ return {
434+ "cloud_mode" : settings .cloud_mode ,
435+ "config" : runtime_config_status (settings ),
436+ "routing" : runtime_routing_status (settings ),
437+ "routes" : runtime_route_status (settings ),
438+ "hard_rules" : runtime_hard_rule_status (settings ),
439+ "warnings" : runtime_status_warnings (settings ),
440+ }
441+
442+
443+ def runtime_config_status (settings : RouterSettings ) -> dict [str , Any ]:
444+ payload : dict [str , Any ] = {
445+ "config_source" : settings .config_source ,
446+ "config_sha256" : settings .config_sha256 ,
447+ "route_bank_sha256" : settings .route_bank_sha256 ,
448+ "runtime_config_exists" : settings .runtime_config_exists ,
449+ "route_bank_loaded" : settings .route_bank_loaded ,
450+ "audit_log_enabled" : settings .audit_log_enabled ,
451+ "access_log" : settings .access_log ,
452+ "prompt_log_mode" : settings .prompt_log_mode ,
453+ }
454+ if not settings .cloud_mode :
455+ payload .update (
456+ {
457+ "config_path" : settings .config_path ,
458+ "runtime_home" : settings .runtime_home ,
459+ "audit_log_dir" : settings .audit_log_dir ,
460+ "prompt_log_dir" : settings .prompt_log_dir ,
461+ "route_bank_path" : settings .route_bank_path ,
462+ }
463+ )
464+ return payload
465+
466+
467+ def runtime_routing_status (settings : RouterSettings ) -> dict [str , Any ]:
468+ return {
469+ "entry_model" : settings .entry_model ,
470+ "entry_model_aliases" : sorted (settings .entry_model_aliases ),
471+ "fallback_route_id" : settings .fallback_route_id ,
472+ "route_id_aliases" : dict (sorted (settings .route_id_aliases .items ())),
473+ "route_kernel" : settings .route_kernel ,
474+ "aurelio_router" : settings .aurelio_router ,
475+ "aurelio_hybrid_alpha" : settings .aurelio_hybrid_alpha ,
476+ "threshold" : settings .threshold ,
477+ "margin" : settings .margin ,
478+ "agent_signal_enabled" : settings .agent_signal_enabled ,
479+ "agent_signal_route_id" : settings .effective_agent_signal_route_id ,
480+ "agent_signal_min_input_chars" : settings .agent_signal_min_input_chars ,
481+ "agent_signal_min_message_count" : settings .agent_signal_min_message_count ,
482+ }
483+
484+
485+ def runtime_route_status (settings : RouterSettings ) -> dict [str , Any ]:
486+ return {
487+ route_id : runtime_route_entry (route_id , route .target_model , len (route .utterances ), settings )
488+ for route_id , route in sorted (settings .routes .items ())
489+ }
490+
491+
492+ def runtime_route_entry (
493+ route_id : str ,
494+ target_model : str | None ,
495+ utterance_count : int ,
496+ settings : RouterSettings ,
497+ ) -> dict [str , Any ]:
498+ entry : dict [str , Any ] = {
499+ "utterance_count" : utterance_count ,
500+ "target_model_configured" : bool (target_model ),
501+ }
502+ if not settings .cloud_mode :
503+ entry ["target_model" ] = target_model
504+ else :
505+ entry ["target_model_sha256" ] = stable_sha256 (target_model )
506+ return entry
507+
508+
509+ def runtime_hard_rule_status (settings : RouterSettings ) -> list [dict [str , Any ]]:
510+ rows : list [dict [str , Any ]] = []
511+ for hard_rule in settings .hard_rules :
512+ row : dict [str , Any ] = {
513+ "route_id" : hard_rule .route_id ,
514+ "keyword_count" : len (hard_rule .keywords ),
515+ }
516+ if settings .cloud_mode :
517+ row ["keyword_sha256s" ] = [
518+ stable_sha256 (keyword ) for keyword in hard_rule .keywords
519+ ]
520+ else :
521+ row ["keywords" ] = list (hard_rule .keywords )
522+ rows .append (row )
523+ return rows
524+
525+
526+ def runtime_status_warnings (settings : RouterSettings ) -> list [str ]:
527+ warnings : list [str ] = []
528+ if settings .config_source == "repo_default" and not settings .runtime_config_exists :
529+ warnings .append ("runtime_config_missing" )
530+ if settings .placeholder_target_models :
531+ warnings .append ("placeholder_targets" )
532+ if settings .cloud_mode and settings .prompt_log_mode == "off" :
533+ warnings .append ("prompt_review_log_disabled" )
534+ return warnings
535+
536+
537+ def stable_sha256 (value : str | None ) -> str | None :
538+ if not value :
539+ return None
540+ return hashlib .sha256 (value .encode ("utf-8" )).hexdigest ()
541+
542+
404543def usage_from_response_content (content : bytes ) -> dict [str , int ] | None :
405544 try :
406545 payload = json .loads (content )
@@ -434,6 +573,7 @@ async def stream_with_context(
434573 format_signals : dict [str , Any ] | None = None ,
435574 audit_metadata : dict [str , Any ] | None = None ,
436575 audit_logger : AuditLogger | None = None ,
576+ route_counters : RouteCounters | None = None ,
437577):
438578 if upstream_started_ms is None :
439579 upstream_started_ms = started_ms
0 commit comments