Skip to content

symfonic.agent.facade_args

facade_args

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}."
        )