From 8eac52054bf33f4362552ace09dcca5b4e1b47dc Mon Sep 17 00:00:00 2001 From: giggling-ginger <110955495+giggling-ginger@users.noreply.github.com> Date: Wed, 8 Jul 2026 07:34:00 +0000 Subject: [PATCH] fix(desktop): keep configured MoA presets in model picker --- hermes_cli/inventory.py | 50 +++++++++++++++++++++++++++++- tests/hermes_cli/test_inventory.py | 38 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/hermes_cli/inventory.py b/hermes_cli/inventory.py index 08cfe322b87..d283f2a1013 100644 --- a/hermes_cli/inventory.py +++ b/hermes_cli/inventory.py @@ -340,13 +340,61 @@ def _filter_explicit_provider_rows(rows: list[dict], ctx: ConfigContext) -> list if slug == "moa": # MoA is a virtual routing mode, not an independently configured # provider. Hide it from explicit-only pickers unless it is the - # current provider (handled above). + # current provider (handled above) or the user explicitly wrote an + # enabled MoA preset into config.yaml. Use raw config so the + # DEFAULT_CONFIG preset does not make every desktop picker show MoA. + if _raw_config_has_enabled_moa_preset(): + kept.append(row) continue if is_provider_explicitly_configured(slug): kept.append(row) return kept +def _raw_config_has_enabled_moa_preset() -> bool: + """Return True when the user's raw config explicitly enables MoA. + + ``load_config()`` includes ``DEFAULT_CONFIG["moa"].presets.default`` for + everyone. Explicit-only model pickers must not treat that default as a user + choice, but they should keep MoA visible once the user has saved at least + one enabled preset (or an older flat MoA config) in their own config.yaml. + """ + try: + from hermes_cli.config import read_raw_config + + raw = read_raw_config() + except Exception: + return False + + if not isinstance(raw, dict): + return False + moa = raw.get("moa") + if not isinstance(moa, dict): + return False + + presets = moa.get("presets") + if isinstance(presets, dict): + for name, preset in presets.items(): + if not str(name or "").strip(): + continue + if not isinstance(preset, dict): + return True + if preset.get("enabled", True): + return True + return False + + legacy_keys = { + "reference_models", + "aggregator", + "reference_temperature", + "aggregator_temperature", + "max_tokens", + "reference_max_tokens", + "fanout", + } + return any(key in moa for key in legacy_keys) and bool(moa.get("enabled", True)) + + def _apply_picker_hints(rows: list[dict]) -> None: """Add ``authenticated``/``auth_type``/``key_env``/``warning`` per row. diff --git a/tests/hermes_cli/test_inventory.py b/tests/hermes_cli/test_inventory.py index 34c61aa1307..4167fd92b83 100644 --- a/tests/hermes_cli/test_inventory.py +++ b/tests/hermes_cli/test_inventory.py @@ -402,6 +402,7 @@ def test_explicit_only_filters_ambient_credentials_but_keeps_current_and_custom_ ctx = _empty_ctx(provider="openai-codex", model="gpt-5.4") with ( _list_auth_returning(rows), + patch("hermes_cli.config.read_raw_config", return_value={}), patch( "hermes_cli.auth.is_provider_explicitly_configured", side_effect=lambda slug: slug == "gemini", @@ -416,6 +417,43 @@ def test_explicit_only_filters_ambient_credentials_but_keeps_current_and_custom_ ] +def test_explicit_only_keeps_moa_when_raw_config_has_enabled_preset(): + rows = [ + {"slug": "moa", "name": "MoA", "models": ["review"], + "total_models": 1, "is_current": False, "is_user_defined": False, + "source": "virtual"}, + ] + ctx = _empty_ctx(provider="openrouter", model="anthropic/claude-opus-4.8") + raw_config = { + "moa": { + "active_preset": "review", + "presets": { + "review": { + "enabled": True, + "reference_models": [ + {"provider": "openai-codex", "model": "gpt-5.5"}, + ], + "aggregator": { + "provider": "openrouter", + "model": "anthropic/claude-opus-4.8", + }, + }, + }, + }, + } + + with ( + _list_auth_returning(rows), + patch("hermes_cli.config.load_config", return_value=raw_config), + patch("hermes_cli.config.read_raw_config", return_value=raw_config), + patch("hermes_cli.auth.is_provider_explicitly_configured", return_value=False), + ): + payload = build_models_payload(ctx, explicit_only=True) + + assert [row["slug"] for row in payload["providers"]] == ["moa"] + assert payload["providers"][0]["models"] == ["review"] + + # ─── picker_hints ──────────────────────────────────────────────────────