Skip to content

symfonic.capabilities.delegation.values

values

The values delegation passes around: entries, plans, outcomes, reports.

Every type here is frozen. A roster entry that could be edited after the graph compiled would let the palette the model was shown and the child that actually runs disagree, and nothing downstream would notice.

ChildBuildPlan dataclass

ChildBuildPlan(*, name: str, description: str, config: Any, provider: Any, tools: Sequence[Any] = (), when_to_use: str | None = None, shared: dict[str, Any] = dict())

Everything a builder needs, and nothing about how to build it.

The compiler's whole output for one spec. Splitting the decision (this package) from the construction (a builder port at the composition root) is what lets inheritance, sanitisation, and ownership be tested without ever constructing an agent — and what keeps a capability from importing the facade whose children it compiles.

Attributes:

Name Type Description
shared dict[str, Any]

Wiring the parent hands every child it builds — the memory orchestrator, for instance. A dict rather than named fields because the set is the composition root's business, not this package's: a capability that enumerated the parent's collaborators would be coupled to all of them.

ChildDefinition dataclass

ChildDefinition(*, name: str, description: str, version: str)

The catalogue shape an agent store answers list/read with.

DelegationOutcome

Bases: StrEnum

What happened when the parent's model asked to delegate.

Four values, and only one of them is a delegation. The other three are the reasons the shipped tool returned prose for — depth reached, name unknown, child raised — promoted from strings a caller would have to pattern-match into a value it can branch on. The message stays; what changes is that the message is no longer the only record.

delivered property

delivered: bool

True only for a hand-off a child actually completed.

Read by the recording path: a refused, misrouted, or failed delegation is not something the parent delegated to, and stamping it on the response would make an operator reading delegated_to believe a child ran.

DelegationRecord dataclass

DelegationRecord(*, name: str, outcome: DelegationOutcome, depth: int, message: str, run_id: str = '', root_run_id: str = '', parent_run_id: str | None = None)

One attempted hand-off, as a value.

Attributes:

Name Type Description
name str

The routing key the model asked for — kept even when it matched nothing, because "which name did it guess?" is the question an operator asks about an unknown-child outcome.

depth int

The depth the child would have run at, whether or not it ran.

message str

Exactly what the tool returns to the model.

RosterEntry dataclass

RosterEntry(*, name: str, description: str, runner: Any, when_to_use: str | None = None, owned: bool = False)

One registered child: how to reach it, and who owns it.

The declaration form is deliberately not carried here. By the time an entry exists the compiler has already answered every question the two forms differed on, and keeping the spec around would invite a later stage to re-decide inheritance behind the compiler's back.

listing

listing() -> str

The one line the parent's model reads for this child.

Source code in src/symfonic/capabilities/delegation/values.py
def listing(self) -> str:
    """The one line the parent's model reads for this child."""
    extra = f" ({self.when_to_use})" if self.when_to_use else ""
    return f"  - {self.name}: {self.description}{extra}"

TeardownReport dataclass

TeardownReport(*, verb: str, attempted: int = 0, failures: tuple[str, ...] = ())

What a flush or close managed, and what it did not.

clean is derived from :attr:failures rather than set, so there is no field an unlucky caller can pass to make a teardown that lost a pool look successful.

merge

merge(other: TeardownReport) -> TeardownReport

Combine two phases of one shutdown into a single report.

Source code in src/symfonic/capabilities/delegation/values.py
def merge(self, other: TeardownReport) -> TeardownReport:
    """Combine two phases of one shutdown into a single report."""
    return TeardownReport(
        verb=f"{self.verb}+{other.verb}",
        attempted=self.attempted + other.attempted,
        failures=self.failures + other.failures,
    )