mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-16 20:50:18 +00:00
harden #5511: canonicalise active provider for ownership set (latent alias bug)
Codex 3rd CORE (pre-existing latent bug the providers: scan exposes): the
_provider_models_set ownership lookup used the RAW config_provider (e.g. 'z-ai')
but _PROVIDER_MODELS is keyed by canonical slug ('zai'), so an aliased active
provider got an empty ownership set -> _skip_custom_providers guard-2 silently
failed -> another providers.<slug>.models entry could hijack an active-owned
model (verified: active z-ai + glm-5 + providers.openai.models.glm-5 -> openai).
Fix: canonicalise config_provider before the _PROVIDER_MODELS lookup (and reuse
the canonical slug for the scan's ownership guard). Regression test with an
aliased active provider (verified fail-without-fix).
This is the root-cause fix for the alias class — the ownership guard now holds
for every aliased provider (zai/kilo/etc.), not just the reported site.
Co-authored-by: akay64 <akay64@users.noreply.github.com>
This commit is contained in:
+11
-5
@@ -2522,14 +2522,20 @@ def resolve_model_provider(model_id: str) -> tuple:
|
||||
)
|
||||
_default_model = model_cfg.get('default') if isinstance(model_cfg, dict) else None
|
||||
# Owns model if it appears in the static catalog for the configured provider.
|
||||
# _PROVIDER_MODELS is keyed by CANONICAL slug (e.g. 'zai', not the 'z-ai'
|
||||
# alias a user may write in config), so canonicalise config_provider before
|
||||
# the lookup — otherwise an aliased active provider gets an empty ownership
|
||||
# set and _skip_custom_providers guard-2 silently fails, letting another
|
||||
# providers.<slug>.models entry hijack an active-owned model (#5511).
|
||||
_canon_config_provider = _canonicalise_provider_id(config_provider) if config_provider else ""
|
||||
_provider_models_set: set[str] = set()
|
||||
if (
|
||||
config_provider is not None
|
||||
and config_provider in _PROVIDER_MODELS
|
||||
and isinstance(_PROVIDER_MODELS[config_provider], list)
|
||||
_canon_config_provider
|
||||
and _canon_config_provider in _PROVIDER_MODELS
|
||||
and isinstance(_PROVIDER_MODELS[_canon_config_provider], list)
|
||||
):
|
||||
_provider_models_set = {
|
||||
m.get('id', '') for m in _PROVIDER_MODELS[config_provider]
|
||||
m.get('id', '') for m in _PROVIDER_MODELS[_canon_config_provider]
|
||||
if isinstance(m, dict) and isinstance(m.get('id'), str)
|
||||
}
|
||||
_skip_custom_providers = (
|
||||
@@ -2577,7 +2583,7 @@ def resolve_model_provider(model_id: str) -> tuple:
|
||||
# active provider (#5511 gate finding — e.g. active ai-gateway + default
|
||||
# gpt-5 was being pulled to providers.openai.models.gpt-5). In that case
|
||||
# restrict the scan to the active provider's own canonical slug.
|
||||
_active_slug = _canonicalise_provider_id(config_provider) if config_provider else ""
|
||||
_active_slug = _canon_config_provider
|
||||
for slug, pdef in providers_cfg.items():
|
||||
if not isinstance(pdef, dict):
|
||||
continue
|
||||
|
||||
@@ -845,3 +845,29 @@ def test_providers_scan_active_provider_own_entry_still_matches_5511():
|
||||
)
|
||||
assert provider == 'myprov', f"active provider's own entry must match; got {provider!r}"
|
||||
assert base_url == 'https://my.example/v1'
|
||||
|
||||
|
||||
def test_providers_scan_ownership_guard_canonicalises_aliased_active_provider_5511():
|
||||
"""An ALIASED active provider (e.g. 'z-ai' → canonical 'zai') must still be
|
||||
recognized as owning its catalog models, so another providers.<slug>.models
|
||||
entry can't hijack an active-owned model (#5511 latent-bug gate finding —
|
||||
_provider_models_set was built with the raw alias, missing the canonical
|
||||
_PROVIDER_MODELS key, so the ownership guard silently failed)."""
|
||||
import api.config as config
|
||||
# Pick a real catalog model id owned by the canonical 'zai' provider.
|
||||
zai_models = config._PROVIDER_MODELS.get('zai') or []
|
||||
zai_ids = [m.get('id') for m in zai_models if isinstance(m, dict) and m.get('id')]
|
||||
if not zai_ids:
|
||||
import pytest
|
||||
pytest.skip("no zai catalog models to exercise the alias ownership guard")
|
||||
owned = zai_ids[0]
|
||||
model, provider, base_url = _resolve_with_providers(
|
||||
owned,
|
||||
{'openai': {'models': [owned]}},
|
||||
provider='z-ai', # aliased form the user may write in config
|
||||
)
|
||||
assert config._canonicalise_provider_id('z-ai') == 'zai'
|
||||
assert provider == 'z-ai', (
|
||||
"aliased active provider (z-ai→zai) that owns the model must not be "
|
||||
f"hijacked by providers.openai.models; got {provider!r}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user