ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
@@ -0,0 +1,32 @@
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from browser_use import Agent
secret_key = os.environ.get('OTP_SECRET_KEY')
if not secret_key:
# For this example copy the code from the website https://authenticationtest.com/totpChallenge/
# For real 2fa just copy the secret key when you setup 2fa, you can get this e.g. in 1Password
secret_key = 'JBSWY3DPEHPK3PXP'
sensitive_data = {'bu_2fa_code': secret_key}
task = """
1. Go to https://authenticationtest.com/totpChallenge/ and try to log in.
2. If prompted for 2FA code:
Input the the secret bu_2fa_code.
When you input bu_2fa_code, the 6 digit code will be generated automatically.
"""
Agent(task=task, sensitive_data=sensitive_data).run_sync() # type: ignore
@@ -0,0 +1,120 @@
"""
Action filters (domains) let you limit actions available to the Agent on a step-by-step/page-by-page basis.
@registry.action(..., domains=['*'])
async def some_action(browser_session: BrowserSession):
...
This helps prevent the LLM from deciding to use an action that is not compatible with the current page.
It helps limit decision fatigue by scoping actions only to pages where they make sense.
It also helps prevent mis-triggering stateful actions or actions that could break other programs or leak secrets.
For example:
- only run on certain domains @registry.action(..., domains=['example.com', '*.example.com', 'example.co.*']) (supports globs, but no regex)
- only fill in a password on a specific login page url
- only run if this action has not run before on this page (e.g. by looking up the url in a file on disk)
During each step, the agent recalculates the actions available specifically for that page, and informs the LLM.
"""
import asyncio
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from browser_use import ChatOpenAI
from browser_use.agent.service import Agent, Tools
from browser_use.browser import BrowserSession
# Initialize tools and registry
tools = Tools()
registry = tools.registry
# Action will only be available to Agent on Google domains because of the domain filter
@registry.action(description='Trigger disco mode', domains=['google.com', '*.google.com'])
async def disco_mode(browser_session: BrowserSession):
# Execute JavaScript using CDP
cdp_session = await browser_session.get_or_create_cdp_session()
await cdp_session.cdp_client.send.Runtime.evaluate(
params={
'expression': """(() => {
// define the wiggle animation
document.styleSheets[0].insertRule('@keyframes wiggle { 0% { transform: rotate(0deg); } 50% { transform: rotate(10deg); } 100% { transform: rotate(0deg); } }');
document.querySelectorAll("*").forEach(element => {
element.style.animation = "wiggle 0.5s infinite";
});
})()"""
},
session_id=cdp_session.session_id,
)
# Custom filter function that checks URL
async def is_login_page(browser_session: BrowserSession) -> bool:
"""Check if current page is a login page."""
try:
# Get current URL using CDP
cdp_session = await browser_session.get_or_create_cdp_session()
result = await cdp_session.cdp_client.send.Runtime.evaluate(
params={'expression': 'window.location.href', 'returnByValue': True}, session_id=cdp_session.session_id
)
url = result.get('result', {}).get('value', '')
return 'login' in url.lower() or 'signin' in url.lower()
except Exception:
return False
# Note: page_filter is not directly supported anymore, so we'll just use domains
# and check the condition inside the function
@registry.action(description='Use the force, luke', domains=['*'])
async def use_the_force(browser_session: BrowserSession):
# Check if it's a login page
if not await is_login_page(browser_session):
return # Skip if not a login page
# Execute JavaScript using CDP
cdp_session = await browser_session.get_or_create_cdp_session()
await cdp_session.cdp_client.send.Runtime.evaluate(
params={
'expression': """(() => {
document.querySelector('body').innerHTML = 'These are not the droids you are looking for';
})()"""
},
session_id=cdp_session.session_id,
)
async def main():
"""Main function to run the example"""
browser_session = BrowserSession()
await browser_session.start()
llm = ChatOpenAI(model='gpt-4.1-mini')
# Create the agent
agent = Agent( # disco mode will not be triggered on apple.com because the LLM won't be able to see that action available, it should work on Google.com though.
task="""
Go to apple.com and trigger disco mode (if dont know how to do that, then just move on).
Then go to google.com and trigger disco mode.
After that, go to the Google login page and Use the force, luke.
""",
llm=llm,
browser_session=browser_session,
tools=tools,
)
# Run the agent
await agent.run(max_steps=10)
# Cleanup
await browser_session.kill()
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,38 @@
import asyncio
import os
import sys
from browser_use.browser.session import BrowserSession
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from browser_use import ActionResult, Agent, ChatOpenAI, Tools
tools = Tools()
llm = ChatOpenAI(model='gpt-4.1-mini')
@tools.registry.action('Click on submit button')
async def click_submit_button(browser_session: BrowserSession):
page = await browser_session.must_get_current_page()
submit_button = await page.must_get_element_by_prompt('submit button', llm)
await submit_button.click()
return ActionResult(is_done=True, extracted_content='Submit button clicked!')
async def main():
task = 'go to brower-use.com and then click on the submit button'
agent = Agent(task=task, llm=llm, tools=tools)
await agent.run()
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,112 @@
import asyncio
import http.client
import json
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
import logging
from pydantic import BaseModel
from browser_use import ActionResult, Agent, ChatOpenAI, Tools
from browser_use.browser.profile import BrowserProfile
logger = logging.getLogger(__name__)
class Person(BaseModel):
name: str
email: str | None = None
class PersonList(BaseModel):
people: list[Person]
SERP_API_KEY = os.getenv('SERPER_API_KEY')
if not SERP_API_KEY:
raise ValueError('SERPER_API_KEY is not set')
tools = Tools(exclude_actions=['search_google'], output_model=PersonList)
@tools.registry.action('Search the web for a specific query. Returns a short description and links of the results.')
async def search_web(query: str):
# do a serp search for the query
conn = http.client.HTTPSConnection('google.serper.dev')
payload = json.dumps({'q': query})
headers = {'X-API-KEY': SERP_API_KEY, 'Content-Type': 'application/json'}
conn.request('POST', '/search', payload, headers)
res = conn.getresponse()
data = res.read()
serp_data = json.loads(data.decode('utf-8'))
# exclude searchParameters and credits
serp_data = {k: v for k, v in serp_data.items() if k not in ['searchParameters', 'credits']}
# keep the value of the key "organic"
organic = serp_data.get('organic', [])
# remove the key "position"
organic = [{k: v for k, v in d.items() if k != 'position'} for d in organic]
# print the original data
logger.debug(json.dumps(organic, indent=2))
# to string
organic_str = json.dumps(organic)
return ActionResult(extracted_content=organic_str, include_in_memory=False, include_extracted_content_only_once=True)
names = [
'Ruedi Aebersold',
'Bernd Bodenmiller',
'Eugene Demler',
'Erich Fischer',
'Pietro Gambardella',
'Matthias Huss',
'Reto Knutti',
'Maksym Kovalenko',
'Antonio Lanzavecchia',
'Maria Lukatskaya',
'Jochen Markard',
'Javier Pérez-Ramírez',
'Federica Sallusto',
'Gisbert Schneider',
'Sonia I. Seneviratne',
'Michael Siegrist',
'Johan Six',
'Tanja Stadler',
'Shinichi Sunagawa',
'Michael Bruce Zimmermann',
]
async def main():
task = 'use search_web with "find email address of the following ETH professor:" for each of the following persons in a list of actions. Finally return the list with name and email if provided - do always 5 at once'
task += '\n' + '\n'.join(names)
model = ChatOpenAI(model='gpt-4.1-mini')
browser_profile = BrowserProfile()
agent = Agent(task=task, llm=model, tools=tools, browser_profile=browser_profile)
history = await agent.run()
result = history.final_result()
if result:
parsed: PersonList = PersonList.model_validate_json(result)
for person in parsed.people:
print(f'{person.name} - {person.email}')
else:
print('No result')
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,341 @@
"""
OpenAI Computer Use Assistant (CUA) Integration
This example demonstrates how to integrate OpenAI's Computer Use Assistant as a fallback
action when standard browser actions are insufficient to achieve the desired goal.
The CUA can perform complex computer interactions that might be difficult to achieve
through regular browser-use actions.
"""
import asyncio
import base64
import os
import sys
from io import BytesIO
from PIL import Image
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from openai import AsyncOpenAI
from pydantic import BaseModel, Field
from browser_use import Agent, ChatOpenAI, Tools
from browser_use.agent.views import ActionResult
from browser_use.browser import BrowserSession
class OpenAICUAAction(BaseModel):
"""Parameters for OpenAI Computer Use Assistant action."""
description: str = Field(..., description='Description of your next goal')
async def handle_model_action(browser_session: BrowserSession, action) -> ActionResult:
"""
Given a computer action (e.g., click, double_click, scroll, etc.),
execute the corresponding operation using CDP.
"""
action_type = action.type
ERROR_MSG: str = 'Could not execute the CUA action.'
if not browser_session.agent_focus:
return ActionResult(error='No active browser session')
try:
match action_type:
case 'click':
x, y = action.x, action.y
button = action.button
print(f"Action: click at ({x}, {y}) with button '{button}'")
# Not handling things like middle click, etc.
if button != 'left' and button != 'right':
button = 'left'
# Use CDP to click
await browser_session.agent_focus.cdp_client.send.Input.dispatchMouseEvent(
params={
'type': 'mousePressed',
'x': x,
'y': y,
'button': button,
'clickCount': 1,
},
session_id=browser_session.agent_focus.session_id,
)
await browser_session.agent_focus.cdp_client.send.Input.dispatchMouseEvent(
params={
'type': 'mouseReleased',
'x': x,
'y': y,
'button': button,
},
session_id=browser_session.agent_focus.session_id,
)
msg = f'Clicked at ({x}, {y}) with button {button}'
return ActionResult(extracted_content=msg, include_in_memory=True, long_term_memory=msg)
case 'scroll':
x, y = action.x, action.y
scroll_x, scroll_y = action.scroll_x, action.scroll_y
print(f'Action: scroll at ({x}, {y}) with offsets (scroll_x={scroll_x}, scroll_y={scroll_y})')
# Move mouse to position first
await browser_session.agent_focus.cdp_client.send.Input.dispatchMouseEvent(
params={
'type': 'mouseMoved',
'x': x,
'y': y,
},
session_id=browser_session.agent_focus.session_id,
)
# Execute scroll using JavaScript
await browser_session.agent_focus.cdp_client.send.Runtime.evaluate(
params={
'expression': f'window.scrollBy({scroll_x}, {scroll_y})',
},
session_id=browser_session.agent_focus.session_id,
)
msg = f'Scrolled at ({x}, {y}) with offsets (scroll_x={scroll_x}, scroll_y={scroll_y})'
return ActionResult(extracted_content=msg, include_in_memory=True, long_term_memory=msg)
case 'keypress':
keys = action.keys
for k in keys:
print(f"Action: keypress '{k}'")
# A simple mapping for common keys; expand as needed.
key_code = k
if k.lower() == 'enter':
key_code = 'Enter'
elif k.lower() == 'space':
key_code = 'Space'
# Use CDP to send key
await browser_session.agent_focus.cdp_client.send.Input.dispatchKeyEvent(
params={
'type': 'keyDown',
'key': key_code,
},
session_id=browser_session.agent_focus.session_id,
)
await browser_session.agent_focus.cdp_client.send.Input.dispatchKeyEvent(
params={
'type': 'keyUp',
'key': key_code,
},
session_id=browser_session.agent_focus.session_id,
)
msg = f'Pressed keys: {keys}'
return ActionResult(extracted_content=msg, include_in_memory=True, long_term_memory=msg)
case 'type':
text = action.text
print(f'Action: type text: {text}')
# Type text character by character
for char in text:
await browser_session.agent_focus.cdp_client.send.Input.dispatchKeyEvent(
params={
'type': 'char',
'text': char,
},
session_id=browser_session.agent_focus.session_id,
)
msg = f'Typed text: {text}'
return ActionResult(extracted_content=msg, include_in_memory=True, long_term_memory=msg)
case 'wait':
print('Action: wait')
await asyncio.sleep(2)
msg = 'Waited for 2 seconds'
return ActionResult(extracted_content=msg, include_in_memory=True, long_term_memory=msg)
case 'screenshot':
# Nothing to do as screenshot is taken at each turn
print('Action: screenshot')
return ActionResult(error=ERROR_MSG)
# Handle other actions here
case _:
print(f'Unrecognized action: {action}')
return ActionResult(error=ERROR_MSG)
except Exception as e:
print(f'Error handling action {action}: {e}')
return ActionResult(error=ERROR_MSG)
tools = Tools()
@tools.registry.action(
'Use OpenAI Computer Use Assistant (CUA) as a fallback when standard browser actions cannot achieve the desired goal. This action sends a screenshot and description to OpenAI CUA and executes the returned computer use actions.',
param_model=OpenAICUAAction,
)
async def openai_cua_fallback(params: OpenAICUAAction, browser_session: BrowserSession):
"""
Fallback action that uses OpenAI's Computer Use Assistant to perform complex
computer interactions when standard browser actions are insufficient.
"""
print(f'🎯 CUA Action Starting - Goal: {params.description}')
try:
# Get browser state summary
state = await browser_session.get_browser_state_summary()
page_info = state.page_info
if not page_info:
raise Exception('Page info not found - cannot execute CUA action')
print(f'📐 Viewport size: {page_info.viewport_width}x{page_info.viewport_height}')
screenshot_b64 = state.screenshot
if not screenshot_b64:
raise Exception('Screenshot not found - cannot execute CUA action')
print(f'📸 Screenshot captured (base64 length: {len(screenshot_b64)} chars)')
# Debug: Check screenshot dimensions
image = Image.open(BytesIO(base64.b64decode(screenshot_b64)))
print(f'📏 Screenshot actual dimensions: {image.size[0]}x{image.size[1]}')
# rescale the screenshot to the viewport size
image = image.resize((page_info.viewport_width, page_info.viewport_height))
# Save as PNG to bytes buffer
buffer = BytesIO()
image.save(buffer, format='PNG')
buffer.seek(0)
# Convert to base64
screenshot_b64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
print(f'📸 Rescaled screenshot to viewport size: {page_info.viewport_width}x{page_info.viewport_height}')
client = AsyncOpenAI(api_key=os.getenv('OPENAI_API_KEY'))
print('🔄 Sending request to OpenAI CUA...')
prompt = f"""
You will be given an action to execute and screenshot of the current screen.
Output one computer_call object that will achieve this goal.
Goal: {params.description}
"""
response = await client.responses.create(
model='computer-use-preview',
tools=[
{
'type': 'computer_use_preview',
'display_width': page_info.viewport_width,
'display_height': page_info.viewport_height,
'environment': 'browser',
}
],
input=[
{
'role': 'user',
'content': [
{'type': 'input_text', 'text': prompt},
{
'type': 'input_image',
'detail': 'auto',
'image_url': f'data:image/png;base64,{screenshot_b64}',
},
],
}
],
truncation='auto',
temperature=0.1,
)
print(f'📥 CUA response received: {response}')
computer_calls = [item for item in response.output if item.type == 'computer_call']
computer_call = computer_calls[0] if computer_calls else None
if not computer_call:
raise Exception('No computer calls found in CUA response')
action = computer_call.action
print(f'🎬 Executing CUA action: {action.type} - {action}')
action_result = await handle_model_action(browser_session, action)
await asyncio.sleep(0.1)
print('✅ CUA action completed successfully')
return action_result
except Exception as e:
msg = f'Error executing CUA action: {e}'
print(f'{msg}')
return ActionResult(error=msg)
async def main():
# Initialize the language model
llm = ChatOpenAI(
model='o4-mini',
temperature=1.0,
)
# Create browser session
browser_session = BrowserSession()
# Example task that might require CUA fallback
# This could be a complex interaction that's difficult with standard actions
task = """
Go to https://csreis.github.io/tests/cross-site-iframe.html
Click on "Go cross-site, complex page" using index
Use the OpenAI CUA fallback to click on "Tree is open..." link.
"""
# Create agent with our custom tools that includes CUA fallback
agent = Agent(
task=task,
llm=llm,
tools=tools,
browser_session=browser_session,
)
print('🚀 Starting agent with CUA fallback support...')
print(f'Task: {task}')
print('-' * 50)
try:
# Run the agent
result = await agent.run()
print(f'\n✅ Task completed! Result: {result}')
except Exception as e:
print(f'\n❌ Error running agent: {e}')
finally:
# Clean up browser session
await browser_session.kill()
print('\n🧹 Browser session closed')
if __name__ == '__main__':
# Example of different scenarios where CUA might be useful
print('🔧 OpenAI Computer Use Assistant (CUA) Integration Example')
print('=' * 60)
print()
print("This example shows how to integrate OpenAI's CUA as a fallback action")
print('when standard browser-use actions cannot achieve the desired goal.')
print()
print('CUA is particularly useful for:')
print('• Complex mouse interactions (drag & drop, precise clicking)')
print('• Keyboard shortcuts and key combinations')
print('• Actions that require pixel-perfect precision')
print("• Custom UI elements that don't respond to standard actions")
print()
print('Make sure you have OPENAI_API_KEY set in your environment!')
print()
# Check if OpenAI API key is available
if not os.getenv('OPENAI_API_KEY'):
print('❌ Error: OPENAI_API_KEY environment variable not set')
print('Please set your OpenAI API key to use CUA integration')
sys.exit(1)
# Run the example
asyncio.run(main())
@@ -0,0 +1,114 @@
"""
Example of implementing file upload functionality.
This shows how to upload files to file input elements on web pages.
"""
import asyncio
import logging
import os
import sys
import aiofiles
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from browser_use import ChatOpenAI
from browser_use.agent.service import Agent, Tools
from browser_use.agent.views import ActionResult
from browser_use.browser import BrowserSession
from browser_use.browser.events import UploadFileEvent
logger = logging.getLogger(__name__)
# Initialize tools
tools = Tools()
@tools.action('Upload file to interactive element with file path')
async def upload_file(index: int, path: str, browser_session: BrowserSession, available_file_paths: list[str]):
if path not in available_file_paths:
return ActionResult(error=f'File path {path} is not available')
if not os.path.exists(path):
return ActionResult(error=f'File {path} does not exist')
try:
# Get the DOM element by index
dom_element = await browser_session.get_dom_element_by_index(index)
if dom_element is None:
msg = f'No element found at index {index}'
logger.info(msg)
return ActionResult(error=msg)
# Check if it's a file input element
if dom_element.tag_name.lower() != 'input' or dom_element.attributes.get('type') != 'file':
msg = f'Element at index {index} is not a file input element'
logger.info(msg)
return ActionResult(error=msg)
# Dispatch the upload file event
event = browser_session.event_bus.dispatch(UploadFileEvent(node=dom_element, file_path=path))
await event
msg = f'Successfully uploaded file to index {index}'
logger.info(msg)
return ActionResult(extracted_content=msg, include_in_memory=True)
except Exception as e:
msg = f'Failed to upload file to index {index}: {str(e)}'
logger.info(msg)
return ActionResult(error=msg)
async def main():
"""Main function to run the example"""
browser_session = BrowserSession()
await browser_session.start()
llm = ChatOpenAI(model='gpt-4.1-mini')
# List of file paths the agent is allowed to upload
# In a real scenario, you'd want to be very careful about what files
# the agent can access and upload
available_file_paths = [
'/tmp/test_document.pdf',
'/tmp/test_image.jpg',
]
# Create test files if they don't exist
for file_path in available_file_paths:
if not os.path.exists(file_path):
async with aiofiles.open(file_path, 'w') as f:
await f.write('Test file content for upload example')
# Create the agent with file upload capability
agent = Agent(
task="""
Go to https://www.w3schools.com/howto/howto_html_file_upload_button.asp and try to upload one of the available test files.
""",
llm=llm,
browser_session=browser_session,
tools=tools,
# Pass the available file paths to the tools context
custom_context={'available_file_paths': available_file_paths},
)
# Run the agent
await agent.run(max_steps=10)
# Cleanup
await browser_session.kill()
# Clean up test files
for file_path in available_file_paths:
if os.path.exists(file_path):
os.remove(file_path)
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,43 @@
import asyncio
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from browser_use import ActionResult, Agent, ChatOpenAI, Tools
tools = Tools()
@tools.registry.action('Done with task')
async def done(text: str):
import yagmail # type: ignore
# To send emails use
# STEP 1: go to https://support.google.com/accounts/answer/185833
# STEP 2: Create an app password (you can't use here your normal gmail password)
# STEP 3: Use the app password in the code below for the password
yag = yagmail.SMTP('your_email@gmail.com', 'your_app_password')
yag.send(
to='recipient@example.com',
subject='Test Email',
contents=f'result\n: {text}',
)
return ActionResult(is_done=True, extracted_content='Email sent!')
async def main():
task = 'go to brower-use.com and then done'
model = ChatOpenAI(model='gpt-4.1-mini')
agent = Agent(task=task, llm=model, tools=tools)
await agent.run()
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,56 @@
import asyncio
import logging
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from onepassword.client import Client # type: ignore # pip install onepassword-sdk
from browser_use import ActionResult, Agent, ChatOpenAI, Tools
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
OP_SERVICE_ACCOUNT_TOKEN = os.getenv('OP_SERVICE_ACCOUNT_TOKEN')
OP_ITEM_ID = os.getenv('OP_ITEM_ID') # Go to 1Password, right click on the item, click "Copy Secret Reference"
tools = Tools()
@tools.registry.action('Get 2FA code from 1Password for Google Account', domains=['*.google.com', 'google.com'])
async def get_1password_2fa() -> ActionResult:
"""
Custom action to retrieve 2FA/MFA code from 1Password using onepassword.client SDK.
"""
client = await Client.authenticate(
# setup instructions: https://github.com/1Password/onepassword-sdk-python/#-get-started
auth=OP_SERVICE_ACCOUNT_TOKEN,
integration_name='Browser-Use',
integration_version='v1.0.0',
)
mfa_code = await client.secrets.resolve(f'op://Private/{OP_ITEM_ID}/One-time passcode')
return ActionResult(extracted_content=mfa_code)
async def main():
# Example task using the 1Password 2FA action
task = 'Go to account.google.com, enter username and password, then if prompted for 2FA code, get 2FA code from 1Password for and enter it'
model = ChatOpenAI(model='gpt-4.1-mini')
agent = Agent(task=task, llm=model, tools=tools)
result = await agent.run()
print(f'Task completed with result: {result}')
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,50 @@
import asyncio
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
load_dotenv()
from pydantic import BaseModel
from browser_use import ChatOpenAI
from browser_use.agent.service import Agent
from browser_use.tools.service import Tools
# Initialize tools first
tools = Tools()
class Model(BaseModel):
title: str
url: str
likes: int
license: str
class Models(BaseModel):
models: list[Model]
@tools.action('Save models', param_model=Models)
def save_models(params: Models):
with open('models.txt', 'a') as f:
for model in params.models:
f.write(f'{model.title} ({model.url}): {model.likes} likes, {model.license}\n')
# video: https://preview.screen.studio/share/EtOhIk0P
async def main():
task = 'Look up models with a license of cc-by-sa-4.0 and sort by most likes on Hugging face, save top 5 to file.'
model = ChatOpenAI(model='gpt-4.1-mini')
agent = Agent(task=task, llm=model, tools=tools)
await agent.run()
if __name__ == '__main__':
asyncio.run(main())