Files
hermes-webui/tests/test_issue5127_process_wakeup_bare_model.py
T
b3nwandb3nw 7a04b7e9aa fix: gate bare-model repair on profile provider match (#5128 review)
When session model_provider differs from profile model.provider, do not
expose profile_default_model for suffix repair. Fast path also requires
profile_provider to match requested_provider when profile_provider is set.

Addresses cross-provider mis-resolution (custom:other-proxy + bare suffix).

Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-06-28 21:52:06 +00:00

152 lines
5.9 KiB
Python

"""Tests for issue #5127 — process-wakeup bare custom-provider model repair.
When ``_resolve_compatible_session_model_state`` takes the #1855 fast path
(both model and model_provider set), a bare runtime model that matches the
suffix of a slash-qualified ``profile_default_model`` must repair to the full
qualified ID for named custom providers.
"""
from unittest.mock import patch
class TestIssue5127CustomProviderBareSuffixRepair:
def test_fast_path_repairs_bare_suffix_to_profile_default(self):
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
result = _resolve_compatible_session_model_state(
"grok-composer-2.5-fast",
"custom:my-proxy",
profile_provider="custom:my-proxy",
profile_default_model="x-ai/grok-composer-2.5-fast",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count == 0
assert result == ("x-ai/grok-composer-2.5-fast", "custom:my-proxy", True)
def test_fast_path_skips_repair_when_profile_provider_mismatches(self):
"""Regression: custom:other-proxy must not inherit my-proxy's qualified default."""
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
result = _resolve_compatible_session_model_state(
"grok-composer-2.5-fast",
"custom:other-proxy",
profile_provider="custom:my-proxy",
profile_default_model="x-ai/grok-composer-2.5-fast",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count == 0
assert result == ("grok-composer-2.5-fast", "custom:other-proxy", False)
def test_fast_path_repairs_generic_custom_provider(self):
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
result = _resolve_compatible_session_model_state(
"some-model",
"custom",
profile_default_model="vendor/some-model",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count == 0
assert result == ("vendor/some-model", "custom", True)
def test_fast_path_does_not_rewrite_unrelated_bare_model(self):
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
result = _resolve_compatible_session_model_state(
"other-model",
"custom:my-proxy",
profile_default_model="x-ai/grok-composer-2.5-fast",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count == 0
assert result == ("other-model", "custom:my-proxy", False)
def test_fast_path_keeps_qualified_model_unchanged(self):
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
result = _resolve_compatible_session_model_state(
"x-ai/grok-composer-2.5-fast",
"custom:my-proxy",
profile_default_model="x-ai/grok-composer-2.5-fast",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count == 0
assert result == ("x-ai/grok-composer-2.5-fast", "custom:my-proxy", False)
def test_openai_codex_stale_openai_slash_still_uses_slow_path(self):
"""Regression: openai/... under openai-codex must not take bare fast return."""
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
mock_catalog.return_value = {
"active_provider": "openai-codex",
"default_model": "gpt-5.5",
"groups": [],
}
result = _resolve_compatible_session_model_state(
"openai/gpt-5.4-mini",
"openai-codex",
profile_default_model="gpt-5.5",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count >= 1
assert result[0] == "gpt-5.5"
assert result[2] is True
class TestReadProfileModelConfigWithExplicitProvider:
def test_returns_profile_default_when_session_has_model_provider(self, tmp_path, monkeypatch):
from api.routes import _read_profile_model_config
profile_home = tmp_path / "prof"
profile_home.mkdir()
(profile_home / "config.yaml").write_text(
"model:\n provider: custom:my-proxy\n default: x-ai/grok-composer-2.5-fast\n",
encoding="utf-8",
)
class _Session:
profile = "testprof"
monkeypatch.setattr(
"api.profiles.get_hermes_home_for_profile",
lambda _p: str(profile_home),
)
provider, default = _read_profile_model_config(_Session(), "custom:my-proxy")
assert provider is None
assert default == "x-ai/grok-composer-2.5-fast"
def test_returns_none_default_when_session_provider_differs_from_profile(
self, tmp_path, monkeypatch,
):
from api.routes import _read_profile_model_config
profile_home = tmp_path / "prof"
profile_home.mkdir()
(profile_home / "config.yaml").write_text(
"model:\n provider: custom:my-proxy\n default: x-ai/grok-composer-2.5-fast\n",
encoding="utf-8",
)
class _Session:
profile = "testprof"
monkeypatch.setattr(
"api.profiles.get_hermes_home_for_profile",
lambda _p: str(profile_home),
)
provider, default = _read_profile_model_config(_Session(), "custom:other-proxy")
assert provider is None
assert default is None