From d143bf7a3b8656c3edcfeb3a81db075f7e06ba6b Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:48:20 -0700 Subject: [PATCH] fix(model-metadata): resolve provider prefixes from live registry --- agent/model_metadata.py | 66 +++++++++++------------------- tests/agent/test_model_metadata.py | 43 ++++++++++--------- 2 files changed, 47 insertions(+), 62 deletions(-) diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 334ab4f58930f..45baf18f3c311 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -66,33 +66,19 @@ def _resolve_requests_verify() -> bool | str: return val return True -# Provider names that can appear as a "provider:" prefix before a model ID. -# Only these are stripped — Ollama-style "model:tag" colons (e.g. "qwen3.5:27b") -# are preserved so the full model name reaches cache lookups and server queries. -_PROVIDER_PREFIXES: frozenset[str] = frozenset({ - "openrouter", "nous", "openai-codex", "copilot", "copilot-acp", - "gemini", "ollama-cloud", "zai", "kimi-coding", "kimi-coding-cn", "stepfun", "minimax", "minimax-oauth", "minimax-cn", "anthropic", "deepseek", "deepinfra", - "opencode-zen", "opencode-go", "ai-gateway", "kilocode", "alibaba", "novita", - "qwen-oauth", - "xiaomi", - "arcee", - "gmi", - "tencent-tokenhub", - "custom", "local", - # Common aliases - "google", "google-gemini", "google-ai-studio", - "glm", "z-ai", "z.ai", "zhipu", "github", "github-copilot", - "github-models", "kimi", "moonshot", "kimi-cn", "moonshot-cn", "claude", "deep-seek", "deep-infra", - "ollama", - "stepfun", "opencode", "zen", "go", "vercel", "kilo", "dashscope", "aliyun", "qwen", - "mimo", "xiaomi-mimo", - "tencent", "tokenhub", "tencent-cloud", "tencentmaas", - "arcee-ai", "arceeai", - "gmi-cloud", "gmicloud", - "xai", "x-ai", "x.ai", "grok", - "nvidia", "nim", "nvidia-nim", "nemotron", - "qwen-portal", "novita-ai", "novitaai", -}) +# Compatibility snapshot for callers that inspect this private constant. +# Prefix routing below queries the registry live so later registrations work. +try: + from providers import list_providers as _list_providers +except Exception: + def _list_providers(): + return [] + +_PROVIDER_PREFIXES: frozenset[str] = frozenset( + value.lower() + for profile in _list_providers() + for value in (profile.name, *profile.aliases) +) _OLLAMA_TAG_PATTERN = re.compile( @@ -111,6 +97,9 @@ _TAILSCALE_CGNAT = ipaddress.IPv4Network("100.64.0.0/10") def _strip_provider_prefix(model: str) -> str: """Strip a recognised provider prefix from a model string. + Provider names and aliases come from the provider-profile registry, so + bundled and user plugins are recognised without a core catalog update. + ``"local:my-model"`` → ``"my-model"`` ``"qwen3.5:27b"`` → ``"qwen3.5:27b"`` (unchanged — not a provider prefix) ``"qwen:0.5b"`` → ``"qwen:0.5b"`` (unchanged — Ollama model:tag) @@ -120,7 +109,13 @@ def _strip_provider_prefix(model: str) -> str: return model prefix, suffix = model.split(":", 1) prefix_lower = prefix.strip().lower() - if prefix_lower in _PROVIDER_PREFIXES: + try: + from providers import get_provider_profile + + is_provider = get_provider_profile(prefix_lower) is not None + except Exception: + is_provider = False + if is_provider: # Don't strip if suffix looks like an Ollama tag (e.g. "7b", "latest", "q4_0") if _OLLAMA_TAG_PATTERN.match(suffix.strip()): return model @@ -704,7 +699,6 @@ _URL_TO_PROVIDER: Dict[str, str] = { # Auto-extend with hostnames derived from provider profiles. # Any provider with a base_url not already in the map gets added automatically. try: - from providers import list_providers as _list_providers for _pp in _list_providers(): _host = _pp.get_hostname() if _host and _host not in _URL_TO_PROVIDER: @@ -712,20 +706,6 @@ try: except Exception: pass -# Auto-extend _PROVIDER_PREFIXES the same way, so "provider:model" strings -# strip correctly for plugin providers (user plugins under -# $HERMES_HOME/plugins/model-providers/ included) without editing this file. -# The _OLLAMA_TAG_PATTERN guard in _strip_provider_prefix still protects -# Ollama-style "model:tag" strings from over-stripping. -try: - _plugin_prefixes: set[str] = set() - for _pp in _list_providers(): - _plugin_prefixes.add(_pp.name.lower()) - _plugin_prefixes.update(str(_a).lower() for _a in _pp.aliases) - _PROVIDER_PREFIXES = frozenset(_PROVIDER_PREFIXES | _plugin_prefixes) -except Exception: - pass - def _infer_provider_from_url(base_url: str) -> Optional[str]: """Infer the models.dev provider name from a base URL. diff --git a/tests/agent/test_model_metadata.py b/tests/agent/test_model_metadata.py index a1d36fedf1c8d..7c36e14dda53c 100644 --- a/tests/agent/test_model_metadata.py +++ b/tests/agent/test_model_metadata.py @@ -894,28 +894,33 @@ class TestStripProviderPrefix: assert _strip_provider_prefix("http://example.com") == "http://example.com" assert _strip_provider_prefix("https://example.com") == "https://example.com" - def test_registered_provider_names_and_aliases_are_prefixes(self): - """Prefixes auto-extend from registered profiles, like _URL_TO_PROVIDER. + def test_registered_profile_name_and_alias_are_stripped(self, monkeypatch): + import providers + from providers import ProviderProfile - Bundled plugin providers (and user plugins under - $HERMES_HOME/plugins/model-providers/) must strip without a manual - entry in the static frozenset. - """ - from providers import list_providers + monkeypatch.setattr(providers, "_REGISTRY", {}) + monkeypatch.setattr(providers, "_ALIASES", {}) + monkeypatch.setattr(providers, "_PROVIDER_LIST_CACHE", None) + monkeypatch.setattr(providers, "_discovered", True) + providers.register_provider( + ProviderProfile(name="fake-provider", aliases=("fake-alias",)) + ) - from agent.model_metadata import _PROVIDER_PREFIXES + assert _strip_provider_prefix("fake-provider:org/model") == "org/model" + assert _strip_provider_prefix("fake-alias:org/model") == "org/model" - for profile in list_providers(): - assert profile.name.lower() in _PROVIDER_PREFIXES, ( - f"registered provider {profile.name!r} missing from prefixes" - ) - for alias in profile.aliases: - assert str(alias).lower() in _PROVIDER_PREFIXES, ( - f"alias {alias!r} of {profile.name!r} missing from prefixes" - ) - # And a concrete strip using a bundled provider absent from the - # static set (fireworks ships as a plugin only). - assert _strip_provider_prefix("fireworks:some/model-v1") == "some/model-v1" + def test_bundled_plugin_provider_prefix_is_stripped(self): + assert _strip_provider_prefix("fireworks:accounts/fireworks/models/foo") == ( + "accounts/fireworks/models/foo" + ) + + def test_unknown_provider_prefix_is_unchanged(self): + assert _strip_provider_prefix("not-a-provider:org/model") == ( + "not-a-provider:org/model" + ) + + def test_ollama_model_tag_is_unchanged(self): + assert _strip_provider_prefix("qwen3.5:27b") == "qwen3.5:27b" @patch("agent.model_metadata.fetch_model_metadata") def test_ollama_model_tag_not_mangled_in_context_lookup(self, mock_fetch):