symfonic.services.observability.values¶
values ¶
Observation values — what an observer is told, in one vocabulary.
Every type here is a frozen, stdlib-only dataclass. That is the same discipline
:mod:symfonic.kernel.contracts.values applies to the loop, and for the same
reason: an observer written against these types never learns a provider SDK, a
span API, or a metrics schema, so a new exporter is a new adapter rather than
a new emission site inside the engine.
Payload fields (query, text, arguments) are None when the
capture policy denied them. None here means withheld, never absent —
the structural siblings (length, argument_names) stay populated so a
denied run is still diagnosable.
CostObservation
dataclass
¶
CostObservation(scope: RunScope, model: str, usage: UsageDelta, cost_usd: float, pricing_unknown: bool, breakdown: Mapping[str, int] = (lambda: MappingProxyType({}))(), text: str | None = None, duration_ms: float = 0.0)
What the run cost, and whether the registry could actually say.
pricing_unknown is the discriminator that keeps "a $0 run" apart from
"a run whose model the pricing registry did not recognise" — two states
that look identical in a dashboard and mean opposite things on a bill.
EventsDropped
dataclass
¶
EventsDropped(scope: RunScope, index: int, dropped_kind: str, dropped_count: int, first_dropped_index: int | None, last_dropped_index: int | None)
A shed notice: what the adapter could not deliver, and over what range.
RunFailed
dataclass
¶
The run emitted its error terminal.
The kernel formats the error as "TypeName: message". Splitting it here
once means every downstream schema — span status, NodeErrorEvent, a log
line — gets the same two fields instead of re-parsing the string.
RunFinished
dataclass
¶
RunFinished(scope: RunScope, index: int, reason: str, text: str | None, usage: UsageDelta, duration_ms: float, stop_reason: str | None)
The run reached exactly one terminal event (EVT-1).
RunScope
dataclass
¶
RunScope(run_id: str, tenant_id: str | None, session_id: str | None, entry_point: str, model: str, provider_family: str = '', prompt: str = '', root_run_id: str = '', parent_run_id: str | None = None)
The identity every observation is attributed to.
Built once per invocation by whoever composes the sink, because the kernel
event stream carries a run_id and nothing else: tenant, session, model
and entry point are plan facts, and re-deriving them per event is how two
observers end up disagreeing about which tenant paid for a run.
RunStarted
dataclass
¶
The run has produced its first event.
TextEmitted
dataclass
¶
One text-bearing event from the stream, in stream order.
ToolCompleted
dataclass
¶
A tool returned. The outcome is opaque, exactly as it is to the kernel.
ToolInvoked
dataclass
¶
ToolInvoked(scope: RunScope, index: int, call_id: str, name: str, arguments: Mapping[str, Any] | None, argument_names: tuple[str, ...] = ())
The loop dispatched an allowlisted tool call.