diff --git a/hermes_cli/model_setup_flows_common.py b/hermes_cli/model_setup_flows_common.py index 969b561585..b986ba7067 100644 --- a/hermes_cli/model_setup_flows_common.py +++ b/hermes_cli/model_setup_flows_common.py @@ -229,21 +229,24 @@ def _oauth_gate(logged_in: bool, name: str, login_fn, *login_args, fresh_name: s def _models_dev_merged(provider_id: str, curated) -> list: - """models.dev agentic models for *provider_id* plus curated ids not yet listed - (case-insensitive). Empty list when models.dev has nothing / is unavailable.""" + """Curated ids first (our agentic picks lead, and models.dev lag can't hide new ones), then + models.dev agentic models not yet listed (case-insensitive; models.dev's spelling wins since + it comes from the provider). Live-first providers keep + models.dev order ahead of curated, matching ``_profile_live_catalog``. Empty list when + models.dev has nothing / is unavailable.""" + from hermes_cli.models_catalog_static import _LIVE_FIRST_PICKER_PROVIDERS mdev_models: list = [] with contextlib.suppress(Exception): from agent.models_dev import list_agentic_models mdev_models = list_agentic_models(provider_id) if not mdev_models: return [] - seen = {m.lower() for m in mdev_models} - merged = list(mdev_models) - for m in curated: - if m.lower() not in seen: - merged.append(m) - seen.add(m.lower()) - return merged + primary, secondary = (mdev_models, curated) if provider_id in _LIVE_FIRST_PICKER_PROVIDERS else (curated, mdev_models) + canonical = {m.lower(): m for m in mdev_models} + merged: dict = {} + for m in [*primary, *secondary]: + merged.setdefault(m.lower(), canonical.get(m.lower(), m)) + return list(merged.values()) def _show_curated(model_list) -> None: diff --git a/tests/hermes_cli/test_models_dev_preferred_merge.py b/tests/hermes_cli/test_models_dev_preferred_merge.py index f4f320c37b..96660215d1 100644 --- a/tests/hermes_cli/test_models_dev_preferred_merge.py +++ b/tests/hermes_cli/test_models_dev_preferred_merge.py @@ -139,3 +139,10 @@ class TestProviderModelIdsPreferred: _model_flow_kimi({}, current_model="") assert captured["models"] == _PROVIDER_MODELS["kimi-coding"] + + +def test_setup_menu_merge_leads_with_curated_in_full(): + from hermes_cli.model_setup_flows_common import _models_dev_merged + with patch("agent.models_dev.list_agentic_models", return_value=["a/x", "b/Y", "c/z"]): + assert _models_dev_merged("nvidia", ["B/y", "d/w"]) == ["b/Y", "d/w", "a/x", "c/z"] + assert _models_dev_merged("opencode-zen", ["B/y", "d/w"]) == ["a/x", "b/Y", "c/z", "d/w"]