Skip to content

symfonic.core.config

config

AgentConfig, ModelConfig, CompactionConfig โ€” frozen configuration dataclasses.

Per TDD ยง2.3: No provider field on ModelConfig โ€” provider is injected via BaseAgentDeps(ModelProvider=...).

AgentConfig dataclass

AgentConfig(
    model: ModelConfig = ModelConfig(),
    compaction: CompactionConfig = CompactionConfig(),
    max_conversation_messages: int = 200,
    recursion_limit: int = 50,
    max_agent_depth: int = 2,
    ask_user_enabled: bool = False,
    role_models: dict[str, ModelConfig] = dict(),
    experimental_interrupt: bool = False,
    procedural_enforce_preconditions: bool = False,
)

Top-level agent configuration combining model and compaction settings.

with_anthropic classmethod

with_anthropic(
    model_name: str = "claude-sonnet-4-5", **kwargs: object
) -> tuple[AgentConfig, AnthropicProvider]

Convenience constructor. Returns (config, provider) tuple.

Source code in src/symfonic/core/config.py
@classmethod
def with_anthropic(
    cls,
    model_name: str = "claude-sonnet-4-5",
    **kwargs: object,
) -> tuple[AgentConfig, AnthropicProvider]:
    """Convenience constructor. Returns (config, provider) tuple."""
    from .providers import AnthropicProvider

    return cls(model=ModelConfig(model_name=model_name, **kwargs)), AnthropicProvider()

CompactionConfig dataclass

CompactionConfig(
    context_window_tokens: int | None = None,
    max_history_share: float = 0.5,
    chars_per_token: int = 4,
    keep_recent_messages: int = 10,
    max_chunk_tokens: int = 30000,
    compaction_model: str = "claude-haiku-4-5",
    soft_threshold_tokens: int = 4000,
    enable_memory_flush: bool = True,
)

Configuration for conversation compaction/summarization.

context_window_tokens is now optional (v7.1.2). When None (the new default), ContextWindowNode falls back to ModelConfig.context_window if set, then to the 200_000 safety net (Claude-sized) -- this gives non-Claude callers a way to pin the right budget without overriding CompactionConfig. Set explicitly (e.g. CompactionConfig(context_window_tokens=50_000)) to force a tighter or wider ceiling than the model declares.

ModelConfig dataclass

ModelConfig(
    model_name: str = "claude-sonnet-4-5",
    temperature: float = 1.0,
    max_tokens: int = 16384,
    max_retries: int = 5,
    top_p: float | None = None,
    top_k: int | None = None,
    thinking: dict[str, object] | None = None,
    extra_headers: dict[str, str] = dict(),
    context_window: int | None = None,
    timeout_seconds: float | None = None,
    http_client: Any | None = None,
)

LLM model configuration. Provider-agnostic.

context_window (added in v7.1.2) declares the model's native context-window budget in tokens. When set, ContextWindowNode uses it as the hard ceiling unless CompactionConfig.context_window_tokens explicitly overrides it. None (default) preserves pre-v7.1.2 behaviour: the node falls through to the 200_000 safety net. Typical values: Claude 3/4 = 200_000, GPT-4 Turbo = 128_000, GPT-4 = 8_192, Llama-3 32k = 32_000.

http_client class-attribute instance-attribute

http_client: Any | None = None

Underlying HTTP client (typically httpx.AsyncClient) for true per-chunk idle bound. Threaded to providers whose LangChain class accepts the kwarg -- http_async_client on OpenAI / DeepSeek / Kimi. Providers whose class does NOT accept it (Anthropic, Google, Ollama at the time of v7.25.0) emit a one-shot :class:HttpClientUnsupportedWarning on first encounter and ignore the field; timeout_seconds still threads. Typed Any so symfonic-core does not import httpx at module load -- adopters who don't pin a client pay no import cost. None (default) preserves the provider's own default client lifecycle.

timeout_seconds class-attribute instance-attribute

timeout_seconds: float | None = None

Total-request timeout in seconds. Threaded to the provider's LangChain-class kwarg whose name varies by provider: default_request_timeout on Anthropic, timeout on OpenAI / DeepSeek / Kimi / Google. Coarse-grained; bounds the entire HTTP round-trip but does NOT enforce per-chunk idle. None (default) leaves the provider's own default in place (typically 600 for the OpenAI family; httpx-driven for Anthropic).

top_k class-attribute instance-attribute

top_k: int | None = None

Top-k sampling: sample only from the k most likely tokens. Honoured by Anthropic, Google, and Ollama (their LangChain classes expose top_k). The OpenAI chat API has NO top_k parameter, so OpenAI / DeepSeek / Kimi ignore it and emit a one-shot warning. None leaves the provider default.

top_p class-attribute instance-attribute

top_p: float | None = None

Nucleus sampling: keep the smallest token set whose cumulative probability >= top_p (0-1). Threaded to every provider's LangChain class (all accept top_p). None leaves the provider default. Note: most providers advise tuning EITHER temperature OR top_p, not both.

resolve_context_window_tokens

resolve_context_window_tokens(
    compaction: CompactionConfig,
    model: ModelConfig | None = None,
) -> int

Return the effective context-window budget in tokens.

Fallback chain
  1. compaction.context_window_tokens if explicitly set (not None).
  2. model.context_window if a ModelConfig is provided and the field is set.
  3. _DEFAULT_CONTEXT_WINDOW_SAFETY_NET (200_000) -- Claude-sized.

The result is always a positive int. v7.1.2 introduced this helper as part of the 2026-05-18 compaction audit follow-up: previously CompactionConfig.context_window_tokens was hardcoded to 200_000, which silently mispriced non-Claude models.

Source code in src/symfonic/core/config.py
def resolve_context_window_tokens(
    compaction: CompactionConfig,
    model: ModelConfig | None = None,
) -> int:
    """Return the effective context-window budget in tokens.

    Fallback chain:
        1. ``compaction.context_window_tokens`` if explicitly set (not None).
        2. ``model.context_window`` if a ``ModelConfig`` is provided and the
           field is set.
        3. ``_DEFAULT_CONTEXT_WINDOW_SAFETY_NET`` (200_000) -- Claude-sized.

    The result is always a positive int. v7.1.2 introduced this helper as
    part of the 2026-05-18 compaction audit follow-up: previously
    ``CompactionConfig.context_window_tokens`` was hardcoded to 200_000,
    which silently mispriced non-Claude models.
    """
    if compaction.context_window_tokens is not None:
        return compaction.context_window_tokens
    if model is not None and model.context_window is not None:
        return model.context_window
    return _DEFAULT_CONTEXT_WINDOW_SAFETY_NET