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>
152 lines
5.9 KiB
Python
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 |