browser-use intervention hack

frdel committed Jan 19, 2025 at 14:43 UTC cd82c448ae59c097aeb9aa30f6e5586923a65e63
11 files changed +70 -5
README.md
+2 -1
@@ -4,7 +4,8 @@
4
5 # `Agent Zero`
6
7 -[![Join our Skool Community](https://img.shields.io/badge/Skool-Join%20our%20Community-4A90E2?style=for-the-badge&logo=skool&logoColor=white)](https://www.skool.com/agent-zero) [![Join our Discord](https://img.shields.io/badge/Discord-Join%20our%20server-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/B8KZKNsPpj) [![Subscribe on YouTube](https://img.shields.io/badge/YouTube-Subscribe-red?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/@AgentZeroFW) [![Connect on LinkedIn](https://img.shields.io/badge/LinkedIn-Connect-blue?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/jan-tomasek/) [![Follow on X.com](https://img.shields.io/badge/X.com-Follow-1DA1F2?style=for-the-badge&logo=x&logoColor=white)](https://x.com/JanTomasekDev)
7 +[![Thanks to Sponsors](https://img.shields.io/badge/GitHub%20Sponsors-Thanks%20to%20Sponsors-FF69B4?style=for-the-badge&logo=githubsponsors&logoColor=white)](https://github.com/sponsors/frdel) [![Join our Skool Community](https://img.shields.io/badge/Skool-Join%20our%20Community-4A90E2?style=for-the-badge&logo=skool&logoColor=white)](https://www.skool.com/agent-zero) [![Join our Discord](https://img.shields.io/badge/Discord-Join%20our%20server-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/B8KZKNsPpj) [![Subscribe on YouTube](https://img.shields.io/badge/YouTube-Subscribe-red?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/@AgentZeroFW) [![Connect on LinkedIn](https://img.shields.io/badge/LinkedIn-Connect-blue?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/jan-tomasek/) [![Follow on X.com](https://img.shields.io/badge/X.com-Follow-1DA1F2?style=for-the-badge&logo=x&logoColor=white)](https://x.com/JanTomasekDev)
8 +
9
10 [Installation](./docs/installation.md) •
11 [How to update](./docs/installation.md#how-to-update-agent-zero) •
docs/res/favicon.png
Binary files /dev/null and b/docs/res/favicon.png differ
docs/res/favicon_round.png
Binary files /dev/null and b/docs/res/favicon_round.png differ
python/extensions/message_loop_prompts/.gitkeep
python/extensions/message_loop_start/.gitkeep
python/extensions/message_loop_start/_10_iteration_no.py new
+14
@@ -0,0 +1,14 @@
1 +from python.helpers.extension import Extension
2 +from agent import Agent, LoopData
3 +
4 +DATA_NAME_ITER_NO = "iteration_no"
5 +
6 +class IterationNo(Extension):
7 + async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
8 + # total iteration number
9 + no = self.agent.get_data(DATA_NAME_ITER_NO) or 0
10 + self.agent.set_data(DATA_NAME_ITER_NO, no + 1)
11 +
12 +
13 +def get_iter_no(agent: Agent) -> int:
14 + return agent.get_data(DATA_NAME_ITER_NO) or 0
\ No newline at end of file
python/extensions/monologue_end/.gitkeep
python/extensions/system_prompt/.gitkeep
python/tools/browser_agent.py
+38 -3
@@ -1,13 +1,14 @@
1 import asyncio
2 import json
3 import time
4 -from agent import Agent
4 +from agent import Agent, InterventionException
5
6 import models
7 from python.helpers.tool import Tool, Response
8 from python.helpers import dirty_json, files, rfc_exchange, defer, strings, persist_chat
9 from python.helpers.print_style import PrintStyle
10 from python.helpers.browser_use import browser_use
11 +from python.extensions.message_loop_start._10_iteration_no import get_iter_no
12 from pydantic import BaseModel
13 import uuid
14
@@ -24,6 +25,8 @@ class State:
25 self.task = None
26 self.use_agent = None
27 self.browser = None
28 + self.iter_no = 0
29 +
30
31 def __del__(self):
32 self.kill_task()
@@ -42,6 +45,9 @@ class State:
45 # Await the coroutine to get the browser context
46 self.context = await self.browser.new_context()
47
48 + # override async methods to create hooks
49 + self.override_hooks()
50 +
51 # Add init script to the context - this will be applied to all new pages
52 await self.context._initialize_session()
53 pw_context = self.context.session.context # type: ignore
@@ -68,6 +74,7 @@ class State:
74 self.context = None
75 self.use_agent = None
76 self.browser = None
77 + self.iter_no = 0
78
79 async def _run_task(self, task: str):
80
@@ -117,9 +124,34 @@ class State:
124 system_prompt_class=CustomSystemPrompt,
125 controller=controller,
126 )
127 +
128 + self.iter_no = get_iter_no(self.agent)
129 +
130 + # orig_err_hnd = self.use_agent._handle_step_error
131 + # def new_err_hnd(*args, **kwargs):
132 + # if isinstance(args[0], InterventionException):
133 + # raise args[0]
134 + # return orig_err_hnd(*args, **kwargs)
135 + # self.use_agent._handle_step_error = new_err_hnd
136 +
137 result = await self.use_agent.run()
138 return result
139
140 + def override_hooks(self):
141 + # override async function to create a hook
142 + def override_hook(func):
143 + async def wrapper(*args, **kwargs):
144 + await self.agent.wait_if_paused()
145 + if self.iter_no != get_iter_no(self.agent):
146 + raise InterventionException("Task cancelled")
147 + return await func(*args, **kwargs)
148 + return wrapper
149 +
150 + if self.context:
151 + self.context.get_state = override_hook(self.context.get_state)
152 + self.context.get_session = override_hook(self.context.get_session)
153 + self.context.remove_highlights = override_hook(self.context.remove_highlights)
154 +
155 async def get_page(self):
156 if self.use_agent:
157 return await self.use_agent.browser_context.get_current_page()
@@ -150,8 +182,11 @@ class BrowserAgent(Tool):
182 # collect result
183 result = await task.result()
184 answer = result.final_result()
153 - answer_data = dirty_json.DirtyJson.parse_string(answer)
154 - answer_text = strings.dict_to_text(answer_data) # type: ignore
185 + try:
186 + answer_data = dirty_json.DirtyJson.parse_string(answer)
187 + answer_text = strings.dict_to_text(answer_data) # type: ignore
188 + except Exception as e:
189 + answer_text = answer
190 self.log.update(answer=answer_text)
191 return Response(message=answer, break_loop=False)
192
webui/index.html
+1 -1
@@ -457,7 +457,7 @@
457 <div :class="{'field': true, 'field-full': field.type === 'textarea'}">
458 <div class="field-label">
459 <div class="field-title" x-text="field.title"></div>
460 - <div class="field-description" x-html="field.description"></div>
460 + <div class="field-description" x-html="field.description || ''"></div>
461 </div>
462
463 <div class="field-control">
webui/public/favicon_round.svg new
+15
@@ -0,0 +1,15 @@
1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 +<svg width="100%" height="100%" viewBox="0 0 960 960" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
4 + <g transform="matrix(1.07993,0,0,1.07993,-76.6057,-32.2424)">
5 + <circle cx="515.545" cy="474.371" r="442.821" style="fill:rgb(1,4,26);"/>
6 + </g>
7 + <g transform="matrix(1,0,0,1,0,-61.814)">
8 + <g transform="matrix(1.03321,0,0,1.03321,-15.9385,-15.938)">
9 + <path d="M717.77,788.27C638.99,652.89 559.87,516.92 479.15,378.22C399.29,516.59 320.58,652.95 241.99,789.12L120,789.12C239.91,581.87 479.49,170.89 479.49,170.89C479.49,170.89 720.12,580.92 840,788.27L717.77,788.27Z" style="fill:white;fill-rule:nonzero;"/>
10 + </g>
11 + <g transform="matrix(1.03321,0,0,1.03321,-15.9385,-15.938)">
12 + <path d="M633.08,788.85L323.54,788.85C344.15,753.01 364.09,718.33 383.88,683.93L574.1,683.93C593.38,718.23 612.57,752.36 633.08,788.85Z" style="fill:white;fill-rule:nonzero;"/>
13 + </g>
14 + </g>
15 +</svg>