chore: import upstream snapshot with attribution
Test Browser Use CLI Install / uv pip install (ubuntu-latest) (push) Failing after 1s
Test Browser Use CLI Install / uvx browser-use from local wheel (push) Failing after 1s
Test Browser Use CLI Install / uvx browser-use[cli] from PyPI (push) Failing after 1s
package / pip-install-on-macos-latest-py-3.11 (push) Has been skipped
package / pip-install-on-macos-latest-py-3.13 (push) Has been skipped
package / pip-install-on-ubuntu-latest-py-3.11 (push) Has been skipped
package / pip-install-on-windows-latest-py-3.13 (push) Has been skipped
cloud_evals / trigger_cloud_eval_image_build (push) Failing after 1s
docker / build_publish_image (push) Failing after 1s
Test Browser Use CLI Install / browser-use skill sync (push) Failing after 1s
lint / code-style (push) Failing after 0s
lint / type-checker (push) Failing after 1s
package / pip-build (push) Failing after 1s
lint / syntax-errors (push) Failing after 3s
package / pip-install-on-ubuntu-latest-py-3.13 (push) Has been skipped
package / pip-install-on-windows-latest-py-3.11 (push) Has been skipped
test / ${{ matrix.test_filename }} (push) Has been skipped
test / evaluate-tasks (push) Has been skipped
test / setup-chromium (push) Failing after 2s
test / find_tests (push) Failing after 2s
Test Browser Use CLI Install / uv pip install (windows-latest) (push) Has been cancelled
Test Browser Use CLI Install / uv pip install (macos-latest) (push) Has been cancelled
Test Browser Use CLI Install / uv pip install (ubuntu-latest) (push) Failing after 1s
Test Browser Use CLI Install / uvx browser-use from local wheel (push) Failing after 1s
Test Browser Use CLI Install / uvx browser-use[cli] from PyPI (push) Failing after 1s
package / pip-install-on-macos-latest-py-3.11 (push) Has been skipped
package / pip-install-on-macos-latest-py-3.13 (push) Has been skipped
package / pip-install-on-ubuntu-latest-py-3.11 (push) Has been skipped
package / pip-install-on-windows-latest-py-3.13 (push) Has been skipped
cloud_evals / trigger_cloud_eval_image_build (push) Failing after 1s
docker / build_publish_image (push) Failing after 1s
Test Browser Use CLI Install / browser-use skill sync (push) Failing after 1s
lint / code-style (push) Failing after 0s
lint / type-checker (push) Failing after 1s
package / pip-build (push) Failing after 1s
lint / syntax-errors (push) Failing after 3s
package / pip-install-on-ubuntu-latest-py-3.13 (push) Has been skipped
package / pip-install-on-windows-latest-py-3.11 (push) Has been skipped
test / ${{ matrix.test_filename }} (push) Has been skipped
test / evaluate-tasks (push) Has been skipped
test / setup-chromium (push) Failing after 2s
test / find_tests (push) Failing after 2s
Test Browser Use CLI Install / uv pip install (windows-latest) (push) Has been cancelled
Test Browser Use CLI Install / uv pip install (macos-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from browser_use import Agent, Browser, ChatOpenAI, Tools
|
||||
from browser_use.tools.views import UploadFileAction
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def apply_to_rochester_regional_health(info: dict, resume_path: str):
|
||||
"""
|
||||
json format:
|
||||
{
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"email": "john.doe@example.com",
|
||||
"phone": "555-555-5555",
|
||||
"age": "21",
|
||||
"US_citizen": boolean,
|
||||
"sponsorship_needed": boolean,
|
||||
|
||||
"resume": "Link to resume",
|
||||
"postal_code": "12345",
|
||||
"country": "USA",
|
||||
"city": "Rochester",
|
||||
"address": "123 Main St",
|
||||
|
||||
"gender": "Male",
|
||||
"race": "Asian",
|
||||
"Veteran_status": "Not a veteran",
|
||||
"disability_status": "No disability"
|
||||
}
|
||||
"""
|
||||
|
||||
llm = ChatOpenAI(model='o3')
|
||||
|
||||
tools = Tools()
|
||||
|
||||
@tools.action(description='Upload resume file')
|
||||
async def upload_resume(browser_session):
|
||||
params = UploadFileAction(path=resume_path, index=0)
|
||||
return 'Ready to upload resume'
|
||||
|
||||
browser = Browser(cross_origin_iframes=True)
|
||||
|
||||
task = f"""
|
||||
- Your goal is to fill out and submit a job application form with the provided information.
|
||||
- Navigate to https://apply.appcast.io/jobs/50590620606/applyboard/apply/
|
||||
- Scroll through the entire application and use extract_structured_data action to extract all the relevant information needed to fill out the job application form. use this information and return a structured output that can be used to fill out the entire form: {info}. Use the done action to finish the task. Fill out the job application form with the following information.
|
||||
- Before completing every step, refer to this information for accuracy. It is structured in a way to help you fill out the form and is the source of truth.
|
||||
- Follow these instructions carefully:
|
||||
- if anything pops up that blocks the form, close it out and continue filling out the form.
|
||||
- Do not skip any fields, even if they are optional. If you do not have the information, make your best guess based on the information provided.
|
||||
Fill out the form from top to bottom, never skip a field to come back to it later. When filling out a field, only focus on one field per step. For each of these steps, scroll to the related text. These are the steps:
|
||||
1) use input_text action to fill out the following:
|
||||
- "First name"
|
||||
- "Last name"
|
||||
- "Email"
|
||||
- "Phone number"
|
||||
2) use the upload_file_to_element action to fill out the following:
|
||||
- Resume upload field
|
||||
3) use input_text action to fill out the following:
|
||||
- "Postal code"
|
||||
- "Country"
|
||||
- "State"
|
||||
- "City"
|
||||
- "Address"
|
||||
- "Age"
|
||||
4) use click action to select the following options:
|
||||
- "Are you legally authorized to work in the country for which you are applying?"
|
||||
- "Will you now or in the future require sponsorship for employment visa status (e.g., H-1B visa status, etc.) to work legally for Rochester Regional Health?"
|
||||
- "Do you have, or are you in the process of obtaining, a professional license?"
|
||||
- SELECT NO FOR THIS FIELD
|
||||
5) use input_text action to fill out the following:
|
||||
- "What drew you to healthcare?"
|
||||
6) use click action to select the following options:
|
||||
- "How many years of experience do you have in a related role?"
|
||||
- "Gender"
|
||||
- "Race"
|
||||
- "Hispanic/Latino"
|
||||
- "Veteran status"
|
||||
- "Disability status"
|
||||
7) use input_text action to fill out the following:
|
||||
- "Today's date"
|
||||
8) CLICK THE SUBMIT BUTTON AND CHECK FOR A SUCCESS SCREEN. Once there is a success screen, complete your end task of writing final_result and outputting it.
|
||||
- Before you start, create a step-by-step plan to complete the entire task. Make sure to delegate a step for each field to be filled out.
|
||||
*** IMPORTANT ***:
|
||||
- You are not done until you have filled out every field of the form.
|
||||
- When you have completed the entire form, press the submit button to submit the application and use the done action once you have confirmed that the application is submitted
|
||||
- PLACE AN EMPHASIS ON STEP 4, the click action. That section should be filled out.
|
||||
- At the end of the task, structure your final_result as 1) a human-readable summary of all detections and actions performed on the page with 2) a list with all questions encountered in the page. Do not say "see above." Include a fully written out, human-readable summary at the very end.
|
||||
"""
|
||||
|
||||
available_file_paths = [resume_path]
|
||||
|
||||
agent = Agent(
|
||||
task=task,
|
||||
llm=llm,
|
||||
browser=browser,
|
||||
tools=tools,
|
||||
available_file_paths=available_file_paths,
|
||||
)
|
||||
|
||||
history = await agent.run()
|
||||
|
||||
return history.final_result()
|
||||
|
||||
|
||||
async def main(test_data_path: str, resume_path: str):
|
||||
# Verify files exist
|
||||
if not os.path.exists(test_data_path):
|
||||
raise FileNotFoundError(f'Test data file not found at: {test_data_path}')
|
||||
if not os.path.exists(resume_path):
|
||||
raise FileNotFoundError(f'Resume file not found at: {resume_path}')
|
||||
|
||||
with open(test_data_path) as f: # noqa: ASYNC230
|
||||
mock_info = json.load(f)
|
||||
|
||||
results = await apply_to_rochester_regional_health(mock_info, resume_path=resume_path)
|
||||
print('Search Results:', results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Apply to Rochester Regional Health job')
|
||||
parser.add_argument('--test-data', required=True, help='Path to test data JSON file')
|
||||
parser.add_argument('--resume', required=True, help='Path to resume PDF file')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
asyncio.run(main(args.test_data, args.resume))
|
||||
@@ -0,0 +1,83 @@
|
||||
import asyncio
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from browser_use import Agent, Browser, ChatBrowserUse
|
||||
|
||||
|
||||
class GroceryItem(BaseModel):
|
||||
"""A single grocery item"""
|
||||
|
||||
name: str = Field(..., description='Item name')
|
||||
price: float = Field(..., description='Price as number')
|
||||
brand: str | None = Field(None, description='Brand name')
|
||||
size: str | None = Field(None, description='Size or quantity')
|
||||
url: str = Field(..., description='Full URL to item')
|
||||
|
||||
|
||||
class GroceryCart(BaseModel):
|
||||
"""Grocery cart results"""
|
||||
|
||||
items: list[GroceryItem] = Field(default_factory=list, description='All grocery items found')
|
||||
|
||||
|
||||
async def add_to_cart(items: list[str] = ['milk', 'eggs', 'bread']):
|
||||
browser = Browser(cdp_url='http://localhost:9222')
|
||||
|
||||
llm = ChatBrowserUse(model='bu-2-0')
|
||||
|
||||
# Task prompt
|
||||
task = f"""
|
||||
Search for "{items}" on Instacart at the nearest store.
|
||||
|
||||
You will buy all of the items at the same store.
|
||||
For each item:
|
||||
1. Search for the item
|
||||
2. Find the best match (closest name, lowest price)
|
||||
3. Add the item to the cart
|
||||
|
||||
Site:
|
||||
- Instacart: https://www.instacart.com/
|
||||
"""
|
||||
|
||||
# Create agent with structured output
|
||||
agent = Agent(
|
||||
browser=browser,
|
||||
llm=llm,
|
||||
task=task,
|
||||
output_model_schema=GroceryCart,
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
result = await agent.run()
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Get user input
|
||||
items_input = input('What items would you like to add to cart (comma-separated)? ').strip()
|
||||
if not items_input:
|
||||
items = ['milk', 'eggs', 'bread']
|
||||
print(f'Using default items: {items}')
|
||||
else:
|
||||
items = [item.strip() for item in items_input.split(',')]
|
||||
|
||||
result = asyncio.run(add_to_cart(items))
|
||||
|
||||
# Access structured output
|
||||
if result and result.structured_output:
|
||||
cart = result.structured_output
|
||||
|
||||
print(f'\n{"=" * 60}')
|
||||
print('Items Added to Cart')
|
||||
print(f'{"=" * 60}\n')
|
||||
|
||||
for item in cart.items:
|
||||
print(f'Name: {item.name}')
|
||||
print(f'Price: ${item.price}')
|
||||
if item.brand:
|
||||
print(f'Brand: {item.brand}')
|
||||
if item.size:
|
||||
print(f'Size: {item.size}')
|
||||
print(f'URL: {item.url}')
|
||||
print(f'{"-" * 60}')
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Goal: Automates CAPTCHA solving on a demo website.
|
||||
|
||||
|
||||
Simple try of the agent.
|
||||
@dev You need to add OPENAI_API_KEY to your environment variables.
|
||||
NOTE: captchas are hard. For this example it works. But e.g. for iframes it does not.
|
||||
for this example it helps to zoom in.
|
||||
"""
|
||||
|
||||
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 Agent, ChatOpenAI
|
||||
|
||||
|
||||
async def main():
|
||||
llm = ChatOpenAI(model='gpt-4.1-mini')
|
||||
agent = Agent(
|
||||
task='go to https://captcha.com/demos/features/captcha-demo.aspx and solve the captcha',
|
||||
llm=llm,
|
||||
)
|
||||
await agent.run()
|
||||
input('Press Enter to exit')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,52 @@
|
||||
# Goal: Checks for available visa appointment slots on the Greece MFA website.
|
||||
|
||||
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
|
||||
|
||||
if not os.getenv('OPENAI_API_KEY'):
|
||||
raise ValueError('OPENAI_API_KEY is not set. Please add it to your environment variables.')
|
||||
|
||||
tools = Tools()
|
||||
|
||||
|
||||
class WebpageInfo(BaseModel):
|
||||
"""Model for webpage link."""
|
||||
|
||||
link: str = 'https://appointment.mfa.gr/en/reservations/aero/ireland-grcon-dub/'
|
||||
|
||||
|
||||
@tools.action('Go to the webpage', param_model=WebpageInfo)
|
||||
def go_to_webpage(webpage_info: WebpageInfo):
|
||||
"""Returns the webpage link."""
|
||||
return webpage_info.link
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to execute the agent task."""
|
||||
task = (
|
||||
'Go to the Greece MFA webpage via the link I provided you.'
|
||||
'Check the visa appointment dates. If there is no available date in this month, check the next month.'
|
||||
'If there is no available date in both months, tell me there is no available date.'
|
||||
)
|
||||
|
||||
model = ChatOpenAI(model='gpt-4.1-mini')
|
||||
agent = Agent(task, model, tools=tools, use_vision=True)
|
||||
|
||||
await agent.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = ["browser-use", "mistralai"]
|
||||
# ///
|
||||
|
||||
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 asyncio
|
||||
import logging
|
||||
|
||||
from browser_use import Agent, ChatOpenAI
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def main():
|
||||
agent = Agent(
|
||||
task="""
|
||||
Objective: Navigate to the following UR, what is on page 3?
|
||||
|
||||
URL: https://docs.house.gov/meetings/GO/GO00/20220929/115171/HHRG-117-GO00-20220929-SD010.pdf
|
||||
""",
|
||||
llm=ChatOpenAI(model='gpt-4.1-mini'),
|
||||
)
|
||||
result = await agent.run()
|
||||
logger.info(result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
Show how to use custom outputs.
|
||||
|
||||
@dev You need to add OPENAI_API_KEY to your environment variables.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
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 httpx
|
||||
from pydantic import BaseModel
|
||||
|
||||
from browser_use import Agent, ChatOpenAI, Tools
|
||||
from browser_use.agent.views import ActionResult
|
||||
|
||||
|
||||
class Profile(BaseModel):
|
||||
platform: str
|
||||
profile_url: str
|
||||
|
||||
|
||||
class Profiles(BaseModel):
|
||||
profiles: list[Profile]
|
||||
|
||||
|
||||
tools = Tools(exclude_actions=['search'], output_model=Profiles)
|
||||
BEARER_TOKEN = os.getenv('BEARER_TOKEN')
|
||||
|
||||
if not BEARER_TOKEN:
|
||||
# use the api key for ask tessa
|
||||
# you can also use other apis like exa, xAI, perplexity, etc.
|
||||
raise ValueError('BEARER_TOKEN is not set - go to https://www.heytessa.ai/ and create an api key')
|
||||
|
||||
|
||||
@tools.registry.action('Search the web for a specific query')
|
||||
async def search_web(query: str):
|
||||
keys_to_use = ['url', 'title', 'content', 'author', 'score']
|
||||
headers = {'Authorization': f'Bearer {BEARER_TOKEN}'}
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
'https://asktessa.ai/api/search',
|
||||
headers=headers,
|
||||
json={'query': query},
|
||||
)
|
||||
|
||||
final_results = [
|
||||
{key: source[key] for key in keys_to_use if key in source}
|
||||
for source in await response.json()['sources']
|
||||
if source['score'] >= 0.2
|
||||
]
|
||||
# print(json.dumps(final_results, indent=4))
|
||||
result_text = json.dumps(final_results, indent=4)
|
||||
print(result_text)
|
||||
return ActionResult(extracted_content=result_text, include_in_memory=True)
|
||||
|
||||
|
||||
async def main():
|
||||
task = (
|
||||
'Go to this tiktok video url, open it and extract the @username from the resulting url. Then do a websearch for this username to find all his social media profiles. Return me the links to the social media profiles with the platform name.'
|
||||
' https://www.tiktokv.com/share/video/7470981717659110678/ '
|
||||
)
|
||||
model = ChatOpenAI(model='gpt-4.1-mini')
|
||||
agent = Agent(task=task, llm=model, tools=tools)
|
||||
|
||||
history = await agent.run()
|
||||
|
||||
result = history.final_result()
|
||||
if result:
|
||||
parsed: Profiles = Profiles.model_validate_json(result)
|
||||
|
||||
for profile in parsed.profiles:
|
||||
print('\n--------------------------------')
|
||||
print(f'Platform: {profile.platform}')
|
||||
print(f'Profile URL: {profile.profile_url}')
|
||||
|
||||
else:
|
||||
print('No result')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,185 @@
|
||||
import os
|
||||
|
||||
from onepassword.client import Client
|
||||
|
||||
from browser_use import ActionResult, Agent, Browser, ChatOpenAI, Tools
|
||||
from browser_use.browser.session import BrowserSession
|
||||
|
||||
"""
|
||||
Use Case: Securely log into a website using credentials stored in 1Password vault.
|
||||
- Use fill_field action to fill in username and password fields with values retrieved from 1Password. The LLM never sees the actual credentials.
|
||||
- Use blur_page and unblur_page actions to visually obscure sensitive information on the page while filling in credentials for extra security.
|
||||
|
||||
**SETUP**
|
||||
How to setup 1Password with Browser Use
|
||||
- Get Individual Plan for 1Password
|
||||
- Go to the Home page and click “New Vault”
|
||||
- Add the credentials you need for any websites you want to log into
|
||||
- Go to “Developer” tab, navigate to “Directory” and create a Service Account
|
||||
- Give the service account access to the vault
|
||||
- Copy the Service Account Token and set it as environment variable OP_SERVICE_ACCOUNT_TOKEN
|
||||
- Install the onepassword package: pip install onepassword-sdk
|
||||
Note: In this example, we assume that you created a vault named "prod-secrets" and added an item named "X" with fields "username" and "password".
|
||||
"""
|
||||
|
||||
|
||||
async def main():
|
||||
# Gets your service account token from environment variable
|
||||
token = os.getenv('OP_SERVICE_ACCOUNT_TOKEN')
|
||||
|
||||
# Authenticate with 1Password
|
||||
op_client = await Client.authenticate(auth=token, integration_name='Browser Use Secure Login', integration_version='v1.0.0')
|
||||
|
||||
# Initialize tools
|
||||
tools = Tools()
|
||||
|
||||
@tools.registry.action('Apply CSS blur filter to entire page content')
|
||||
async def blur_page(browser_session: BrowserSession):
|
||||
"""
|
||||
Applies CSS blur filter directly to document.body to obscure all page content.
|
||||
The blur will remain until unblur_page is called.
|
||||
DOM remains accessible for element finding while page is visually blurred.
|
||||
"""
|
||||
try:
|
||||
# Get CDP session
|
||||
cdp_session = await browser_session.get_or_create_cdp_session()
|
||||
|
||||
# Apply blur filter to document.body
|
||||
result = await cdp_session.cdp_client.send.Runtime.evaluate(
|
||||
params={
|
||||
'expression': """
|
||||
(function() {
|
||||
// Check if already blurred
|
||||
if (document.body.getAttribute('data-page-blurred') === 'true') {
|
||||
console.log('[BLUR] Page already blurred');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply CSS blur filter to body
|
||||
document.body.style.filter = 'blur(15px)';
|
||||
document.body.style.webkitFilter = 'blur(15px)'; // Safari support
|
||||
document.body.style.transition = 'filter 0.3s ease';
|
||||
document.body.setAttribute('data-page-blurred', 'true');
|
||||
|
||||
console.log('[BLUR] Applied CSS blur to page');
|
||||
return true;
|
||||
})();
|
||||
""",
|
||||
'returnByValue': True,
|
||||
},
|
||||
session_id=cdp_session.session_id,
|
||||
)
|
||||
|
||||
success = result.get('result', {}).get('value', False)
|
||||
if success:
|
||||
print('[BLUR] Applied CSS blur to page')
|
||||
return ActionResult(extracted_content='Successfully applied CSS blur to page', include_in_memory=True)
|
||||
else:
|
||||
return ActionResult(error='Failed to apply blur', include_in_memory=True)
|
||||
|
||||
except Exception as e:
|
||||
print(f'[BLUR ERROR] {e}')
|
||||
return ActionResult(error=f'Failed to blur page: {str(e)}', include_in_memory=True)
|
||||
|
||||
@tools.registry.action('Remove CSS blur filter from page')
|
||||
async def unblur_page(browser_session: BrowserSession):
|
||||
"""
|
||||
Removes the CSS blur filter from document.body, restoring normal page visibility.
|
||||
"""
|
||||
try:
|
||||
# Get CDP session
|
||||
cdp_session = await browser_session.get_or_create_cdp_session()
|
||||
|
||||
# Remove blur filter from body
|
||||
result = await cdp_session.cdp_client.send.Runtime.evaluate(
|
||||
params={
|
||||
'expression': """
|
||||
(function() {
|
||||
if (document.body.getAttribute('data-page-blurred') !== 'true') {
|
||||
console.log('[BLUR] Page not blurred');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove CSS blur filter
|
||||
document.body.style.filter = 'none';
|
||||
document.body.style.webkitFilter = 'none';
|
||||
document.body.removeAttribute('data-page-blurred');
|
||||
|
||||
console.log('[BLUR] Removed CSS blur from page');
|
||||
return true;
|
||||
})();
|
||||
""",
|
||||
'returnByValue': True,
|
||||
},
|
||||
session_id=cdp_session.session_id,
|
||||
)
|
||||
|
||||
removed = result.get('result', {}).get('value', False)
|
||||
if removed:
|
||||
print('[BLUR] Removed CSS blur from page')
|
||||
return ActionResult(extracted_content='Successfully removed CSS blur from page', include_in_memory=True)
|
||||
else:
|
||||
print('[BLUR] Page was not blurred')
|
||||
return ActionResult(
|
||||
extracted_content='Page was not blurred (may have already been removed)', include_in_memory=True
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f'[BLUR ERROR] {e}')
|
||||
return ActionResult(error=f'Failed to unblur page: {str(e)}', include_in_memory=True)
|
||||
|
||||
# LLM can call this action to use actors to fill in sensitive fields using 1Password values.
|
||||
@tools.registry.action('Fill in a specific field for a website using value from 1Password vault')
|
||||
async def fill_field(vault_name: str, item_name: str, field_name: str, browser_session: BrowserSession):
|
||||
"""
|
||||
Fills in a specific field for a website using the value from 1Password.
|
||||
Note: Use blur_page before calling this if you want visual security.
|
||||
"""
|
||||
try:
|
||||
# Resolve field value from 1Password
|
||||
field_value = await op_client.secrets.resolve(f'op://{vault_name}/{item_name}/{field_name}')
|
||||
|
||||
# Get current page
|
||||
page = await browser_session.must_get_current_page()
|
||||
|
||||
# Find and fill the element
|
||||
target_field = await page.must_get_element_by_prompt(f'{field_name} input field', llm)
|
||||
await target_field.fill(field_value)
|
||||
|
||||
return ActionResult(
|
||||
extracted_content=f'Successfully filled {field_name} field for {vault_name}/{item_name}', include_in_memory=True
|
||||
)
|
||||
except Exception as e:
|
||||
return ActionResult(error=f'Failed to fill {field_name} field: {str(e)}', include_in_memory=True)
|
||||
|
||||
browser_session = Browser()
|
||||
|
||||
llm = ChatOpenAI(model='o3')
|
||||
|
||||
agent = Agent(
|
||||
task="""
|
||||
Navigate to https://x.com/i/flow/login
|
||||
Wait for the page to load.
|
||||
Use fill_field action with vault_name='prod-secrets' and item_name='X' and field_name='username'.
|
||||
Click the Next button.
|
||||
Use fill_field action with vault_name='prod-secrets' and item_name='X' and field_name='password'.
|
||||
Click the Log in button.
|
||||
Give me the latest 5 tweets from the logged in user's timeline.
|
||||
|
||||
**IMPORTANT** Use blur_page action if you anticipate filling sensitive fields.
|
||||
Only use unblur_page action after you see the logged in user's X timeline.
|
||||
Your priority is to keep the username and password hidden while filling sensitive fields.
|
||||
""",
|
||||
browser_session=browser_session,
|
||||
llm=llm,
|
||||
tools=tools,
|
||||
file_system_path='./agent_data',
|
||||
)
|
||||
|
||||
await agent.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import asyncio
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,36 @@
|
||||
import asyncio
|
||||
|
||||
from browser_use import Agent, Browser, ChatBrowserUse, Tools
|
||||
|
||||
|
||||
async def main():
|
||||
browser = Browser(cdp_url='http://localhost:9222')
|
||||
|
||||
llm = ChatBrowserUse(model='bu-2-0')
|
||||
|
||||
tools = Tools()
|
||||
|
||||
task = """
|
||||
Design me a mid-range water-cooled ITX computer
|
||||
Keep the total budget under $2000
|
||||
|
||||
Go to https://pcpartpicker.com/
|
||||
Make sure the build is complete and has no incompatibilities.
|
||||
Provide the full list of parts with prices and a link to the completed build.
|
||||
"""
|
||||
|
||||
agent = Agent(
|
||||
task=task,
|
||||
browser=browser,
|
||||
tools=tools,
|
||||
llm=llm,
|
||||
)
|
||||
|
||||
history = await agent.run(max_steps=100000)
|
||||
return history
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
history = asyncio.run(main())
|
||||
final_result = history.final_result()
|
||||
print(final_result)
|
||||
@@ -0,0 +1,91 @@
|
||||
import asyncio
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from browser_use import Agent, Browser, ChatBrowserUse
|
||||
|
||||
|
||||
class ProductListing(BaseModel):
|
||||
"""A single product listing"""
|
||||
|
||||
title: str = Field(..., description='Product title')
|
||||
url: str = Field(..., description='Full URL to listing')
|
||||
price: float = Field(..., description='Price as number')
|
||||
condition: str | None = Field(None, description='Condition: Used, New, Refurbished, etc')
|
||||
source: str = Field(..., description='Source website: Amazon, eBay, or Swappa')
|
||||
|
||||
|
||||
class PriceComparison(BaseModel):
|
||||
"""Price comparison results"""
|
||||
|
||||
search_query: str = Field(..., description='The search query used')
|
||||
listings: list[ProductListing] = Field(default_factory=list, description='All product listings')
|
||||
|
||||
|
||||
async def find(item: str = 'Used iPhone 12'):
|
||||
"""
|
||||
Search for an item across multiple marketplaces and compare prices.
|
||||
|
||||
Args:
|
||||
item: The item to search for (e.g., "Used iPhone 12")
|
||||
|
||||
Returns:
|
||||
PriceComparison object with structured results
|
||||
"""
|
||||
browser = Browser(cdp_url='http://localhost:9222')
|
||||
|
||||
llm = ChatBrowserUse(model='bu-2-0')
|
||||
|
||||
# Task prompt
|
||||
task = f"""
|
||||
Search for "{item}" on eBay, Amazon, and Swappa. Get any 2-3 listings from each site.
|
||||
|
||||
For each site:
|
||||
1. Search for "{item}"
|
||||
2. Extract ANY 2-3 listings you find (sponsored, renewed, used - all are fine)
|
||||
3. Get: title, price (number only, if range use lower number), source, full URL, condition
|
||||
4. Move to next site
|
||||
|
||||
Sites:
|
||||
- eBay: https://www.ebay.com/
|
||||
- Amazon: https://www.amazon.com/
|
||||
- Swappa: https://swappa.com/
|
||||
"""
|
||||
|
||||
# Create agent with structured output
|
||||
agent = Agent(
|
||||
browser=browser,
|
||||
llm=llm,
|
||||
task=task,
|
||||
output_model_schema=PriceComparison,
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
result = await agent.run()
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Get user input
|
||||
query = input('What item would you like to compare prices for? ').strip()
|
||||
if not query:
|
||||
query = 'Used iPhone 12'
|
||||
print(f'Using default query: {query}')
|
||||
|
||||
result = asyncio.run(find(query))
|
||||
|
||||
# Access structured output
|
||||
if result and result.structured_output:
|
||||
comparison = result.structured_output
|
||||
|
||||
print(f'\n{"=" * 60}')
|
||||
print(f'Price Comparison Results: {comparison.search_query}')
|
||||
print(f'{"=" * 60}\n')
|
||||
|
||||
for listing in comparison.listings:
|
||||
print(f'Title: {listing.title}')
|
||||
print(f'Price: ${listing.price}')
|
||||
print(f'Source: {listing.source}')
|
||||
print(f'URL: {listing.url}')
|
||||
print(f'Condition: {listing.condition or "N/A"}')
|
||||
print(f'{"-" * 60}')
|
||||
@@ -0,0 +1,120 @@
|
||||
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 Agent, ChatOpenAI
|
||||
|
||||
task = """
|
||||
### Prompt for Shopping Agent – Migros Online Grocery Order
|
||||
|
||||
**Objective:**
|
||||
Visit [Migros Online](https://www.migros.ch/en), search for the required grocery items, add them to the cart, select an appropriate delivery window, and complete the checkout process using TWINT.
|
||||
|
||||
**Important:**
|
||||
- Make sure that you don't buy more than it's needed for each article.
|
||||
- After your search, if you click the "+" button, it adds the item to the basket.
|
||||
- if you open the basket sidewindow menu, you can close it by clicking the X button on the top right. This will help you navigate easier.
|
||||
---
|
||||
|
||||
### Step 1: Navigate to the Website
|
||||
- Open [Migros Online](https://www.migros.ch/en).
|
||||
- You should be logged in as Nikolaos Kaliorakis
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Add Items to the Basket
|
||||
|
||||
#### Shopping List:
|
||||
|
||||
**Meat & Dairy:**
|
||||
- Beef Minced meat (1 kg)
|
||||
- Gruyère cheese (grated preferably)
|
||||
- 2 liters full-fat milk
|
||||
- Butter (cheapest available)
|
||||
|
||||
**Vegetables:**
|
||||
- Carrots (1kg pack)
|
||||
- Celery
|
||||
- Leeks (1 piece)
|
||||
- 1 kg potatoes
|
||||
|
||||
At this stage, check the basket on the top right (indicates the price) and check if you bought the right items.
|
||||
|
||||
**Fruits:**
|
||||
- 2 lemons
|
||||
- Oranges (for snacking)
|
||||
|
||||
**Pantry Items:**
|
||||
- Lasagna sheets
|
||||
- Tahini
|
||||
- Tomato paste (below CHF2)
|
||||
- Black pepper refill (not with the mill)
|
||||
- 2x 1L Oatly Barista(oat milk)
|
||||
- 1 pack of eggs (10 egg package)
|
||||
|
||||
#### Ingredients I already have (DO NOT purchase):
|
||||
- Olive oil, garlic, canned tomatoes, dried oregano, bay leaves, salt, chili flakes, flour, nutmeg, cumin.
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Handling Unavailable Items
|
||||
- If an item is **out of stock**, find the best alternative.
|
||||
- Use the following recipe contexts to choose substitutions:
|
||||
- **Pasta Bolognese & Lasagna:** Minced meat, tomato paste, lasagna sheets, milk (for béchamel), Gruyère cheese.
|
||||
- **Hummus:** Tahini, chickpeas, lemon juice, olive oil.
|
||||
- **Chickpea Curry Soup:** Chickpeas, leeks, curry, lemons.
|
||||
- **Crispy Slow-Cooked Pork Belly with Vegetables:** Potatoes, butter.
|
||||
- Example substitutions:
|
||||
- If Gruyère cheese is unavailable, select another semi-hard cheese.
|
||||
- If Tahini is unavailable, a sesame-based alternative may work.
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Adjusting for Minimum Order Requirement
|
||||
- If the total order **is below CHF 99**, add **a liquid soap refill** to reach the minimum. If it;s still you can buy some bread, dark chockolate.
|
||||
- At this step, check if you have bought MORE items than needed. If the price is more then CHF200, you MUST remove items.
|
||||
- If an item is not available, choose an alternative.
|
||||
- if an age verification is needed, remove alcoholic products, we haven't verified yet.
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Select Delivery Window
|
||||
- Choose a **delivery window within the current week**. It's ok to pay up to CHF2 for the window selection.
|
||||
- Preferably select a slot within the workweek.
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Checkout
|
||||
- Proceed to checkout.
|
||||
- Select **TWINT** as the payment method.
|
||||
- Check out.
|
||||
-
|
||||
- if it's needed the username is: nikoskalio.dev@gmail.com
|
||||
- and the password is : TheCircuit.Migros.dev!
|
||||
---
|
||||
|
||||
### Step 7: Confirm Order & Output Summary
|
||||
- Once the order is placed, output a summary including:
|
||||
- **Final list of items purchased** (including any substitutions).
|
||||
- **Total cost**.
|
||||
- **Chosen delivery time**.
|
||||
|
||||
**Important:** Ensure efficiency and accuracy throughout the process."""
|
||||
|
||||
|
||||
agent = Agent(task=task, llm=ChatOpenAI(model='gpt-4.1-mini'))
|
||||
|
||||
|
||||
async def main():
|
||||
await agent.run()
|
||||
input('Press Enter to close the browser...')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user