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