feat(config): add ModelPresetConfig and runtime preset switching

- Add `ModelPresetConfig` schema for named model presets
- Add `model_presets` dict to `Config` and `model_preset` field to `AgentDefaults`
- Add `resolve_preset()` to return effective model params from preset or defaults
- Add `@model_validator` to reject unknown preset names
- Update `_match_provider()` to use resolved preset model/provider
- Update `make_provider()` and `provider_signature()` to use `resolve_preset()`
- Add `model_preset` property to `AgentLoop` for atomic runtime switching
- Update `AgentLoop.from_config()` to inject a runtime `default` preset
- Wire self-tool to inspect/clear preset state
- Update CLI display strings to show active preset
This commit is contained in:
chengyongru
2026-05-12 20:06:22 +08:00
committed by Xubin Ren
parent 1175420339
commit 6f78267c82
8 changed files with 348 additions and 33 deletions
+5
View File
@@ -52,3 +52,8 @@ class RuntimeState(Protocol):
def _last_usage(self) -> Any: ...
def _sync_subagent_runtime_limits(self) -> None: ...
@property
def model_preset(self) -> str | None: ...
_active_preset: str | None
+8 -1
View File
@@ -347,6 +347,7 @@ class MyTool(Tool, ContextAware):
# RESTRICTED keys
for k in self.RESTRICTED:
parts.append(self._format_value(getattr(state, k, None), k))
parts.append(self._format_value(state.model_preset, "model_preset"))
# Other useful top-level keys shown in description
for k in ("workspace", "provider_retry_mode", "max_tool_result_chars", "_current_iteration", "web_config", "exec_config", "subagents"):
if _has_real_attr(state, k):
@@ -411,6 +412,8 @@ class MyTool(Tool, ContextAware):
if "min_len" in spec and len(str(value)) < spec["min_len"]:
return f"Error: '{key}' must be at least {spec['min_len']} characters"
setattr(self._runtime_state, key, value)
if key == "model":
self._runtime_state._active_preset = None
if key == "max_iterations" and hasattr(self._runtime_state, "_sync_subagent_runtime_limits"):
self._runtime_state._sync_subagent_runtime_limits()
self._audit("modify", f"{key}: {old!r} -> {value!r}")
@@ -429,7 +432,11 @@ class MyTool(Tool, ContextAware):
f"REJECTED type mismatch {key}: expects {old_t.__name__}, got {new_t.__name__}",
)
return f"Error: '{key}' expects {old_t.__name__}, got {new_t.__name__}"
setattr(self._runtime_state, key, value)
try:
setattr(self._runtime_state, key, value)
except (ValueError, KeyError) as e:
self._audit("modify", f"REJECTED {key}: {e}")
return f"Error: {e}"
self._audit("modify", f"{key}: {old!r} -> {value!r}")
return f"Set {key} = {value!r} (was {old!r})"
if callable(value):