mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-20 00:22:31 +00:00
fix(config): reserve implicit default model preset
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
1d14c2ba40
commit
c9b84c7b11
@ -437,8 +437,7 @@ class AgentLoop:
|
|||||||
context_window_tokens = extra.pop("context_window_tokens", None) or resolved.context_window_tokens
|
context_window_tokens = extra.pop("context_window_tokens", None) or resolved.context_window_tokens
|
||||||
model_preset_snapshot_builder = extra.pop("model_preset_snapshot_builder", None)
|
model_preset_snapshot_builder = extra.pop("model_preset_snapshot_builder", None)
|
||||||
model_presets = dict(config.model_presets)
|
model_presets = dict(config.model_presets)
|
||||||
if "default" not in model_presets:
|
model_presets["default"] = config.resolve_default_preset()
|
||||||
model_presets["default"] = resolved
|
|
||||||
return cls(
|
return cls(
|
||||||
bus=bus,
|
bus=bus,
|
||||||
provider=provider,
|
provider=provider,
|
||||||
|
|||||||
@ -282,17 +282,12 @@ class Config(BaseSettings):
|
|||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def _validate_model_preset(self) -> "Config":
|
def _validate_model_preset(self) -> "Config":
|
||||||
name = self.agents.defaults.model_preset
|
name = self.agents.defaults.model_preset
|
||||||
if name and name not in self.model_presets:
|
if name and name != "default" and name not in self.model_presets:
|
||||||
raise ValueError(f"model_preset {name!r} not found in model_presets")
|
raise ValueError(f"model_preset {name!r} not found in model_presets")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def resolve_preset(self, name: str | None = None) -> ModelPresetConfig:
|
def resolve_default_preset(self) -> ModelPresetConfig:
|
||||||
"""Return effective model params: from active preset, or individual defaults."""
|
"""Return the implicit `default` preset from agents.defaults fields."""
|
||||||
name = self.agents.defaults.model_preset if name is None else name
|
|
||||||
if name:
|
|
||||||
if name not in self.model_presets:
|
|
||||||
raise KeyError(f"model_preset {name!r} not found in model_presets")
|
|
||||||
return self.model_presets[name]
|
|
||||||
d = self.agents.defaults
|
d = self.agents.defaults
|
||||||
return ModelPresetConfig(
|
return ModelPresetConfig(
|
||||||
model=d.model, provider=d.provider, max_tokens=d.max_tokens,
|
model=d.model, provider=d.provider, max_tokens=d.max_tokens,
|
||||||
@ -300,6 +295,15 @@ class Config(BaseSettings):
|
|||||||
temperature=d.temperature, reasoning_effort=d.reasoning_effort,
|
temperature=d.temperature, reasoning_effort=d.reasoning_effort,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def resolve_preset(self, name: str | None = None) -> ModelPresetConfig:
|
||||||
|
"""Return effective model params from a named preset or the implicit default."""
|
||||||
|
name = self.agents.defaults.model_preset if name is None else name
|
||||||
|
if not name or name == "default":
|
||||||
|
return self.resolve_default_preset()
|
||||||
|
if name not in self.model_presets:
|
||||||
|
raise KeyError(f"model_preset {name!r} not found in model_presets")
|
||||||
|
return self.model_presets[name]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def workspace_path(self) -> Path:
|
def workspace_path(self) -> Path:
|
||||||
"""Get expanded workspace path."""
|
"""Get expanded workspace path."""
|
||||||
|
|||||||
@ -284,7 +284,7 @@ def test_from_config_injects_default_preset(tmp_path) -> None:
|
|||||||
assert loop.model_presets["default"].model == "openai/gpt-4.1"
|
assert loop.model_presets["default"].model == "openai/gpt-4.1"
|
||||||
|
|
||||||
|
|
||||||
def test_from_config_preserves_existing_default_preset(tmp_path) -> None:
|
def test_from_config_reserves_default_for_agent_defaults(tmp_path) -> None:
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from nanobot.config.schema import Config
|
from nanobot.config.schema import Config
|
||||||
@ -297,4 +297,4 @@ def test_from_config_preserves_existing_default_preset(tmp_path) -> None:
|
|||||||
fake_provider = _provider("openai/gpt-4.1")
|
fake_provider = _provider("openai/gpt-4.1")
|
||||||
with patch("nanobot.providers.factory.make_provider", return_value=fake_provider):
|
with patch("nanobot.providers.factory.make_provider", return_value=fake_provider):
|
||||||
loop = AgentLoop.from_config(config)
|
loop = AgentLoop.from_config(config)
|
||||||
assert loop.model_presets["default"].model == "custom-model"
|
assert loop.model_presets["default"].model == "openai/gpt-4.1"
|
||||||
|
|||||||
@ -39,6 +39,24 @@ def test_resolve_preset_returns_active_preset() -> None:
|
|||||||
assert resolved.reasoning_effort == "low"
|
assert resolved.reasoning_effort == "low"
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_preset_is_agents_defaults_even_when_named_preset_is_active() -> None:
|
||||||
|
config = Config.model_validate({
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"model": "openai/gpt-4.1",
|
||||||
|
"provider": "openai",
|
||||||
|
"modelPreset": "fast",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"modelPresets": {
|
||||||
|
"fast": {"model": "openai/gpt-4.1-mini", "provider": "openai"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert config.resolve_preset().model == "openai/gpt-4.1-mini"
|
||||||
|
assert config.resolve_preset("default").model == "openai/gpt-4.1"
|
||||||
|
|
||||||
|
|
||||||
def test_model_presets_accepts_camel_case_root_key() -> None:
|
def test_model_presets_accepts_camel_case_root_key() -> None:
|
||||||
config = Config.model_validate({
|
config = Config.model_validate({
|
||||||
"modelPresets": {
|
"modelPresets": {
|
||||||
@ -79,6 +97,19 @@ def test_validator_rejects_unknown_preset() -> None:
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def test_model_preset_accepts_explicit_default_name() -> None:
|
||||||
|
config = Config.model_validate({
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"model": "openai/gpt-4.1",
|
||||||
|
"modelPreset": "default",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert config.resolve_preset().model == "openai/gpt-4.1"
|
||||||
|
|
||||||
|
|
||||||
def test_resolve_preset_rejects_unknown_named_preset() -> None:
|
def test_resolve_preset_rejects_unknown_named_preset() -> None:
|
||||||
import pytest
|
import pytest
|
||||||
with pytest.raises(KeyError, match="model_preset 'missing' not found"):
|
with pytest.raises(KeyError, match="model_preset 'missing' not found"):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user