fix(webui): activate existing model presets safely

This commit is contained in:
Xubin Ren
2026-09-02 16:25:07 +08:00
parent 7ba4bc02f0
commit da96c5c6eb
3 changed files with 43 additions and 8 deletions
+5 -1
View File
@@ -269,7 +269,11 @@ def update_model_call_order(
config_path: Path | None = None,
) -> dict[str, Any]:
config = _load_settings_config(config_path)
if models.update_model_call_order(config, query):
if models.update_model_call_order(
config,
query,
oauth_status=_oauth_provider_status,
):
_save_settings_config(config, config_path)
return settings_payload(config_path=config_path)
+11 -5
View File
@@ -1312,7 +1312,12 @@ def update_model_configuration(
return changed
def update_model_call_order(config: Config, query: QueryParams) -> bool:
def update_model_call_order(
config: Config,
query: QueryParams,
*,
oauth_status: OAuthStatusReader,
) -> bool:
raw_order = query_first_alias(query, "order", "presetNames")
if raw_order is None:
raise WebUISettingsError("model call order is required")
@@ -1331,15 +1336,16 @@ def update_model_call_order(config: Config, query: QueryParams) -> bool:
raise WebUISettingsError("model call order must contain at least one preset")
normalized_order = [cast(str, name).strip() for name in cast(list[object], order)]
unknown = [name for name in normalized_order if name not in config.model_presets]
if unknown:
raise WebUISettingsError(f"unknown model preset: {unknown[0]}")
_, editable = _model_call_order_state(config)
if not editable:
if not editable and _legacy_model_configuration_migratable(config, oauth_status):
raise WebUISettingsError(
"convert the existing model configuration to presets first",
status=409,
)
unknown = [name for name in normalized_order if name not in config.model_presets]
if unknown:
raise WebUISettingsError(f"unknown model preset: {unknown[0]}")
defaults = config.agents.defaults
fallback_models: list[FallbackCandidate] = list(normalized_order[1:])
+27 -2
View File
@@ -592,7 +592,7 @@ def test_update_model_call_order_sets_primary_and_fallbacks(
assert saved.agents.defaults.fallback_models == ["primary"]
def test_update_model_call_order_requires_named_primary(
def test_update_model_call_order_activates_existing_named_preset(
tmp_path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
@@ -602,11 +602,36 @@ def test_update_model_call_order_requires_named_primary(
save_config(config, config_path)
monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path)
payload = update_model_call_order({"order": [json.dumps(["backup"])]})
assert payload["model_call_order"] == ["backup"]
assert payload["model_call_order_editable"] is True
assert load_config(config_path).agents.defaults.model_preset == "backup"
def test_update_model_call_order_preserves_real_legacy_configuration(
tmp_path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
config_path = tmp_path / "config.json"
config = Config()
config.providers.openai.api_key = "sk-test"
config.agents.defaults.model = "openai/gpt-4o"
config.agents.defaults.provider = "openai"
config.model_presets["backup"] = ModelPresetConfig(
model="openai/gpt-4.1-mini",
provider="openai",
)
save_config(config, config_path)
monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path)
with pytest.raises(WebUISettingsError) as error:
update_model_call_order({"order": [json.dumps(["backup"])]})
assert error.value.status == 409
assert load_config(config_path).agents.defaults.model_preset is None
saved = load_config(config_path)
assert saved.agents.defaults.model_preset is None
assert saved.agents.defaults.model == "openai/gpt-4o"
def test_migrate_model_configurations_preserves_legacy_chain(