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-09 22:33:34 +02:00
|
|
|
"""Regression tests for gateway /model support of config.yaml custom_providers."""
|
|
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from gateway.config import Platform
|
|
|
|
|
from gateway.platforms.base import MessageEvent, MessageType
|
|
|
|
|
from gateway.run import GatewayRunner
|
|
|
|
|
from gateway.session import SessionSource
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _make_runner():
|
|
|
|
|
runner = object.__new__(GatewayRunner)
|
|
|
|
|
runner.adapters = {}
|
2026-04-10 02:52:56 -07:00
|
|
|
runner._voice_mode = {}
|
|
|
|
|
runner._session_model_overrides = {}
|
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-09 22:33:34 +02:00
|
|
|
return runner
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _make_event(text="/model"):
|
|
|
|
|
return MessageEvent(
|
|
|
|
|
text=text,
|
|
|
|
|
message_type=MessageType.TEXT,
|
|
|
|
|
source=SessionSource(platform=Platform.TELEGRAM, chat_id="12345", chat_type="dm"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_handle_model_command_lists_saved_custom_provider(tmp_path, monkeypatch):
|
|
|
|
|
hermes_home = tmp_path / ".hermes"
|
|
|
|
|
hermes_home.mkdir()
|
|
|
|
|
(hermes_home / "config.yaml").write_text(
|
|
|
|
|
yaml.safe_dump(
|
|
|
|
|
{
|
|
|
|
|
"model": {
|
|
|
|
|
"default": "gpt-5.4",
|
|
|
|
|
"provider": "openai-codex",
|
|
|
|
|
"base_url": "https://chatgpt.com/backend-api/codex",
|
|
|
|
|
},
|
|
|
|
|
"providers": {},
|
|
|
|
|
"custom_providers": [
|
|
|
|
|
{
|
|
|
|
|
"name": "Local (127.0.0.1:4141)",
|
|
|
|
|
"base_url": "http://127.0.0.1:4141/v1",
|
|
|
|
|
"model": "rotator-openrouter-coding",
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
encoding="utf-8",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
import gateway.run as gateway_run
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
|
|
|
|
|
monkeypatch.setattr("agent.models_dev.fetch_models_dev", lambda: {})
|
|
|
|
|
|
|
|
|
|
result = await _make_runner()._handle_model_command(_make_event())
|
|
|
|
|
|
|
|
|
|
assert result is not None
|
|
|
|
|
assert "Local (127.0.0.1:4141)" in result
|
|
|
|
|
assert "custom:local-(127.0.0.1:4141)" in result
|
|
|
|
|
assert "rotator-openrouter-coding" in result
|