symfonic.kernel.contracts.forcing¶
forcing ¶
Whether this provider will honour a required tool call, asked once.
ModelProvider has carried supports_forced_tool_choice(config) since
v7.8.5, and it exists to prevent one thing: an extended-thinking model refuses
a hard tool_choice, so binding one anyway produces a vendor error at the
API boundary, after the request is paid for. The legacy engine asked. The
kernel's forced-choice path did not -- it bound the force and let the provider
find out -- which is the invariant unenforced rather than absent.
Asked once, at compile time, because both answers it needs are fixed there:
the provider is the one the agent was composed with and the ModelConfig is
the one the plan resolved. Asking per turn would re-run an adopter's
introspection for an answer that cannot have changed.
ForcingSupport
dataclass
¶
The provider's answer, and its own words for a refusal.
forcing_support ¶
Ask the provider, defaulting to supported when it does not answer.
A provider that does not implement the probe is not refusing -- the method is optional and most providers honour a forced choice. Defaulting to refusal would disable the lever for every custom provider that never heard of the probe, which is a larger break than the one this prevents.
A probe that raises is a different matter and is treated as a refusal: a provider whose introspection is broken cannot be relied on to honour the force either, and a required call that silently becomes optional is the failure this whole path exists to remove.