fix(image_routing): check stripped custom:<name> provider key for vision override
When model.provider is set to custom:<name>, _supports_vision_override()
previously tried only the runtime provider key ('custom') and the raw
config value ('custom:my-proxy'). It did not try the stripped name
('my-proxy'), which is the actual key under providers: in config.yaml.
This caused native image routing to fall back to text mode even when the
user explicitly declared supports_vision: true on the named provider's
model entry.
Fixes #39963
This commit is contained in:
parent
7485fe0605
commit
5e11628546
2 changed files with 40 additions and 3 deletions
|
|
@ -185,7 +185,8 @@ def _supports_vision_override(
|
|||
2. ``providers.<provider>.models.<model>.supports_vision``
|
||||
(named custom providers — ``provider`` may be the runtime-resolved
|
||||
value ``"custom"`` and/or the user-declared name under
|
||||
``model.provider``; both are tried)
|
||||
``model.provider``; both are tried. For ``custom:<name>`` syntax,
|
||||
the stripped ``<name>`` is also tried as a provider key.)
|
||||
|
||||
Returns None when no override is set, so the caller falls through to
|
||||
models.dev. Returns False explicitly only when the user wrote a
|
||||
|
|
@ -205,11 +206,16 @@ def _supports_vision_override(
|
|||
# get rewritten to provider="custom" at runtime
|
||||
# (hermes_cli/runtime_provider.py:_resolve_named_custom_runtime), so the
|
||||
# config still holds the user-declared name under model.provider. Try
|
||||
# both as candidate provider keys.
|
||||
# both as candidate provider keys, plus the stripped suffix from
|
||||
# "custom:<name>" (where <name> is the key under providers:).
|
||||
config_provider = str(model_cfg.get("provider") or "").strip()
|
||||
# Extract the stripped name from "custom:<name>" if present
|
||||
stripped_suffix = ""
|
||||
if config_provider.startswith("custom:"):
|
||||
stripped_suffix = config_provider[len("custom:"):]
|
||||
providers_raw = cfg.get("providers")
|
||||
providers_cfg: Dict[str, Any] = providers_raw if isinstance(providers_raw, dict) else {}
|
||||
for p in dict.fromkeys(filter(None, (provider, config_provider))):
|
||||
for p in dict.fromkeys(filter(None, (provider, config_provider, stripped_suffix))):
|
||||
entry_raw = providers_cfg.get(p)
|
||||
entry: Dict[str, Any] = entry_raw if isinstance(entry_raw, dict) else {}
|
||||
models_raw = entry.get("models")
|
||||
|
|
|
|||
|
|
@ -224,6 +224,37 @@ class TestSupportsVisionOverride:
|
|||
cfg = {"model": "some-string", "providers": ["not-a-dict"]}
|
||||
assert _supports_vision_override(cfg, "custom", "my-llava") is None
|
||||
|
||||
def test_custom_colon_name_stripped_suffix_lookup(self):
|
||||
# model.provider: custom:my-proxy → should resolve stripped key "my-proxy"
|
||||
cfg = {
|
||||
"model": {"provider": "custom:my-proxy"},
|
||||
"providers": {
|
||||
"my-proxy": {"models": {"gpt-5.5": {"supports_vision": True}}},
|
||||
},
|
||||
}
|
||||
assert _supports_vision_override(cfg, "custom", "gpt-5.5") is True
|
||||
|
||||
def test_custom_colon_name_stripped_suffix_false(self):
|
||||
# Explicitly disabled vision on the stripped key.
|
||||
cfg = {
|
||||
"model": {"provider": "custom:my-proxy"},
|
||||
"providers": {
|
||||
"my-proxy": {"models": {"gpt-5.5": {"supports_vision": False}}},
|
||||
},
|
||||
}
|
||||
assert _supports_vision_override(cfg, "custom", "gpt-5.5") is False
|
||||
|
||||
def test_custom_colon_name_no_stripped_key_falls_through(self):
|
||||
# custom:my-proxy but providers only has "custom" — stripped key
|
||||
# doesn't match, but "custom" does via runtime provider.
|
||||
cfg = {
|
||||
"model": {"provider": "custom:my-proxy"},
|
||||
"providers": {
|
||||
"custom": {"models": {"gpt-5.5": {"supports_vision": True}}},
|
||||
},
|
||||
}
|
||||
assert _supports_vision_override(cfg, "custom", "gpt-5.5") is True
|
||||
|
||||
|
||||
# ─── _lookup_supports_vision (override-aware) ────────────────────────────────
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue