fix(web): preserve declared providers in model writes (#63058)

Unify the named-provider fixes from #52506, #57185, #60337, and #60901 at the main-model normalization chokepoint.

Co-authored-by: izumi0uu <izumi0uu@gmail.com>
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
Co-authored-by: Paulo Henrique <paulohenrique_789@hotmail.com>
This commit is contained in:
Teknium 2026-07-12 03:08:08 -07:00 committed by GitHub
parent b0ff1c3cc5
commit 9a15fad0d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 64 additions and 1 deletions

View file

@ -1023,19 +1023,42 @@ def _normalize_main_model_assignment(provider: str, model: str) -> tuple[str, st
``normalize_model_for_provider`` (e.g. ``anthropic/claude-opus-4.6``
on native anthropic ``claude-opus-4-6``).
"""
from hermes_cli.config import get_compatible_custom_providers
from hermes_cli.models import _KNOWN_PROVIDER_NAMES, normalize_provider
from hermes_cli.model_normalize import normalize_model_for_provider
from hermes_cli.providers import resolve_custom_provider, resolve_user_provider
prov_in = (provider or "").strip()
model_in = (model or "").strip()
canonical = normalize_provider(prov_in)
# User-declared providers are real routing targets, not analytics vendor
# labels. Resolve them before the unknown-vendor fallback. ``providers:``
# keeps its declared bare slug; ``custom_providers:`` canonicalizes both a
# bare display name and ``custom:<name>`` to the durable custom slug.
try:
cfg = load_config()
except Exception:
cfg = {}
user_providers = cfg.get("providers") if isinstance(cfg, dict) else None
user_provider = resolve_user_provider(
prov_in, user_providers if isinstance(user_providers, dict) else {}
)
custom_provider = resolve_custom_provider(
prov_in,
get_compatible_custom_providers(cfg) if isinstance(cfg, dict) else [],
)
if user_provider is not None:
return user_provider.id, model_in
if custom_provider is not None:
return custom_provider.id, model_in
if canonical not in _KNOWN_PROVIDER_NAMES and "/" in model_in:
# Vendor prefix posing as a provider (analytics fallback). Resolve
# against the user's current provider when it's an aggregator that
# serves vendor-prefixed slugs; otherwise default to openrouter.
try:
cur_cfg = load_config().get("model", {})
cur_cfg = cfg.get("model", {})
cur_provider = (
str(cur_cfg.get("provider", "") or "").strip().lower()
if isinstance(cur_cfg, dict) else ""

View file

@ -0,0 +1,40 @@
"""Dashboard main-model writes preserve declared provider identities."""
from unittest.mock import patch
from hermes_cli.web_server import _normalize_main_model_assignment
def _normalize(config, provider, model="vendor/model-a"):
with patch("hermes_cli.web_server.load_config", return_value=config):
return _normalize_main_model_assignment(provider, model)
def test_providers_block_keeps_declared_bare_slug():
result = _normalize(
{"providers": {"commandcode": {"base_url": "http://localhost:55990/v1"}}},
"commandcode",
)
assert result == ("commandcode", "vendor/model-a")
def test_custom_provider_name_canonicalizes_to_durable_slug():
config = {
"custom_providers": [
{"name": "US Azure", "base_url": "http://localhost:18025/v1"}
]
}
assert _normalize(config, "US Azure") == ("custom:us-azure", "vendor/model-a")
assert _normalize(config, "custom:us-azure") == (
"custom:us-azure",
"vendor/model-a",
)
def test_unknown_vendor_still_uses_aggregator_fallback():
assert _normalize({}, "unconfigured-vendor") == (
"openrouter",
"vendor/model-a",
)