Honor configured Browser model preset

Activate the selected Browser preset after the first Browser tool call so follow-up model turns use it. Clear the temporary override at monologue end and preserve the main model as the safe fallback.

Alessandro committed Aug 16, 2026 at 15:21 UTC a0eefe19bd81e180dd6c72bbb4206b265722f827
5 files changed +117 -3
plugins/_browser/extensions/python/_functions/agent/Agent/get_chat_model/start/_20_browser_model.py new
+14
@@ -0,0 +1,14 @@
1 +from helpers.extension import Extension
2 +from plugins._browser.helpers.config import (
3 + browser_model_is_active,
4 + resolve_browser_model,
5 +)
6 +
7 +
8 +class BrowserModelProvider(Extension):
9 + def execute(self, data: dict = {}, **kwargs):
10 + if self.agent and browser_model_is_active(self.agent):
11 + data["result"] = resolve_browser_model(
12 + self.agent,
13 + fallback=data.get("result"),
14 + )
plugins/_browser/extensions/python/monologue_end/_20_browser_model.py new
+8
@@ -0,0 +1,8 @@
1 +from helpers.extension import Extension
2 +from plugins._browser.helpers.config import clear_browser_model
3 +
4 +
5 +class BrowserModelCleanup(Extension):
6 + def execute(self, **kwargs):
7 + if self.agent:
8 + clear_browser_model(self.agent)
plugins/_browser/helpers/config.py
+27 -2
@@ -9,6 +9,7 @@ if TYPE_CHECKING:
9
10 PLUGIN_NAME = "_browser"
11 MODEL_PRESET_KEY = "model_preset"
12 +BROWSER_MODEL_ACTIVE_KEY = "_browser_model_active"
13 DEFAULT_HOMEPAGE_KEY = "default_homepage"
14 AUTOFOCUS_ACTIVE_PAGE_KEY = "autofocus_active_page"
15 TAB_SCOPE_KEY = "browser_tab_scope"
@@ -319,10 +320,34 @@ def resolve_browser_model_selection(
320 }
321
322
322 -def resolve_browser_model(agent: "Agent", settings: dict[str, Any] | None = None):
323 +def activate_browser_model(agent: "Agent") -> dict[str, Any]:
324 + selection = resolve_browser_model_selection(agent=agent)
325 + agent.set_data(
326 + BROWSER_MODEL_ACTIVE_KEY,
327 + selection["selected_preset_name"]
328 + if selection["source_kind"] == "preset"
329 + else "",
330 + )
331 + return selection
332 +
333 +
334 +def clear_browser_model(agent: "Agent") -> None:
335 + agent.set_data(BROWSER_MODEL_ACTIVE_KEY, "")
336 +
337 +
338 +def browser_model_is_active(agent: "Agent") -> bool:
339 + return bool(agent.get_data(BROWSER_MODEL_ACTIVE_KEY))
340 +
341 +
342 +def resolve_browser_model(
343 + agent: "Agent",
344 + settings: dict[str, Any] | None = None,
345 + fallback: Any = None,
346 +):
347 selection = resolve_browser_model_selection(agent=agent, settings=settings)
348 if selection["source_kind"] == "main":
325 - return agent.get_chat_model()
349 + clear_browser_model(agent)
350 + return fallback if fallback is not None else agent.get_chat_model()
351
352 import models
353 from plugins._model_config.helpers import model_config
plugins/_browser/tools/browser.py
+5
@@ -10,6 +10,7 @@ from typing import Any
10 from helpers import files
11 from helpers.print_style import PrintStyle
12 from helpers.tool import Response, Tool
13 +from plugins._browser.helpers.config import activate_browser_model
14 from plugins._browser.helpers.selector import get_tool_runtime
15
16
@@ -74,6 +75,10 @@ class Browser(Tool):
75 action = "clipboard"
76 else:
77 action = str(action or self.method or "state").strip().lower().replace("-", "_")
78 + try:
79 + activate_browser_model(self.agent)
80 + except Exception as exc:
81 + PrintStyle.warning(f"Browser model preset could not be activated: {exc}")
82 try:
83 runtime = await get_runtime(self.agent.context.id, agent=self.agent)
84 except Exception as exc:
tests/test_browser_agent_regressions.py
+63 -1
@@ -22,6 +22,19 @@ class _TestAgentContext:
22 return None
23
24
25 +class _TestAgent:
26 + pass
27 +
28 +
29 +class _TestAgentConfig:
30 + pass
31 +
32 +
33 +class _TestAgentContextType:
34 + BACKGROUND = "background"
35 + USER = SimpleNamespace(value="user")
36 +
37 +
38 class _TestResponse(SimpleNamespace):
39 def __init__(self, message="", break_loop=False, **kwargs):
40 super().__init__(message=message, break_loop=break_loop, **kwargs)
@@ -67,7 +80,15 @@ class _TestWsResult(dict):
80 )
81
82
70 -sys.modules.setdefault("agent", SimpleNamespace(AgentContext=_TestAgentContext))
83 +sys.modules.setdefault(
84 + "agent",
85 + SimpleNamespace(
86 + Agent=_TestAgent,
87 + AgentConfig=_TestAgentConfig,
88 + AgentContext=_TestAgentContext,
89 + AgentContextType=_TestAgentContextType,
90 + ),
91 +)
92 sys.modules.setdefault("helpers.tool", SimpleNamespace(Response=_TestResponse, Tool=_TestTool))
93 sys.modules.setdefault("helpers.ws", SimpleNamespace(WsHandler=_TestWsHandler))
94 sys.modules.setdefault("helpers.ws_manager", SimpleNamespace(WsResult=_TestWsResult))
@@ -295,6 +316,47 @@ def test_browser_model_selection_falls_back_to_main_for_missing_preset(monkeypat
316 assert selection["config"] == {"provider": "openrouter", "name": "main/model"}
317
318
319 +def test_browser_model_preset_controls_followup_turn_and_clears(monkeypatch):
320 + import importlib
321 + import plugins._browser.helpers.config as browser_config_module
322 +
323 + state = {}
324 + agent = SimpleNamespace(
325 + set_data=lambda key, value: state.__setitem__(key, value),
326 + get_data=lambda key: state.get(key),
327 + )
328 + monkeypatch.setattr(
329 + browser_config_module,
330 + "resolve_browser_model_selection",
331 + lambda agent=None, settings=None: {
332 + "source_kind": "preset",
333 + "selected_preset_name": "Browser",
334 + },
335 + )
336 +
337 + browser_config_module.activate_browser_model(agent)
338 + assert browser_config_module.browser_model_is_active(agent)
339 +
340 + provider_module = importlib.import_module(
341 + "plugins._browser.extensions.python._functions.agent.Agent.get_chat_model.start._20_browser_model"
342 + )
343 + cleanup_module = importlib.import_module(
344 + "plugins._browser.extensions.python.monologue_end._20_browser_model"
345 + )
346 + selected_model = object()
347 + monkeypatch.setattr(
348 + provider_module,
349 + "resolve_browser_model",
350 + lambda agent, fallback=None: selected_model,
351 + )
352 + model_data = {"result": object()}
353 + provider_module.BrowserModelProvider(agent).execute(data=model_data)
354 + assert model_data["result"] is selected_model
355 +
356 + cleanup_module.BrowserModelCleanup(agent).execute()
357 + assert not browser_config_module.browser_model_is_active(agent)
358 +
359 +
360 def test_browser_model_preset_options_include_missing_selected(monkeypatch):
361 from plugins._model_config.helpers import model_config
362