With `model.provider` equal to the live route, `_supports_vision_override` already resolves the capability from config on main, so the child went native regardless and the test only failed on a missing dict key. Persist a different default route so the assertion fails on the aux slow path itself, which is where the reported symptom fires (a /model switch or session-scoped pick).
109 lines
3.3 KiB
Python
109 lines
3.3 KiB
Python
"""Delegated children keep the route identity required by native vision.
|
|
|
|
Regression for the subagent path omitted when #70071 threaded named custom
|
|
provider identity through the other agent-construction surfaces.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from agent.auxiliary_client import reset_runtime_main, set_runtime_main
|
|
from hermes_constants import get_hermes_home
|
|
from tools import vision_tools # noqa: F401 - registers vision_analyze
|
|
from tools.delegate_tool_config import _resolve_child_runtime
|
|
from tools.registry import registry
|
|
|
|
|
|
_TINY_PNG = base64.b64decode(
|
|
b"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
|
|
)
|
|
|
|
|
|
def _parent() -> SimpleNamespace:
|
|
return SimpleNamespace(
|
|
model="vision-model",
|
|
provider="custom",
|
|
requested_provider="custom:vision-endpoint",
|
|
base_url="https://vision.invalid/v1",
|
|
api_mode="chat_completions",
|
|
capabilities={},
|
|
fallback_model=None,
|
|
request_overrides={},
|
|
reasoning_config=None,
|
|
acp_args=[],
|
|
)
|
|
|
|
|
|
def _child_runtime(parent: SimpleNamespace, *, override_base_url=None):
|
|
return _resolve_child_runtime(
|
|
parent,
|
|
{},
|
|
"test-key",
|
|
model=None,
|
|
override_provider=None,
|
|
override_base_url=override_base_url,
|
|
override_api_key=None,
|
|
override_api_mode=None,
|
|
override_acp_command=None,
|
|
override_acp_args=None,
|
|
)
|
|
|
|
|
|
def test_inherited_named_custom_child_uses_native_vision(tmp_path):
|
|
runtime = _child_runtime(_parent())
|
|
assert runtime["provider"] == "custom"
|
|
|
|
image = tmp_path / "page.png"
|
|
image.write_bytes(_TINY_PNG)
|
|
# The persisted default is a different named route: config-side fallback must not mask the child's
|
|
# missing identity (a /model switch or session-scoped pick is where the aux slow path fired live).
|
|
get_hermes_home().joinpath("config.yaml").write_text(
|
|
"""\
|
|
model:
|
|
provider: custom:text-endpoint
|
|
default: text-model
|
|
providers:
|
|
vision-endpoint:
|
|
name: Vision Endpoint
|
|
api: https://vision.invalid/v1
|
|
models:
|
|
vision-model:
|
|
supports_vision: true
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
token = set_runtime_main(
|
|
runtime["provider"],
|
|
runtime["model"],
|
|
requested_provider=runtime.get("requested_provider") or "",
|
|
base_url=runtime["base_url"],
|
|
api_key=runtime["api_key"],
|
|
api_mode=runtime["api_mode"],
|
|
)
|
|
try:
|
|
with patch(
|
|
"tools.vision_tools.vision_analyze_tool", new_callable=AsyncMock
|
|
) as auxiliary:
|
|
result = registry.dispatch(
|
|
"vision_analyze", {"image_url": str(image), "question": "describe"}
|
|
)
|
|
finally:
|
|
reset_runtime_main(token)
|
|
|
|
assert isinstance(result, dict) and result["_multimodal"] is True
|
|
auxiliary.assert_not_called()
|
|
assert runtime.get("requested_provider") == "custom:vision-endpoint"
|
|
|
|
|
|
def test_endpoint_override_does_not_borrow_parent_named_identity():
|
|
runtime = _child_runtime(
|
|
_parent(), override_base_url="https://different.invalid/v1"
|
|
)
|
|
|
|
assert runtime["provider"] == "custom"
|
|
assert runtime["requested_provider"] == "custom"
|