Skip to content

symfonic.capabilities.tools.execution.values

values

Value types for the tool-execution capability (T3.1.3).

Everything here is frozen. A :class:ToolCall is a reading of what the model asked for, not a handle on a provider object, so two readings of the same call compare equal โ€” which is what lets the lifecycle parity suite assert equality instead of re-deriving.

ExecutionReport dataclass

ExecutionReport(outcomes: tuple[ToolOutcome, ...] = (), trace: tuple[PhaseRecord, ...] = ())

Every call's outcome, in call order, plus the phase trace.

PhaseRecord dataclass

PhaseRecord(phase: str, call_id: str, action: str, detail: str = '')

What one lifecycle phase did to one call, for the trace.

PreconditionVerdict dataclass

PreconditionVerdict(admitted: bool, content: str, reason: str, is_error: bool = True, undetermined: bool = False)

One precondition's answer for one call.

Three answers, and the third is narrow on purpose. A check that is happy returns None, so "admitted" is the absence of an objection rather than a claim any single check is entitled to make. A check that objects returns :meth:block. A check that could not reach the thing it needed in order to decide returns :meth:unavailable -- and only that case.

Why the third is not "anything went wrong". An enforcement fault must not become a permission. A predicate that is present but malformed, an evaluator that raised, a state the rule cannot read: each of those is a gate that failed while a rule existed, and admitting on them turns a broken gate into an open one. What unavailable covers is the narrower fact that the store of rules could not be reached at all -- there may be no rule for this call, and refusing every tool because a database is down takes an agent offline for a reason unrelated to what it was asked to do.

The distinction is declared by the check rather than inferred by the kernel, because only the check knows which of the two happened.

unavailable classmethod

unavailable(*, reason: str) -> PreconditionVerdict

No rules could be consulted, so no rule was applied.

Admits, and says it admitted without deciding. content is empty because nothing is fed back to the model: the call runs, and the fact that it ran unchecked belongs in the diagnostic rather than in the conversation.

Source code in src/symfonic/capabilities/tools/execution/values.py
@classmethod
def unavailable(cls, *, reason: str) -> PreconditionVerdict:
    """No rules could be consulted, so no rule was applied.

    Admits, and says it admitted without deciding. ``content`` is empty
    because nothing is fed back to the model: the call runs, and the fact
    that it ran unchecked belongs in the diagnostic rather than in the
    conversation.
    """
    return cls(admitted=True, content="", reason=reason, undetermined=True)

SteeringResult dataclass

SteeringResult(call_id: str, tool_name: str, content: str, is_error: bool = True, origin: str = 'steering')

The answer given to a call that was not dispatched.

It carries call_id because an unanswered tool call permanently bricks a provider thread: every blocked call owes the transcript exactly one result.

ToolCall dataclass

ToolCall(id: str, name: str, args: Mapping[str, Any] = dict())

One call the model asked for: an id, a name, and arguments.

id may be empty. A call with no id cannot be answered, and the framework keeps it rather than discarding it so the provider bug surfaces as an unanswered call rather than as a call that vanished.

signature property

signature: str

Name plus arguments โ€” the repeat key the loop cut counts.

ToolOutcome dataclass

ToolOutcome(call: ToolCall, content: str, is_error: bool = False, attempts: int = 0, phase: str = 'dispatch', reason: str = '', value: Any = None)

The single result of one call, whatever produced it.

ToolProgress dataclass

ToolProgress(call_id: str, tool_name: str, sequence: int, payload: Any = None)

One intermediate value a still-running tool emitted.