mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-19 14:12:01 +00:00
fix configured provider slash model routing
This commit is contained in:
committed by
nesquena-hermes
parent
6ae0ecca0a
commit
630fd26eec
+13
-2
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user