From 732ababa1a6fdb2a35dc2c9f8de37db699861ae7 Mon Sep 17 00:00:00 2001 From: helix4u <4317663+helix4u@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:32:17 -0600 Subject: [PATCH] fix(doctor): allow vendor slugs for named custom providers --- hermes_cli/doctor.py | 6 ++- hermes_cli/providers.py | 5 +- tests/hermes_cli/test_doctor.py | 48 +++++++++++++++++++ .../test_model_switch_custom_providers.py | 9 ++++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/hermes_cli/doctor.py b/hermes_cli/doctor.py index 619d00df8ab..d56ff88c9d2 100644 --- a/hermes_cli/doctor.py +++ b/hermes_cli/doctor.py @@ -740,6 +740,7 @@ def run_doctor(args): # Warn if model is set to a provider-prefixed name on a provider that doesn't use them provider_for_policy = runtime_provider or catalog_provider + provider_policy_id = str(provider_for_policy or "").strip().lower() providers_accepting_vendor_slugs = { "openrouter", "custom", @@ -753,8 +754,9 @@ def run_doctor(args): if ( default_model and "/" in default_model - and provider_for_policy - and provider_for_policy not in providers_accepting_vendor_slugs + and provider_policy_id + and provider_policy_id not in providers_accepting_vendor_slugs + and not provider_policy_id.startswith("custom:") ): check_warn( f"model.default '{default_model}' uses a vendor/model slug but provider is '{provider_raw}'", diff --git a/hermes_cli/providers.py b/hermes_cli/providers.py index ba25f7e6315..efc3a8576ed 100644 --- a/hermes_cli/providers.py +++ b/hermes_cli/providers.py @@ -492,7 +492,10 @@ def get_label(provider_id: str) -> str: def is_aggregator(provider: str) -> bool: """Return True when the provider is a multi-model aggregator.""" - pdef = get_provider(provider) + provider_norm = normalize_provider(provider or "") + if provider_norm.startswith("custom:"): + return True + pdef = get_provider(provider_norm) return pdef.is_aggregator if pdef else False diff --git a/tests/hermes_cli/test_doctor.py b/tests/hermes_cli/test_doctor.py index 9d832fa95e6..c9df041d064 100644 --- a/tests/hermes_cli/test_doctor.py +++ b/tests/hermes_cli/test_doctor.py @@ -540,6 +540,54 @@ def test_run_doctor_accepts_hermes_provider_ids_that_catalog_aliases( ) +def test_run_doctor_accepts_vendor_slugs_for_named_custom_provider(monkeypatch, tmp_path): + home = tmp_path / ".hermes" + home.mkdir(parents=True, exist_ok=True) + (home / "config.yaml").write_text( + "model:\n" + " provider: custom:hpc-ai\n" + " default: deepseek/deepseek-v4-flash\n" + "custom_providers:\n" + " - name: hpc-ai\n" + " base_url: https://hpc-ai.example/v1\n" + " api_key: test-key\n", + encoding="utf-8", + ) + + monkeypatch.setattr(doctor_mod, "HERMES_HOME", home) + monkeypatch.setattr(doctor_mod, "PROJECT_ROOT", tmp_path / "project") + monkeypatch.setattr(doctor_mod, "_DHH", str(home)) + (tmp_path / "project").mkdir(exist_ok=True) + + fake_model_tools = types.SimpleNamespace( + check_tool_availability=lambda *a, **kw: ([], []), + TOOLSET_REQUIREMENTS={}, + ) + monkeypatch.setitem(sys.modules, "model_tools", fake_model_tools) + + try: + from hermes_cli import auth as _auth_mod + monkeypatch.setattr(_auth_mod, "get_nous_auth_status", lambda: {}) + monkeypatch.setattr(_auth_mod, "get_codex_auth_status", lambda: {}) + monkeypatch.setattr(_auth_mod, "get_xai_oauth_auth_status", lambda: {}) + except Exception: + pass + + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + doctor_mod.run_doctor(Namespace(fix=False)) + + out = buf.getvalue() + assert "model.provider 'custom:hpc-ai' is not a recognised provider" not in out + assert "model.provider 'custom:hpc-ai' is unknown" not in out + assert ( + "model.default 'deepseek/deepseek-v4-flash' uses a vendor/model slug but provider is " + "'custom:hpc-ai'" + not in out + ) + assert "Either set model.provider to 'openrouter', or drop the vendor prefix." not in out + + def test_run_doctor_accepts_kimi_coding_cn_provider(monkeypatch, tmp_path): diff --git a/tests/hermes_cli/test_model_switch_custom_providers.py b/tests/hermes_cli/test_model_switch_custom_providers.py index f90c000ec85..d3419f9d5b4 100644 --- a/tests/hermes_cli/test_model_switch_custom_providers.py +++ b/tests/hermes_cli/test_model_switch_custom_providers.py @@ -65,6 +65,15 @@ def test_resolve_provider_full_finds_named_custom_provider(): assert resolved.source == "user-config" +def test_is_aggregator_recognizes_named_custom_provider(): + assert providers_mod.is_aggregator("custom:hpc-ai") is True + assert providers_mod.is_aggregator("custom:litellm") is True + + +def test_is_aggregator_leaves_unknown_provider_non_aggregator(): + assert providers_mod.is_aggregator("not-a-provider") is False + + def test_switch_model_accepts_explicit_named_custom_provider(monkeypatch): """Shared /model switch pipeline should accept --provider for custom_providers.""" monkeypatch.setattr(