symfonic.agent.cutover.run_observability¶
run_observability ¶
One run's half of TA8.20's observability: the handle the compile seam takes.
Its companion, :mod:symfonic.agent.cutover.observability, owns the
agent-lifetime half and explains the split. The short version is that
RunScope is the identity every observation is attributed to and three of its
five fields (run_id, session_id, tenant_id) are facts about one
turn, so a suite built once per agent cannot be a scope built once per agent.
This module holds one class, and it is separate from the suite for the reason the repository asks of every module: the two have different lifetimes. Reading "what did this deployment buy" and "what is this turn attributed to" in one file is what let the first draft of this seam attach a per-run identity to an agent-lifetime object.
RunObservability ¶
RunObservability(suite: ObservabilitySuite, *, run_id: str, session_id: str | None, tenant_id: str | None, entry_point: str, prompt: str = '', request: Any = None)
One run's event sink: built when the plan compiles, released with it.
Instances are callables so they can be handed to
AgentPlanFactory.compile(..., sink_factory=...): the factory calls this
object once, with the ModelResolution it just computed, and binds the
return value as ServiceBindings.event_sink. Taking the resolution as an
argument rather than re-deriving the model here is what keeps RunScope
naming the model the plan actually runs — a second resolution is a second
answer that can disagree with the first.
:attr:sink is None until the plan compiles, and stays None when
nothing is watching. :meth:release is safe in either state and safe after
a normal terminal, which is why the caller may put it in a bare finally.
Source code in src/symfonic/agent/cutover/run_observability.py
release
async
¶
Release what an abandoned run left held. Idempotent, never raises.