symfonic.capabilities.tools.values¶
values ¶
Value types for the tool catalogue and selection capability (T3.1.2).
Everything here is frozen. A descriptor is a reading of a tool, not a handle on it: two readings of the same tool compare equal, which is what lets the manifest-parity suite assert equality instead of re-deriving.
SelectionResult
dataclass
¶
SelectionResult(tools: tuple[Any, ...] = (), forced_choice: str | None = None, trace: tuple[StageRecord, ...] = ())
The tools to bind, the choice to force, and why.
StageOutcome
dataclass
¶
StageOutcome(tools: tuple[Any, ...] | None = None, forced_choice: str | None = None, sets_forced_choice: bool = False, degraded: bool = False, detail: str = '')
One stage's answer.
tools is None means abstain โ "no policy here, leave the set
alone" โ and is distinct from an empty tuple, which means "policy
applied and nothing survived". Collapsing the two is how a resolver
that could not decide ends up handing the model zero tools.
force
classmethod
¶
Record a forced tool choice without touching the candidate set.
Source code in src/symfonic/capabilities/tools/values.py
StageRecord
dataclass
¶
StageRecord(stage: str, action: str, kept: tuple[str, ...] = (), dropped: tuple[str, ...] = (), added: tuple[str, ...] = (), reinstated: tuple[str, ...] = (), forced_choice: str | None = None, detail: str = '')
What one stage did, in names, for the decision trace.
ToolDescriptor
dataclass
¶
ToolDescriptor(name: str, tool: Any = None, description: str = '', hidden: bool = False, category: Any = None, safety_level: Any = None, cost_usd: float | None = None, requires: type | None = None, max_calls_per_invocation: int | None = None, scope: str | None = None, routing_mode: str | None = None)
Everything the framework knows about one tool, read once.
tool is the live object so a selection stage can hand the same
instance back to bind_tools; every other field is derived data
the rest of the framework used to re-derive for itself.
summary
property
¶
The first non-empty description line.
A LangChain tool's description is its whole docstring; framework tools run to several hundred characters of Args/Returns prose, enough to blow a JIT manifest budget on one entry.
from_tool
classmethod
¶
Read a tool (and optionally its registry registration).
Hidden-ness has three independent sources and any one of them is
decisive: @symfonic_tool(visibility="hidden"), the lifted
metadata["visible_to_agents"] is False a vanilla LangChain
tool can carry, and ToolRegistration.visible_to_agents. The
pre-T3.1.2 manifest derivation saw only the first two because it
never held the registration.
Source code in src/symfonic/capabilities/tools/values.py
tool_name ¶
Best-effort structural name read.
Returns None for an object with no usable name. Callers treat
None as "cannot be gated by name" and keep the tool, because
dropping it would make a naming bug look like a policy decision.