fix: key the routed vision-tool veto on is_routing_aggregator

The hand-rolled `_AGGREGATOR_MODEL_PREFIX_PROVIDERS = {"openrouter"}` duplicated
aggregator knowledge already owned by hermes_cli.providers.is_routing_aggregator,
so other vendor-prefixed aggregators (ai-gateway, kilocode, custom:* proxies)
kept bypassing the target-profile veto. Use the existing predicate instead of a
second frozenset.

Review finding: aggregator allowlist duplicated is_routing_aggregator and missed other vendor-prefixed aggregators.
This commit is contained in:
teknium1
2026-09-14 21:13:17 -07:00
committed by Teknium
parent d94f13fe35
commit d4863cf272

View File

@@ -47,11 +47,6 @@ _ALIASES: dict[str, str] = {}
_PROVIDER_LIST_CACHE: list[ProviderProfile] | None = None
_discovered = False
# These APIs accept a ``vendor/model`` identifier while the request is sent to
# the aggregator. A target provider can have stricter message-shape support
# than the aggregator's generic OpenAI-compatible transport profile.
_AGGREGATOR_MODEL_PREFIX_PROVIDERS = frozenset({"openrouter"})
# Repo-root ``plugins/model-providers/`` — populated at discovery time.
_BUNDLED_PLUGINS_DIR = (
Path(__file__).resolve().parent.parent / "plugins" / "model-providers"
@@ -91,17 +86,21 @@ def get_provider_profile(name: str) -> ProviderProfile | None:
def routed_model_rejects_vision_tool_messages(provider: str, model: str) -> bool:
"""Whether an active route or its aggregator-targeted model rejects image tool parts.
``openrouter`` sends vendor-prefixed model IDs (for example,
``xiaomi/mimo-v2.5``), but its own profile cannot describe every routed
provider's tool-message compatibility. Preserve the transport profile as
the default and consult a registered target profile only for known
aggregators. Missing or unrecognized identities deliberately fail open.
Routing aggregators such as ``openrouter`` send vendor-prefixed model IDs
(for example, ``xiaomi/mimo-v2.5``), but their own profile cannot describe
every routed provider's tool-message compatibility. Preserve the transport
profile as the default and consult a registered target profile only for
routing aggregators. Missing or unrecognized identities deliberately fail open.
"""
provider_name = str(provider or "").strip().lower()
profile = get_provider_profile(provider_name)
if profile is not None and profile.supports_vision_tool_messages is False:
return True
if provider_name not in _AGGREGATOR_MODEL_PREFIX_PROVIDERS:
# Routing aggregators accept a ``vendor/model`` identifier while the request is sent
# to the aggregator; the target provider can have stricter message-shape support than
# the aggregator's generic OpenAI-compatible transport profile.
from hermes_cli.providers import is_routing_aggregator
if not is_routing_aggregator(provider_name):
return False
target_name, separator, _ = str(model or "").strip().partition("/")