symfonic.agent.config¶
config ¶
FrameworkConfig -- unified configuration for the symfonic agent framework.
Composes core AgentConfig and memory OrchestratorConfig with framework-specific behaviour flags.
FrameworkConfig ¶
Bases: BaseModel
Immutable configuration composing core and memory configs.
Framework-specific flags control automatic hydration, consolidation, lazy tool routing, and streaming.
v8.7.1: unknown kwargs are ignored but logged once (WARNING). The
class previously set only frozen=True while Pydantic's extra
default is "ignore", so a typo'd or renamed flag on this 100+-flag
config was dropped silently. Rather than hard-fail (extra="forbid"
would break adopters passing stale flags on upgrade), unknown fields are
still dropped -- preserving the original wire behaviour -- but each is
logged once via _warn_unknown_fields so the typo is visible.
Retired or replaced on the 11.0 line (TA8.26, TA8.42, TA8.39, TA8.43,
TA8.38). Thirty-eight
fields below still exist, still validate, and still hold the value you give
them -- and run(), stream() and stream_typed() refuse by
name before dispatch when one of them is actually used. The refusal is
RetiredConfigurationError, it cites the line, it names the contractual
group, and it says what replaced the field or that nothing did. The fields
are kept here rather than deleted so the failure is that named error rather
than the silent drop _warn_unknown_fields performs for an unknown key.
Setting one at its default, or at a value the contract calls inactive
(nightly_nap_cron="", quick_nap_interval=0,
metacognition_sensitive_tags=[]), is not use and does not refuse. The
twelve groups and the forty-three names are declared once, as data, in
:mod:symfonic.agent.cutover.retired_settings -- except the five nested
ones, which are declared in
:mod:symfonic.agent.cutover.orchestrator_policy because that table's
predicate reads flat attributes by design:
phase-12-extraction--extraction_template_path,phase_12_action_type_from_tool_calls,phase_12_llm_max_drafts_per_run,phase_12_llm_max_episodes_per_run,phase_12_llm_model,phase_12_promote_assistant_content,phase_12_use_llm_extractorconsolidation-promotion--promotion_max_drafts_per_run,promotion_min_pattern_count,promotion_recency_days,synthetic_link_min_co_countnap-scheduling--nightly_nap_cron,nightly_nap_enabled,quick_nap_intervalmetacognition-tuning--metacognition_confidence_threshold,metacognition_gate,metacognition_selective_gate,metacognition_sensitive_tags,metacognition_trivial_ack_patternsprompt-mechanics--activation_log_mode,prompt_block_self_editengine-internals--dev_sqlite_checkpoint_path,scorer_on_hot_path,state_classalready-unconditional--experimental_tool_decoratorhuman-pause-policy--ask_user_pause_ttl_seconds,ask_user_tool_descriptionstool-force-lever--procedural_force_first_action_toolgovernance-policy--credential_patterns,fabrication_refuse_min_confidenceprompt-assembly-policy--hms_budget_mode,hms_system_prompt_token_budget,jit_manifest_token_budget,manifest_cache_position,memory_extract_cadence,messages_cache_policy,on_hms_budget_exceeded,system_prefix_cache_ttlmemory-orchestrator-policy--orchestrator.compaction_threshold,orchestrator.enabled_layers,orchestrator.llm_model,orchestrator.llm_temperature,orchestrator.scope_blend
memory-orchestrator-policy is the fifth replacement group and the
only one whose every member is nested, so its five rows are declared in
:mod:symfonic.agent.cutover.orchestrator_policy rather than in the flat
table above -- same class, same group, same line. It is also the only group
whose members were already doing nothing where you wrote them, and that
is the news to read first. orchestrator.enabled_layers and
orchestrator.scope_blend are overwritten from the top-level
enabled_layers and scope_blend_mode before the orchestrator is
built, so setting either was a silent no-op; write the top-level field,
which is the one that already won. compaction_threshold, llm_model
and llm_temperature say "deprecated -- never read at runtime" in their
own field descriptions: schedule SleepConsolidator phases for the
first, and use agent.model and agent.model.temperature for the
other two -- per-memory-layer model routing does not arrive with the move,
because it never existed. The container's other six fields are untouched:
default_top_k and scoring_weights are honoured on the migrated
route when your memory bundle travels, and context_budget,
write_policy and working_graph_retention name a write boundary this
line does not cross yet and keep working where they always did.
prompt-assembly-policy is the fourth replacement group and the
largest: eight spellings of prompt layout -- a token ceiling, a cache
marker's offset, a directive's cadence -- whose outcomes continue because
this line compiles the prompt rather than splicing it.
PromptingCapability orders contributions on the L0/L1/L2
volatility ladder, admit_within_budget drops the most volatile first
under a PromptBudget your composition root binds through
fold_retrieval_bundle(context_window=..., prompt_share=...), and
plan_regions derives the cache breakpoints from the layers. Three
breaks are worth reading before you migrate: the budget covers the whole
compiled prompt rather than the HMS section alone, so the same number binds
tighter; there is no warn/strict pair, because the compiler drops
before it raises and raises only when pinned content alone will not fit;
and on_hms_budget_exceeded becomes something you read
(CompiledPrompt.budget, CompiledPrompt.diagnostics) rather than a
hook that fires. Two C1-G fields are deliberately not in this group:
domain_description_max_chars, which the migrated path reads and which is
therefore honoured rather than refused, and
procedural_render_preflight_in_l1, which switches on a synthesiser this
line has no replacement for and which keeps working where it always did.
governance-policy is the third replacement group and the one whose
members are safety controls: both outcomes continue on this line, composed
on the governance capability your root wires rather than spelled here.
GovernanceCapability.compose(patterns=...) carries the credential list
into CredentialHygieneStage, which scrubs more than the legacy scrubber
did -- tool-call arguments as well as properties, and a whole tool result
string when the pattern matches inside it -- and records an explicit verdict
when a deployment disables it.
GovernanceCapability.compose(refuse_min_confidence=..., min_confidence=...)
carries the fabrication grade into FabricationStage, where one number
becomes two and the refusal default moves from 0.8 to 0.9: write
refuse_min_confidence=0.8 to keep today's behaviour, and raise
min_confidence if you relied on sub-threshold findings staying advisory,
because they now steer a revision. The per-call is_admin= keyword is
replaced in the same lane and is not a field: it refuses through
:mod:symfonic.agent.cutover.authority, where the whole contract and its
adopter-visible breaks are published.
tool-force-lever is also a replacement, and the narrowest one: the
procedural force lever continues under
procedural_force_tool_choice: Literal["off", "soft", "hard"], which has
shipped in this same model since v7.20.0 and which
resolve_force_tool_choice() already prefers. Write
procedural_force_tool_choice="soft" where you wrote
procedural_force_first_action_tool=True -- that is the mapping the
resolver already applies, so the mode you get is unchanged -- or "hard"
for the strict-fail-on-refusal semantics the bool could never reach.
human-pause-policy is the other group that is a replacement rather
than a retirement: the pause capability continues on this line, configured
on the HumanInteractionCapability your composition root wires -- its
lifetime by TTLPolicy, its presentation by InteractionToolSpec --
and only these spellings stop. The nested third spelling of the same
lifetime, agent.ask_user_pause_ttl_seconds, refuses in the same group
with the same class through
:mod:symfonic.agent.cutover.pause_policy, which is also where the whole
contract and its adopter-visible breaks are published.
A 9.12 or 10.4 install runs its own code, which still honours all thirty-eight; the compatibility window is kept by not backporting the retirement module, never by a version branch inside this line.
task_scheduler
property
¶
Return the configured scheduler or a NullScheduler fallback.
child
classmethod
¶
child(parent: FrameworkConfig, *, name: str | None = None, description: str | None = None, model_name: str | None = None, temperature: float | None = None, max_tokens: int | None = None, **overrides: Any) -> FrameworkConfig
Derive a sub-agent config from parent, overriding only what's named.
v9.1.0 (issue #28). The inheritance primitive the declarative
SubAgentSpec (#27) builds on. A child agent inherits every
behaviour flag from parent (auto_hydrate, auto_consolidate,
lazy_tooling, enable_hms_prompt, the whole agent/orchestrator
config, ...) so sub-agents don't drift from the parent's settings;
only the arguments you pass are overridden.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent
|
FrameworkConfig
|
The config to inherit from. |
required |
name
|
str | None
|
Sets the child |
None
|
description
|
str | None
|
Sets the child |
None
|
model_name
|
str | None
|
Override the child |
None
|
temperature
|
float | None
|
Override the child |
None
|
max_tokens
|
int | None
|
Override the child |
None
|
**overrides
|
Any
|
Any other top-level |
{}
|
Returns:
| Type | Description |
|---|---|
FrameworkConfig
|
A new frozen |
Source code in src/symfonic/agent/config.py
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 | |
resolve_force_tool_choice ¶
v7.20.0 T-7.20.0.8: single source of truth for the effective force-lever mode the engine should use.
Precedence (deliberate):
procedural_force_tool_choiceset explicitly to non-default ("soft"or"hard") wins — that's the precise field.- Legacy
procedural_force_first_action_tool=Truewith the enum at its default"off"resolves to"soft"— preserves v7.19.5 runtime semantics (clean abstain on provider refusal). - Both defaults →
"off"(no forcing).
The engine's force-resolver site at
engine.py::_maybe_resolve_forced_tool_choice MUST consult
this method rather than reading either field directly so the
precedence rule lives in exactly one place.