From 311bacb572441e668a87eacf3de3705f5317b2b8 Mon Sep 17 00:00:00 2001 From: "kyssta-exe 25470058+kyssta-exe@users.noreply.github.com" Date: Mon, 20 Jul 2026 05:21:41 +0000 Subject: [PATCH] fix(model-switch): preserve per-model metadata dict in _save_discovered_models_to_config (#67841) When custom_providers[].models uses the mapping form to store per-model metadata (e.g. context_length), _save_discovered_models_to_config must not replace it with a flat list of strings. Add a guard that skips entries whose models value is a dict, preserving the user's curated metadata. The regression was introduced by PR #65652, which added the auto-save helper without considering the dict form. --- hermes_cli/model_switch.py | 5 +++ .../test_model_switch_custom_providers.py | 37 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index 8af49151d2fb6..eb7b237cf2e5c 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -133,6 +133,11 @@ def _save_discovered_models_to_config( if entry_url.rstrip("/").lower() != norm_url: continue existing = entry.get("models") + # Preserve per-model metadata: when ``models`` is a mapping + # (e.g. ``{"model-a": {"context_length": 8192}}``), the user + # has curated metadata per model — do not replace it. + if isinstance(existing, dict): + continue # Only update when models are stale — avoids unnecessary # config writes on every picker open. if isinstance(existing, list) and existing == model_ids: diff --git a/tests/hermes_cli/test_model_switch_custom_providers.py b/tests/hermes_cli/test_model_switch_custom_providers.py index abd1089623ff8..05f0edf7b4f5e 100644 --- a/tests/hermes_cli/test_model_switch_custom_providers.py +++ b/tests/hermes_cli/test_model_switch_custom_providers.py @@ -1479,3 +1479,40 @@ def test_save_discovered_models_noop_on_empty_args(monkeypatch): _save_discovered_models_to_config("", []) assert load_calls == 0, "load_config must not be called for empty args" + + +def test_save_discovered_models_preserves_dict_form(monkeypatch): + """``_save_discovered_models_to_config`` must not replace a dict-form + ``models`` mapping (per-model metadata like ``context_length``) with + a flat list of strings (#67841).""" + from hermes_cli.model_switch import _save_discovered_models_to_config + + save_calls = [] + + def fake_save(config): + save_calls.append(dict(config)) + + monkeypatch.setattr("hermes_cli.config.save_config", fake_save) + monkeypatch.setattr( + "hermes_cli.config.load_config", + lambda: { + "custom_providers": [ + { + "name": "my-gateway", + "base_url": "https://gateway.example.com/v1", + "models": { + "configured-model": {"context_length": 8192}, + }, + } + ] + }, + ) + + # Dict-form models must NOT be overwritten by discovered models + _save_discovered_models_to_config( + "https://gateway.example.com/v1", + ["configured-model", "discovered-model"], + ) + assert save_calls == [], ( + "Dict-form models must not be replaced with a flat list" + )