Skip to content

symfonic.core.observability.metrics_records

metrics_records

The rows a metrics collector keeps, and their dashboard projection.

Split out of :mod:symfonic.core.observability.metrics (409 lines against the 300-line budget). metrics is the collector -- a CallbackHandler that folds events into state. This module is the state's shape: one LLM call, one conversation, the two bounds that keep them from growing without limit, and _record_to_snapshot, which is the only place the public dashboard dict is spelled out.

metrics re-exports every name below.

ConversationRecord dataclass

ConversationRecord(conversation_id: str, tenant_id: str, turns: int = 0, llm_calls: int = 0, input_tokens: int = 0, output_tokens: int = 0, cached_tokens: int = 0, cost_usd: float = 0.0, first_seen: datetime = (lambda: datetime.now(UTC))(), last_seen: datetime = (lambda: datetime.now(UTC))(), call_records: deque[LLMCallRecord] = (lambda: deque(maxlen=MAX_CALL_RECORDS_PER_CONVERSATION))())

Aggregated per-conversation metrics.

LLMCallRecord dataclass

LLMCallRecord(call_id: str, conversation_id: str, model: str, node: str, input_tokens: int, cached_tokens: int, output_tokens: int, cost_usd: float, call_type: str, timestamp: datetime, composition: dict[str, int] = dict(), cache_creation_tokens: int = 0, reasoning_tokens: int = 0, duration_ms: float = 0.0, iteration_index: int = 0, turn_index: int = 0, root_run_id: str = '', parent_run_id: str | None = None)

Single LLM call record for dashboard display.

v8.7.11 — the record now carries the full per-call telemetry the framework already emits on :class:LLMEndEvent (real node name, call_type, iteration_index, turn_index, duration_ms, and the cache_creation/reasoning token splits) instead of hardcoding node="react" / call_type="initial" and dropping latency. All new fields default to zero/empty so pre-v8.7.11 callers that construct the record positionally with the original arg set keep working unchanged.