symfonic.services.models.contract¶
contract ¶
The provider adapter contract, as a check rather than a paragraph.
ModelProvider is a runtime_checkable Protocol, which proves only that
method names exist. Every failure this module catches passed that check and
still broke at an adopter's API boundary:
- an adapter that never declares
_symfonic_provider_familyclassifies as"unknown", so its Anthropic traffic silently losescache_controland its images are encoded in the wrong block shape; - an adapter that declares no default model makes the simple facade send
claude-sonnet-4-5to a gateway that has never heard of it; - an adapter that refuses a forced tool choice without a
refusal_reasonleaves the engine's per-turn WARN unable to name the cause.
The checker is deliberately structural, not a smoke test: it never calls
get_chat_model and never constructs a client, so it runs with no
credentials and no optional extras installed.
ProviderContractViolation
dataclass
¶
One way an adapter fails the contract.
check_provider_contract ¶
Check one provider class or instance against the adapter contract.
Accepts either, because adapters differ in whether they can be constructed
without credentials. Given a class, the probes that need an instance run
against object.__new__(cls) โ no __init__, so no credential read,
no network, no side effects. The Protocol already requires
supports_forced_tool_choice to be a pure function of its ModelConfig
argument, so an adapter that cannot answer from an uninitialised instance
is itself outside the contract; those probes are skipped rather than
guessed at, and the structural checks still apply.