hermes-agent/tests/hermes_cli/test_model_switch_custom_providers.py
donrhmexe a2f46e4665 fix: include custom_providers in /model command listings and resolution
Custom providers defined in config.yaml under  were
completely invisible to the /model command in both gateway (Telegram,
Discord, etc.) and CLI. The provider listing skipped them and explicit
switching via --provider failed with "Unknown provider".

Root cause: gateway/run.py, cli.py, and model_switch.py only read the
 dict from config, ignoring  entirely.

Changes:
- providers.py: add resolve_custom_provider() and extend
  resolve_provider_full() to check custom_providers after user_providers
- model_switch.py: propagate custom_providers through switch_model(),
  list_authenticated_providers(), and get_authenticated_provider_slugs();
  add custom provider section to provider listings
- gateway/run.py: read custom_providers from config, pass to all
  model-switch calls
- cli.py: hoist config loading, pass custom_providers to listing and
  switch calls

Tests: 4 new regression tests covering listing, resolution, and gateway
command handler. All 71 tests pass.
2026-04-10 03:07:00 -07:00

104 lines
3.7 KiB
Python

"""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"