import json import logging import time from typing import Any, Optional logger = logging.getLogger("app") SCHEMA_VERSION = 1 def _normalize_value(value: Any) -> Any: if value is None: return None if isinstance(value, (str, int, float, bool)): return value return str(value) def emit_question_metric( *, stage: str, status: str, duration_ms: Optional[int] = None, first_response_time_ms: Optional[int] = None, trace_id: Optional[str] = None, ai_id: Optional[str] = None, session_id: Optional[str] = None, robot_type: Optional[str] = None, model: Optional[str] = None, stream: Optional[bool] = None, error_type: Optional[str] = None, extra: Optional[dict[str, Any]] = None, ) -> None: payload: dict[str, Any] = { "schema_version": SCHEMA_VERSION, "event": { "kind": "metric", "category": ["question"], "action": "question_perf", }, "stage": stage, "status": status, "observed_at": int(time.time() * 1000), "service": "catalog-agent", } optional_fields = { "trace_id": trace_id, "duration_ms": duration_ms, "first_response_time_ms": first_response_time_ms, "ai_id": ai_id, "session_id": session_id, "robot_type": robot_type, "model": model, "stream": stream, "error_type": error_type, } for key, value in optional_fields.items(): normalized = _normalize_value(value) if normalized is not None: payload[key] = normalized if extra: for key, value in extra.items(): normalized = _normalize_value(value) if normalized is not None: payload[key] = normalized logger.info(json.dumps(payload, ensure_ascii=False, separators=(",", ":")))