From e13f242f0139cbdc14aff418f3bc5a7a5fd506d4 Mon Sep 17 00:00:00 2001 From: stepanov1975 <9785479+stepanov1975@users.noreply.github.com> Date: Fri, 8 May 2026 20:54:55 +0000 Subject: [PATCH] fix(cli): preserve setup config picker writes Resync the setup wizard's in-memory config after the shared model picker writes to disk so the wizard's final save does not overwrite auxiliary choices or other provider updates.\n\nAdds a regression test for auxiliary task choices saved by the picker. --- hermes_cli/setup.py | 11 +++---- tests/hermes_cli/test_setup_model_provider.py | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 50e198b9dc7..1e4b6d7fc7b 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -820,13 +820,12 @@ def setup_model_provider(config: dict, *, quick: bool = False): # Re-sync the wizard's config dict from what cmd_model saved to disk. # This is critical: cmd_model writes to disk via its own load/save cycle, # and the wizard's final save_config(config) must not overwrite those - # changes with stale values (#4172). + # changes with stale values (#4172). Refresh the dict in place so callers + # that keep the same object see every section the shared model picker may + # have changed (model, custom_providers, auxiliary, provider metadata, etc.). _refreshed = load_config() - config["model"] = _refreshed.get("model", config.get("model")) - if "custom_providers" in _refreshed: - config["custom_providers"] = _refreshed["custom_providers"] - else: - config.pop("custom_providers", None) + config.clear() + config.update(_refreshed) # Derive the selected provider for downstream steps (vision setup). selected_provider = None diff --git a/tests/hermes_cli/test_setup_model_provider.py b/tests/hermes_cli/test_setup_model_provider.py index 858c276a355..b79b33315d8 100644 --- a/tests/hermes_cli/test_setup_model_provider.py +++ b/tests/hermes_cli/test_setup_model_provider.py @@ -63,6 +63,38 @@ def _write_model_config(provider, base_url="", model_name="test-model"): save_config(cfg) +def _write_aux_config(task="compression", provider="gemini", model_name="gemini-2.5-flash"): + """Simulate the aux picker writing a task override to disk.""" + cfg = load_config() + aux = cfg.setdefault("auxiliary", {}) + entry = aux.setdefault(task, {}) + entry["provider"] = provider + entry["model"] = model_name + save_config(cfg) + + +def test_setup_model_provider_preserves_auxiliary_choices_written_by_picker(tmp_path, monkeypatch): + """Aux choices made inside hermes setup must survive the wizard's final save.""" + monkeypatch.setenv("HERMES_HOME", str(tmp_path)) + _clear_provider_env(monkeypatch) + + config = load_config() + assert config["auxiliary"]["compression"]["provider"] == "auto" + + def fake_select(): + _write_aux_config("compression", "gemini", "gemini-2.5-flash") + + monkeypatch.setattr("hermes_cli.main.select_provider_and_model", fake_select) + + setup_model_provider(config, quick=True) + save_config(config) # mirrors run_setup_wizard(section="model") final save + + reloaded = load_config() + compression = reloaded["auxiliary"]["compression"] + assert compression["provider"] == "gemini" + assert compression["model"] == "gemini-2.5-flash" + + def test_setup_keep_current_custom_from_config_does_not_fall_through(tmp_path, monkeypatch): """Keep-current custom should not fall through to the generic model menu.""" monkeypatch.setenv("HERMES_HOME", str(tmp_path))