hermes-agent/tests/hermes_cli/test_cli_custom_provider_vision.py

125 lines
3.9 KiB
Python

"""End-to-end CLI coverage for named custom-provider vision routing.
Adapted from the independently reproduced CLI regression in #69896. Unlike
that PR, these tests keep the live transport canonical (``provider=custom``)
and assert that the separate requested identity reaches each capability gate.
"""
from __future__ import annotations
from types import SimpleNamespace
from hermes_cli.cli_agent_setup_mixin import CLIAgentSetupMixin
MODEL = "qwen3.8-max-preview"
REQUESTED_PROVIDER = "custom:qwen-token-plan"
class _RuntimeCLI(CLIAgentSetupMixin):
def __init__(self, *, model: str, provider: str):
self.model = model
self.requested_provider = provider
self.provider = provider
self.api_key = None
self.base_url = None
self.api_mode = "chat_completions"
self.acp_command = None
self.acp_args = []
self.agent = None
self._fallback_model = []
self._explicit_api_key = None
self._explicit_base_url = None
self._credential_pool = None
self.service_tier = None
def _normalize_model_for_provider(self, _provider: str) -> bool:
return False
def _write_profile_config(hermes_home) -> None:
(hermes_home / "config.yaml").write_text(
"""
model:
default: ollama-cloud/glm-5.2
provider: ollama-cloud
providers:
qwen-token-plan:
base_url: https://qwen-token-plan.example/v1
api_key: test-key
models:
qwen3.8-max-preview:
supports_vision: true
agent:
image_input_mode: auto
""".strip()
+ "\n",
encoding="utf-8",
)
def _resolve_cli_route():
from hermes_cli._parser import build_top_level_parser
from hermes_constants import get_hermes_home
_write_profile_config(get_hermes_home())
parser, _subparsers, _chat = build_top_level_parser()
args, _unknown = parser.parse_known_args(
["-m", MODEL, "--provider", REQUESTED_PROVIDER, "chat"]
)
cli = _RuntimeCLI(model=args.model, provider=args.provider)
assert cli._ensure_runtime_credentials() is True
return cli, cli._resolve_turn_agent_config("inspect the image")
def test_real_cli_args_keep_transport_and_capability_identities_separate():
from agent.image_routing import decide_image_input_mode
from hermes_cli.config import load_config
cli, route = _resolve_cli_route()
runtime = route["runtime"]
assert cli.provider == "custom"
assert cli.requested_provider == REQUESTED_PROVIDER
assert runtime["provider"] == "custom"
assert runtime["requested_provider"] == REQUESTED_PROVIDER
assert decide_image_input_mode(
runtime["provider"],
route["model"],
load_config(),
requested_provider=runtime["requested_provider"],
) == "native"
# A credential refresh with the same two identities must retain the live
# agent instead of treating canonicalization as a provider switch.
sentinel_agent = SimpleNamespace()
cli.agent = sentinel_agent
assert cli._ensure_runtime_credentials() is True
assert cli.agent is sentinel_agent
def test_named_identity_reaches_agent_and_vision_tool_native_gates():
from agent.auxiliary_client import reset_runtime_main, set_runtime_main
from run_agent import AIAgent
from tools.vision_tools import _should_use_native_vision_fast_path
_cli, route = _resolve_cli_route()
runtime = route["runtime"]
token = set_runtime_main(
runtime["provider"],
route["model"],
requested_provider=runtime["requested_provider"],
base_url=runtime["base_url"],
api_key=runtime["api_key"],
api_mode=runtime["api_mode"],
)
try:
agent = AIAgent.__new__(AIAgent)
agent.provider = runtime["provider"]
agent.requested_provider = runtime["requested_provider"]
agent.model = route["model"]
assert agent._model_supports_vision() is True
assert _should_use_native_vision_fast_path() is True
finally:
reset_runtime_main(token)