What the simple facade refuses at construction time.
Split out of :mod:symfonic.agent.facade for the same two reasons
:mod:symfonic.agent.facade_model was: the facade module keeps to the
300-line budget, and these are rules rather than plumbing -- each one names
an argument the constructor will not accept and says why accepting it would
be worse than raising.
Every check here runs before anything is stored, because FAC-8 says
construction validates and stores and does nothing else: a rejected argument
must fail on the line that passed it, not on the first run().
require_instructions
require_instructions(instructions: str | None) -> None
Refuse instructions that are neither a string nor absent.
None is a documented value and keeps FAC-5's meaning -- no system
prompt of the framework's own invention -- so only a non-string that is
not None is an error.
Source code in src/symfonic/agent/facade_args.py
| def require_instructions(instructions: str | None) -> None:
"""Refuse instructions that are neither a string nor absent.
``None`` is a documented value and keeps FAC-5's meaning -- no system
prompt of the framework's own invention -- so only a non-string that is
not ``None`` is an error.
"""
if instructions is not None and not isinstance(instructions, str):
raise ConfigurationError(
f"instructions must be a string or None, got "
f"{type(instructions).__name__}."
)
|
require_provider
require_provider(model_provider: Any) -> None
Refuse a missing or non-conforming provider.
The facade takes the caller's own provider object rather than a name
(FAC-4), so the only thing it can check is the one method it will call.
Checking it here turns a construction-time typo into a construction-time
error instead of an AttributeError inside the first invocation.
Source code in src/symfonic/agent/facade_args.py
| def require_provider(model_provider: Any) -> None:
"""Refuse a missing or non-conforming provider.
The facade takes the caller's own provider object rather than a name
(FAC-4), so the only thing it can check is the one method it will call.
Checking it here turns a construction-time typo into a construction-time
error instead of an ``AttributeError`` inside the first invocation.
"""
if model_provider is None:
raise ConfigurationError(
"model_provider is required: pass an object implementing "
"symfonic.core.ModelProvider (e.g. AnthropicProvider())."
)
if not callable(getattr(model_provider, "get_chat_model", None)):
raise ConfigurationError(
"model_provider must implement ModelProvider.get_chat_model; "
f"got {type(model_provider).__name__}."
)
|
require_round_budget
require_round_budget(max_model_rounds: int | None) -> None
Refuse a round budget that cannot bound a loop.
bool is an int subclass, so max_model_rounds=True would pass
an unguarded integer check and silently mean one round -- a tool-using
agent would return after its first provider call with a partial answer
presented as a whole one. Zero and negatives have no reading at all: the
kernel would stop before the first round. None is the documented way
to ask for the facade default, and is the only non-integer accepted.
Source code in src/symfonic/agent/facade_args.py
| def require_round_budget(max_model_rounds: int | None) -> None:
"""Refuse a round budget that cannot bound a loop.
``bool`` is an ``int`` subclass, so ``max_model_rounds=True`` would pass
an unguarded integer check and silently mean one round -- a tool-using
agent would return after its first provider call with a partial answer
presented as a whole one. Zero and negatives have no reading at all: the
kernel would stop before the first round. ``None`` is the documented way
to ask for the facade default, and is the only non-integer accepted.
"""
if max_model_rounds is None:
return
if (
isinstance(max_model_rounds, bool)
or not isinstance(max_model_rounds, int)
or max_model_rounds < 1
):
raise ConfigurationError(
"max_model_rounds must be a positive integer or None, got "
f"{max_model_rounds!r}."
)
|