mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-25 17:04:52 +00:00
125 lines
3.9 KiB
Python
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)
|