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

This commit is contained in:
wehub-resource-sync
2026-07-13 12:02:32 +08:00
commit 4cd2d4af2b
475 changed files with 121829 additions and 0 deletions
+134
View File
@@ -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))
+83
View File
@@ -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}')
+35
View File
@@ -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())
+52
View File
@@ -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())
+38
View File
@@ -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())
+185
View File
@@ -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())
+36
View File
@@ -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)
+91
View File
@@ -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}')
+120
View File
@@ -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())