refactor(models): unify preset names

This commit is contained in:
Xubin Ren
2026-08-16 11:50:56 +08:00
parent 8dc08853e4
commit 3dc38f6396
37 changed files with 239 additions and 209 deletions
@@ -361,3 +361,15 @@ def test_resolver_mutates_only_its_default_selection() -> None:
assert resolver.model_preset is None
assert initial.model == "base-model"
assert initial.context_window_tokens == 10_000
def test_resolver_preserves_canonical_preset_name_for_case_insensitive_input() -> None:
resolver = ModelRuntimeResolver(
_runtime(),
model_presets={"Deep Research": ModelPresetConfig(model="deep-model")},
)
selected = resolver.select_preset("deep research")
assert selected.model == "deep-model"
assert selected.model_preset == "Deep Research"
+20 -2
View File
@@ -33,14 +33,14 @@ def _provider(default_model: str, max_tokens: int = 123) -> MagicMock:
return provider
def _make_loop(tmp_path, *, preset_snapshot_loader=None) -> AgentLoop:
def _make_loop(tmp_path, *, preset_snapshot_loader=None, model_presets=None) -> AgentLoop:
return AgentLoop(
bus=MessageBus(),
provider=_provider("base-model", max_tokens=123),
workspace=tmp_path,
model="base-model",
context_window_tokens=1000,
model_presets={
model_presets=model_presets or {
"default": ModelPresetConfig(
model="base-model",
max_tokens=123,
@@ -106,6 +106,24 @@ async def test_model_command_switches_preset(tmp_path) -> None:
assert status is not None and "openai/gpt-4.1" in status.content
@pytest.mark.asyncio
async def test_model_command_accepts_canonical_names_with_spaces(tmp_path) -> None:
loop = _make_loop(
tmp_path,
model_presets={
"default": ModelPresetConfig(model="base-model"),
"Deep Research": ModelPresetConfig(model="deep-model"),
},
)
out = await cmd_model(
_ctx(loop, "/model deep research", args="deep research"),
)
assert "Switched model preset to `Deep Research`." in out.content
assert _saved_model_preset(loop) == "Deep Research"
@pytest.mark.asyncio
async def test_model_command_switches_back_to_default(tmp_path) -> None:
loop = _make_loop(tmp_path)
+24
View File
@@ -275,6 +275,30 @@ def test_model_presets_accepts_camel_case_root_key() -> None:
assert config.model_presets["fast"].provider == "openai"
def test_legacy_model_preset_label_is_ignored() -> None:
config = Config.model_validate({
"modelPresets": {
"gpt-5-6-sol": {
"label": "Codex",
"model": "openai-codex/gpt-5.6-luna",
}
}
})
assert set(config.model_presets) == {"gpt-5-6-sol"}
assert "label" not in config.model_presets["gpt-5-6-sol"].model_dump()
def test_model_preset_names_are_unique_ignoring_case() -> None:
with pytest.raises(ValueError, match="unique ignoring case"):
Config.model_validate({
"modelPresets": {
"Fast": {"model": "openai/gpt-4.1-mini"},
"fast": {"model": "openai/gpt-4.1"},
}
})
def test_model_presets_serializes_with_camel_case_root_key() -> None:
config = Config.model_validate({
"model_presets": {
+36 -6
View File
@@ -236,7 +236,7 @@ def _dynamic_provider_config(
return Config.model_validate(raw_config)
def test_create_model_configuration_writes_label_without_changing_call_order(
def test_create_model_configuration_accepts_legacy_label_without_changing_call_order(
tmp_path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
@@ -260,11 +260,10 @@ def test_create_model_configuration_writes_label_without_changing_call_order(
assert payload["agent"]["model"] == "openai/gpt-4o"
assert payload["created_model_preset"] == "fast-writing"
rows = {row["name"]: row for row in payload["model_presets"]}
assert rows["fast-writing"]["label"] == "Fast writing"
assert rows["fast-writing"]["label"] == "fast-writing"
saved = load_config(config_path)
assert saved.agents.defaults.model_preset is None
assert saved.model_presets["fast-writing"].label == "Fast writing"
assert saved.model_presets["fast-writing"].model == "openai/gpt-4.1-mini"
assert saved.model_presets["fast-writing"].provider == "openai"
@@ -279,6 +278,40 @@ def test_create_model_configuration_writes_label_without_changing_call_order(
assert duplicate.value.status == 409
def test_create_model_configuration_preserves_canonical_name(
tmp_path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
config_path = tmp_path / "config.json"
config = Config()
config.providers.openai.api_key = "sk-test"
save_config(config, config_path)
monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path)
payload = create_model_configuration(
{
"name": ["Fast Writing"],
"provider": ["openai"],
"model": ["openai/gpt-4.1-mini"],
}
)
assert payload["created_model_preset"] == "Fast Writing"
rows = {row["name"]: row for row in payload["model_presets"]}
assert rows["Fast Writing"]["label"] == "Fast Writing"
assert "Fast Writing" in load_config(config_path).model_presets
with pytest.raises(WebUISettingsError) as duplicate:
create_model_configuration(
{
"name": ["fast writing"],
"provider": ["openai"],
"model": ["openai/gpt-4.1-mini"],
}
)
assert duplicate.value.status == 409
def test_create_model_configuration_accepts_dynamic_custom_provider(
tmp_path,
monkeypatch: pytest.MonkeyPatch,
@@ -353,7 +386,6 @@ def test_update_model_configuration_edits_named_preset_without_selecting(
config = Config()
config.providers.openai.api_key = "sk-test"
config.model_presets["codex"] = ModelPresetConfig(
label="Old Codex",
provider="openai",
model="openai/gpt-4.1",
)
@@ -382,7 +414,6 @@ def test_update_model_configuration_edits_named_preset_without_selecting(
assert payload["agent"]["model"] == "anthropic/claude-opus-4-5"
saved = load_config(config_path)
assert saved.agents.defaults.model_preset is None
assert saved.model_presets["codex"].label == "Codex"
assert saved.model_presets["codex"].provider == "openai_codex"
assert saved.model_presets["codex"].model == "openai-codex/gpt-5.5"
@@ -834,7 +865,6 @@ def test_update_model_configuration_preserves_custom_context_windows(
config_path = tmp_path / "config.json"
config = Config()
config.model_presets["codex"] = ModelPresetConfig(
label="Codex",
provider="openai",
model="openai/gpt-4.1",
)