fix configured provider slash model routing

This commit is contained in:
MinhoJJang
2026-06-20 18:00:21 +09:00
committed by nesquena-hermes
parent 6ae0ecca0a
commit 630fd26eec
4 changed files with 87 additions and 3 deletions
+13 -2
View File
@@ -2515,8 +2515,19 @@ def model_with_provider_context(model_id: str, model_provider: str | None = None
if provider == "openrouter":
return f"@{provider}:{model}"
# For non-OpenRouter slash IDs, keep the ID intact so existing custom/proxy
# base_url routing and portal-provider handling remain in charge.
# Explicit providers configured in config.yaml (for example local llama.cpp,
# Ollama, LM Studio, vLLM, or other OpenAI-compatible endpoints) must keep
# their provider hint even when the model ID is HuggingFace-style and
# contains '/'. Otherwise a selected local model such as
# 'unsloth/gemma-4-12b-it-GGUF:UD-Q4_K_XL' inherits the default provider
# (e.g. openai-codex) and is sent to the wrong backend.
providers_cfg = cfg.get("providers") if isinstance(cfg, dict) else {}
if isinstance(providers_cfg, dict) and provider in providers_cfg:
return f"@{provider}:{model}"
# For non-OpenRouter slash IDs without an explicit configured provider,
# keep the ID intact so existing custom/proxy base_url routing and
# portal-provider handling remain in charge.
if "/" in model:
return model
+9
View File
@@ -3713,6 +3713,15 @@ def _resolve_compatible_session_model_state(
"""
model = str(model_id or "").strip()
requested_provider = _clean_session_model_provider(model_provider)
if model and requested_provider and model.startswith(f"@{requested_provider}:"):
try:
from api.config import cfg as _active_cfg
providers_cfg = _active_cfg.get("providers") if isinstance(_active_cfg, dict) else {}
except Exception:
providers_cfg = {}
if isinstance(providers_cfg, dict) and requested_provider in providers_cfg:
return model, requested_provider, False
if model and requested_provider:
# Only safe when the model itself does not carry an ``@provider:model``
# qualifier — qualified strings require the catalog to decide whether
@@ -30,7 +30,6 @@ Coverage:
through).
"""
import pathlib
import re
from unittest.mock import patch
@@ -175,6 +174,40 @@ class TestSlowPathStillFires:
"cross-provider artifacts (#1253)."
)
def test_configured_provider_qualified_model_skips_catalog(self):
"""Configured providers already carry trusted routing context."""
import api.config as config
from api.routes import _resolve_compatible_session_model_state
old_cfg = dict(config.cfg)
config.cfg["model"] = {
"provider": "openai-codex",
"default": "gpt-5.5",
}
config.cfg["providers"] = {
"local-llama": {
"base_url": "http://127.0.0.1:8088/v1",
"api_key": "test-key",
},
}
try:
with patch("api.routes.get_available_models") as mock_catalog:
mock_catalog.side_effect = AssertionError("catalog should not be called")
result = _resolve_compatible_session_model_state(
"@local-llama:unsloth/gemma-4-12b-it-GGUF:UD-Q4_K_XL",
"local-llama",
)
finally:
config.cfg.clear()
config.cfg.update(old_cfg)
assert result == (
"@local-llama:unsloth/gemma-4-12b-it-GGUF:UD-Q4_K_XL",
"local-llama",
False,
)
assert mock_catalog.call_count == 0
def test_no_requested_provider_goes_to_slow_path(self):
"""When provider isn't supplied, slow path must repair from catalog."""
from api.routes import _resolve_compatible_session_model_state
+31
View File
@@ -522,6 +522,37 @@ def test_non_openrouter_slash_model_provider_context_stays_unqualified():
assert runtime_model == "anthropic/claude-sonnet-4.6"
def test_configured_provider_slash_model_keeps_provider_context():
"""Configured OpenAI-compatible providers need explicit context for slash IDs."""
import api.config as config
old_cfg = dict(config.cfg)
config.cfg["model"] = {
"provider": "openai-codex",
"default": "gpt-5.5",
}
config.cfg["providers"] = {
"local-llama": {
"base_url": "http://127.0.0.1:8088/v1",
"api_key": "test-key",
},
}
try:
runtime_model = config.model_with_provider_context(
"unsloth/gemma-4-12b-it-GGUF:UD-Q4_K_XL",
"local-llama",
)
model, provider, base_url = config.resolve_model_provider(runtime_model)
finally:
config.cfg.clear()
config.cfg.update(old_cfg)
assert runtime_model == "@local-llama:unsloth/gemma-4-12b-it-GGUF:UD-Q4_K_XL"
assert model == "unsloth/gemma-4-12b-it-GGUF:UD-Q4_K_XL"
assert provider == "local-llama"
assert base_url == "http://127.0.0.1:8088/v1"
def test_cursor_acp_slash_model_always_gets_provider_hint():
"""ACP subprocess models with '/' must not fall through to config default."""
import api.config as config