symfonic.core.testing.deterministic¶
deterministic ¶
Deterministic provider adapters, one per supported wire family (T3.1.4).
MockModelProvider answers "what does the agent do when the model
replies?". It cannot answer "what does the agent do when the model is on
the Anthropic wire?" — it declares no family, so every test written
against it exercises the unknown path, which is the one path where the
framework has no cache dialect, no native content-block encoding and no
forced-tool-choice constraint.
That gap is not academic. Extended thinking blocking a forced tool choice,
cache_control placement, and the top_k sampling knob are all
per-family behaviour, and none of it was reachable from a test that did
not hold a real credential.
:class:DeterministicProvider closes it. It is a real adapter — it
satisfies the provider contract, it appears in the capability matrix
alongside the shipped adapters — that serves a scripted response over a
declared wire family, with no credential, no network and no optional
extra.
Every family-dependent answer is read from the descriptor table rather
than restated here. A double that hard-codes top_k=True for Anthropic
is a second source of truth and the first thing to go stale when the real
row changes; reading the row means a change to the framework's claim about
a family shows up in the double on the same commit.
DeterministicChatModel ¶
Bases: MockChatModel
A scripted chat model that records what actually reached it.
Two things MockChatModel cannot do, both of which a parity matrix
needs:
- Report usage. Its
AIMessagecarries nousage_metadata, so the whole cost path (extract -> typed breakdown -> USD) is unreachable from a test. - Refuse. Its
bind_toolsreturnsselfand swallowstool_choice, so every binding looks successful and the degrade paths the framework documents — nobind_toolsat all, notool_choicekeyword — cannot be exercised.
Both are opt-in here: the defaults behave exactly like
MockChatModel, and a test that wants a refusing provider says so.
DeterministicProvider ¶
DeterministicProvider(family: str = 'unknown', *, response: str = 'Mock response', tool_calls: list[dict[str, Any]] | None = None, structured_response: dict[str, Any] | None = None, usage: dict[str, Any] | None = None, bind_tools_supported: bool = True, tool_choice_supported: bool = True)
A contract-conformant adapter for one declared wire family.
The ClassVars are the declaration the contract checker requires; the
instance attributes set in __init__ are what classification and
resolution actually read, which is the same duck-typed pattern
AWSBedrockProvider uses when it reclassifies itself from the model
it is serving.
Source code in src/symfonic/core/testing/deterministic.py
refusal_reason ¶
A refusal an operator cannot explain is a refusal they cannot fix.
Source code in src/symfonic/core/testing/deterministic.py
supports_forced_tool_choice ¶
Refuse exactly where the declared family's wire refuses.
Pure and derived: the constraint is a property of the wire, so a family whose row does not carry it must not acquire it here.