Files
hermes-agent/tests/tools/test_browser_vision_gemma4.py

83 lines
4.0 KiB
Python
Raw Normal View History

"""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"
)