"""Regression tests for /model support of config.yaml custom_providers. The terminal `hermes model` flow already exposes `custom_providers`, but the shared slash-command pipeline (`/model` in CLI/gateway/Telegram) historically only looked at `providers:`. """ import hermes_cli.providers as providers_mod from hermes_cli.model_switch import list_authenticated_providers, switch_model from hermes_cli.providers import resolve_provider_full _MOCK_VALIDATION = { "accepted": True, "persist": True, "recognized": True, "message": None, } def test_list_authenticated_providers_includes_custom_providers(monkeypatch): """No-args /model menus should include saved custom_providers entries.""" monkeypatch.setattr("agent.models_dev.fetch_models_dev", lambda: {}) monkeypatch.setattr(providers_mod, "HERMES_OVERLAYS", {}) providers = list_authenticated_providers( current_provider="openai-codex", user_providers={}, custom_providers=[ { "name": "Local (127.0.0.1:4141)", "base_url": "http://127.0.0.1:4141/v1", "model": "rotator-openrouter-coding", } ], max_models=50, ) assert any( p["slug"] == "custom:local-(127.0.0.1:4141)" and p["name"] == "Local (127.0.0.1:4141)" and p["models"] == ["rotator-openrouter-coding"] and p["api_url"] == "http://127.0.0.1:4141/v1" for p in providers ) def test_resolve_provider_full_finds_named_custom_provider(): """Explicit /model --provider should resolve saved custom_providers entries.""" resolved = resolve_provider_full( "custom:local-(127.0.0.1:4141)", user_providers={}, custom_providers=[ { "name": "Local (127.0.0.1:4141)", "base_url": "http://127.0.0.1:4141/v1", } ], ) assert resolved is not None assert resolved.id == "custom:local-(127.0.0.1:4141)" assert resolved.name == "Local (127.0.0.1:4141)" assert resolved.base_url == "http://127.0.0.1:4141/v1" assert resolved.source == "user-config" def test_switch_model_accepts_explicit_named_custom_provider(monkeypatch): """Shared /model switch pipeline should accept --provider for custom_providers.""" monkeypatch.setattr( "hermes_cli.runtime_provider.resolve_runtime_provider", lambda requested: { "api_key": "no-key-required", "base_url": "http://127.0.0.1:4141/v1", "api_mode": "chat_completions", }, ) monkeypatch.setattr("hermes_cli.models.validate_requested_model", lambda *a, **k: _MOCK_VALIDATION) monkeypatch.setattr("hermes_cli.model_switch.get_model_info", lambda *a, **k: None) monkeypatch.setattr("hermes_cli.model_switch.get_model_capabilities", lambda *a, **k: None) result = switch_model( raw_input="rotator-openrouter-coding", current_provider="openai-codex", current_model="gpt-5.4", current_base_url="https://chatgpt.com/backend-api/codex", current_api_key="", explicit_provider="custom:local-(127.0.0.1:4141)", user_providers={}, custom_providers=[ { "name": "Local (127.0.0.1:4141)", "base_url": "http://127.0.0.1:4141/v1", "model": "rotator-openrouter-coding", } ], ) assert result.success is True assert result.target_provider == "custom:local-(127.0.0.1:4141)" assert result.provider_label == "Local (127.0.0.1:4141)" assert result.new_model == "rotator-openrouter-coding" assert result.base_url == "http://127.0.0.1:4141/v1" assert result.api_key == "no-key-required"