browser-use upgrade patch test

frdel committed Aug 27, 2025 at 20:45 UTC 439f2216ffc41265af974e7e534f03c6011e39b3
2 files changed +70 -9
models.py
+58 -1
@@ -435,6 +435,52 @@ class BrowserCompatibleChatWrapper(ChatOpenRouter):
435 def get_client(self, *args, **kwargs): # type: ignore
436 return AsyncAIChatReplacement(self, *args, **kwargs)
437
438 + # -- Gemini helper -----------------------------------------
439 + def _gemini_clean_and_conform(self, text: str):
440 + obj = None
441 + try:
442 + # dirty_json parser is robust enough to handle markdown fences
443 + obj = dirty_json.parse(text)
444 + except Exception:
445 + return None # return None if parsing fails
446 +
447 + if not isinstance(obj, dict):
448 + return None
449 +
450 + # Conform actions to browser-use expectations
451 + if isinstance(obj.get("action"), list):
452 + normalized_actions = []
453 + for item in obj["action"]:
454 + if not isinstance(item, dict):
455 + continue # Skip non-dict items
456 +
457 + action_key, action_value = next(iter(item.items()), (None, None))
458 + if not action_key:
459 + continue
460 +
461 + # Create a mutable copy of the value
462 + v = (action_value or {}).copy()
463 +
464 + if action_key in ("scroll_down", "scroll_up", "scroll"):
465 + is_down = action_key != "scroll_up"
466 + v.setdefault("down", is_down)
467 + v.setdefault("num_pages", 1.0)
468 + normalized_actions.append({"scroll": v})
469 + elif action_key == "go_to_url":
470 + v.setdefault("new_tab", False)
471 + normalized_actions.append({action_key: v})
472 + elif action_key == "done":
473 + if "text" in v and "data" not in v:
474 + t = v.pop("text", "")
475 + v["data"] = {"title": "Task result", "response": t, "page_summary": t}
476 + v.setdefault("success", True)
477 + normalized_actions.append({action_key: v})
478 + else:
479 + normalized_actions.append(item)
480 + obj["action"] = normalized_actions
481 +
482 + return dirty_json.stringify(obj)
483 +
484 async def _acall(
485 self,
486 messages: List[BaseMessage],
@@ -450,7 +496,7 @@ class BrowserCompatibleChatWrapper(ChatOpenRouter):
496 model = kwargs.pop("model", None)
497 kwrgs = {**self._wrapper.kwargs, **kwargs}
498
453 - # hack from browser-use to fix json schema for gemini
499 + # hack from browser-use to fix json schema for gemini (additionalProperties, $defs, $ref)
500 if "response_format" in kwrgs and "json_schema" in kwrgs["response_format"] and model.startswith("gemini/"):
501 kwrgs["response_format"]["json_schema"] = ChatGoogle("")._fix_gemini_schema(self._wrapper.kwargs)
502
@@ -460,6 +506,17 @@ class BrowserCompatibleChatWrapper(ChatOpenRouter):
506 stop=stop,
507 **kwrgs,
508 )
509 +
510 + # Gemini: strip triple backticks and conform schema
511 + try:
512 + msg = resp.choices[0].message # type: ignore
513 + if self.provider == "gemini" and isinstance(getattr(msg, "content", None), str):
514 + cleaned = self._gemini_clean_and_conform(msg.content) # type: ignore
515 + if cleaned:
516 + msg.content = cleaned
517 + except Exception:
518 + pass
519 +
520 except Exception as e:
521 raise e
522
python/tools/browser_agent.py
+12 -8
@@ -32,6 +32,16 @@ class State:
32
33 def __del__(self):
34 self.kill_task()
35 + files.delete_dir(self.get_user_data_dir()) # cleanup user data dir
36 +
37 + def get_user_data_dir(self):
38 + return str(
39 + Path.home()
40 + / ".config"
41 + / "browseruse"
42 + / "profiles"
43 + / f"agent_{self.agent.context.id}"
44 + )
45
46 async def _initialize(self):
47 if self.browser_session:
@@ -46,7 +56,6 @@ class State:
56 disable_security=True,
57 chromium_sandbox=False,
58 accept_downloads=True,
49 - downloads_dir=files.get_abs_path("tmp/downloads"),
59 downloads_path=files.get_abs_path("tmp/downloads"),
60 allowed_domains=["*"],
61 executable_path=pw_binary,
@@ -54,17 +63,12 @@ class State:
63 minimum_wait_page_load_time=1.0,
64 wait_for_network_idle_page_load_time=2.0,
65 maximum_wait_page_load_time=10.0,
66 + window_size={"width": 1024, "height": 2048},
67 screen={"width": 1024, "height": 2048},
68 viewport={"width": 1024, "height": 2048},
69 args=["--headless=new"],
70 # Use a unique user data directory to avoid conflicts
61 - user_data_dir=str(
62 - Path.home()
63 - / ".config"
64 - / "browseruse"
65 - / "profiles"
66 - / f"agent_{self.agent.context.id}"
67 - ),
71 + user_data_dir=self.get_user_data_dir(),
72 extra_http_headers=self.agent.config.browser_http_headers or {},
73 )
74 )