Files
hermes-agent/tests/tools/test_browser_vision_gemma4.py
Alexander Whitestone 17cc4bac90
All checks were successful
Lint / lint (pull_request) Successful in 10s
feat: complete Gemma 4 browser_vision wiring — task routing, timeout, tests
Building on the Gemma 4 default already on this branch:

- Change call_llm() task from "vision" to "browser_vision" in browser_vision()
  so auxiliary.browser_vision.* config is consulted for provider/model/timeout
- Route call_llm(task="browser_vision") through the vision provider resolution
  path in auxiliary_client.py (same as task="vision")
- Fix timeout resolution: check auxiliary.browser_vision.timeout before
  auxiliary.vision.timeout (allows browser-specific timeout override)
- Add timeout option to auxiliary.browser_vision in cli-config.yaml.example
- Add test_browser_vision_gemma4.py covering: task routing assertions,
  call_llm() vision branch routing, and timeout config key ordering

Refs #816

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 19:43:42 -04:00

83 lines
4.0 KiB
Python

"""Tests for task routing and timeout config — browser_vision Gemma 4 (Issue #816).
Covers the additional wiring on top of the Gemma 4 default:
- browser_vision() uses task="browser_vision" so auxiliary.browser_vision.*
config is consulted for provider/model/timeout
- call_llm() routes "browser_vision" through vision provider resolution
(same path as "vision" task)
- Timeout is read from auxiliary.browser_vision.timeout before
auxiliary.vision.timeout
Model selection tests are in test_browser_vision_model.py.
"""
import inspect
# ── browser_vision() task routing ────────────────────────────────────────────
class TestBrowserVisionTaskRouting:
"""browser_vision() must use task='browser_vision' in call_llm()."""
def test_call_llm_receives_browser_vision_task(self):
"""browser_vision() source uses task='browser_vision', not 'vision'."""
src = inspect.getsource(
__import__("tools.browser_tool", fromlist=["browser_vision"]).browser_vision
)
assert '"browser_vision"' in src or "'browser_vision'" in src, (
"browser_vision() must pass task='browser_vision' to call_llm(), not 'vision'"
)
def test_call_llm_does_not_use_bare_vision_task(self):
"""The call_llm() invocation must not use task='vision' for browser screenshots."""
import re
src = inspect.getsource(
__import__("tools.browser_tool", fromlist=["browser_vision"]).browser_vision
)
call_llm_blocks = re.findall(r'call_llm\s*\([^)]+\)', src, re.DOTALL)
for block in call_llm_blocks:
assert '"vision"' not in block and "'vision'" not in block, (
f"call_llm() must use task='browser_vision', found 'vision' in: {block}"
)
# ── call_llm() vision routing ────────────────────────────────────────────────
class TestCallLlmBrowserVisionRouting:
"""call_llm(task='browser_vision') must route through vision provider path."""
def test_browser_vision_task_in_vision_branch(self):
"""call_llm() source handles 'browser_vision' in the same branch as 'vision'."""
from agent import auxiliary_client
src = inspect.getsource(auxiliary_client.call_llm)
assert 'task in ("vision", "browser_vision")' in src or \
"task in ('vision', 'browser_vision')" in src, (
"call_llm() should route 'browser_vision' through the vision provider path"
)
# ── timeout resolution ────────────────────────────────────────────────────────
class TestBrowserVisionTimeoutResolution:
"""browser_vision() reads auxiliary.browser_vision.timeout first."""
def test_browser_vision_timeout_checked_before_vision_timeout(self):
"""Source checks auxiliary.browser_vision.timeout before auxiliary.vision.timeout."""
src = inspect.getsource(
__import__("tools.browser_tool", fromlist=["browser_vision"]).browser_vision
)
# Locate the timeout resolution block (before call_kwargs dict)
timeout_block_start = src.find("vision_timeout")
call_kwargs_start = src.find('"task": "browser_vision"')
assert timeout_block_start != -1, "Could not find vision_timeout in browser_vision source"
assert call_kwargs_start != -1, "Could not find task='browser_vision' in browser_vision source"
# The timeout block should mention "browser_vision" before "vision"
block = src[timeout_block_start:call_kwargs_start]
bv_idx = block.find('"browser_vision"')
v_idx = block.find('"vision"')
if bv_idx != -1 and v_idx != -1:
assert bv_idx < v_idx, (
"auxiliary.browser_vision.timeout should be checked before auxiliary.vision.timeout"
)