diff --git a/examples/real_browser.py b/examples/browser/real_browser.py similarity index 100% rename from examples/real_browser.py rename to examples/browser/real_browser.py diff --git a/examples/using_cdp.py b/examples/browser/using_cdp.py similarity index 100% rename from examples/using_cdp.py rename to examples/browser/using_cdp.py diff --git a/examples/clipboard.py b/examples/custom-functions/clipboard.py similarity index 100% rename from examples/clipboard.py rename to examples/custom-functions/clipboard.py diff --git a/examples/file_upload.py b/examples/custom-functions/file_upload.py similarity index 100% rename from examples/file_upload.py rename to examples/custom-functions/file_upload.py diff --git a/examples/notification.py b/examples/custom-functions/notification.py similarity index 100% rename from examples/notification.py rename to examples/custom-functions/notification.py diff --git a/examples/save_to_file_hugging_face.py b/examples/custom-functions/save_to_file_hugging_face.py similarity index 100% rename from examples/save_to_file_hugging_face.py rename to examples/custom-functions/save_to_file_hugging_face.py diff --git a/examples/scrolling_page.py b/examples/custom-functions/scrolling_page.py similarity index 100% rename from examples/scrolling_page.py rename to examples/custom-functions/scrolling_page.py diff --git a/examples/fastapi/README.md b/examples/fastapi/README.md deleted file mode 100644 index 60c372051..000000000 --- a/examples/fastapi/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# FastAPI Browser Agent Example - -This example demonstrates how to create a web interface for controlling a Browser Agent using FastAPI. - -## Requirements - -```bash -uv pip install fastapi uvicorn sse-starlette langchain-openai -``` - -## Running the Example - -1. Navigate to the fastapi example directory: -```bash -cd examples/fastapi -``` - -2. Run the FastAPI application: -```bash -python main.py -``` - -3. Open your web browser and navigate to `http://localhost:8000` - - -## API Endpoints - -- `GET /` - Serves the web interface -- `POST /agent/run` - Creates and runs an agent with the specified task -- `POST /agent/pause` - Pauses the current agent -- `POST /agent/resume` - Resumes the paused agent -- `POST /agent/stop` - Stops the current agent -- `GET /agent/status` - Gets the current status of the agent -- `GET /logs` - Server-sent events endpoint for real-time logs \ No newline at end of file diff --git a/examples/fastapi/main.py b/examples/fastapi/main.py deleted file mode 100644 index 41e4e6ca3..000000000 --- a/examples/fastapi/main.py +++ /dev/null @@ -1,284 +0,0 @@ -import asyncio -import logging -import os -import time -from logging.handlers import RotatingFileHandler -from queue import Queue -from threading import Lock -from typing import Any, Dict, Optional - -import psutil -from fastapi import FastAPI, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import FileResponse, JSONResponse -from fastapi.staticfiles import StaticFiles -from langchain_openai import ChatOpenAI -from pydantic import BaseModel -from sse_starlette.sse import EventSourceResponse - -from browser_use import Agent - -# Create logs directory if it doesn't exist -LOGS_DIR = os.path.join(os.path.dirname(__file__), 'logs') -os.makedirs(LOGS_DIR, exist_ok=True) - -# Configure logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -# Create a rotating file handler (10MB per file, keep 5 backup files) -file_handler = RotatingFileHandler( - os.path.join(LOGS_DIR, 'agent_manager.log'), - maxBytes=10 * 1024 * 1024, # 10MB - backupCount=5, - encoding='utf-8', -) -file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) -logger.addHandler(file_handler) - -# Create a separate file for agent-specific logs -agent_file_handler = RotatingFileHandler( - os.path.join(LOGS_DIR, 'agents.log'), - maxBytes=10 * 1024 * 1024, # 10MB - backupCount=5, - encoding='utf-8', -) -agent_file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) - -# Create a queue for real-time log messages -log_queue = Queue() -log_lock = Lock() - - -class LogHandler(logging.Handler): - def emit(self, record): - log_entry = self.format(record) - with log_lock: - log_queue.put(log_entry) - - -# Add handlers to the browser_use logger -browser_use_logger = logging.getLogger('browser_use') -browser_use_logger.addHandler(LogHandler()) -browser_use_logger.addHandler(agent_file_handler) - -app = FastAPI() - -# Enable CORS -app.add_middleware( - CORSMiddleware, - allow_origins=['*'], # Allows all origins - allow_credentials=True, - allow_methods=['*'], # Allows all methods - allow_headers=['*'], # Allows all headers -) - -# Create static directory if it doesn't exist -STATIC_DIR = os.path.join(os.path.dirname(__file__), 'static') -os.makedirs(STATIC_DIR, exist_ok=True) - -# Serve static files -app.mount('/static', StaticFiles(directory=STATIC_DIR), name='static') - - -class TaskRequest(BaseModel): - agent_id: str - task: str - - -class AgentManager: - def __init__(self): - self.agents: Dict[str, Dict[str, Any]] = {} - self.max_agents = 40 # Increased to 100 agents - self._lock = asyncio.Lock() - self.process = psutil.Process() - self.start_time = time.time() - logger.info(f'AgentManager initialized with max_agents={self.max_agents}') - - async def create_agent(self, agent_id: str, task: str): - async with self._lock: - if len(self.agents) >= self.max_agents: - current_memory = self.process.memory_info().rss / 1024 / 1024 # MB - logger.error(f'Max agents reached. Current memory usage: {current_memory:.2f}MB') - raise ValueError(f'Maximum number of agents ({self.max_agents}) reached. Memory usage: {current_memory:.2f}MB') - - if agent_id in self.agents: - logger.warning(f'Agent {agent_id} already exists') - raise ValueError(f'Agent {agent_id} already exists') - - try: - llm = ChatOpenAI(model='gpt-4o-mini') - agent = { - 'instance': Agent(task=task, llm=llm), - 'task': task, - 'running': False, - 'created_at': time.time(), - 'last_active': time.time(), - } - self.agents[agent_id] = agent - logger.info(f'Created agent {agent_id}. Total agents: {len(self.agents)}') - except Exception as e: - logger.error(f'Failed to create agent {agent_id}: {str(e)}') - raise - - def get_system_stats(self) -> dict: - stats = { - 'total_agents': len(self.agents), - 'memory_usage_mb': self.process.memory_info().rss / 1024 / 1024, - 'cpu_percent': self.process.cpu_percent(), - 'uptime_seconds': time.time() - self.start_time, - 'thread_count': self.process.num_threads(), - } - logger.info(f'System stats: {stats}') - return stats - - def get_agent(self, agent_id: str) -> Agent: - if agent_id not in self.agents: - raise ValueError(f'Agent {agent_id} not found') - return self.agents[agent_id]['instance'] - - def get_agent_status(self, agent_id: str): - if agent_id not in self.agents: - return 'not_created' - - agent_data = self.agents[agent_id] - agent = agent_data['instance'] - - if agent._stopped: - return 'stopped' - elif agent._paused: - return 'paused' - elif agent_data['running']: - return 'running' - return 'ready' - - def set_running(self, agent_id: str, value: bool): - if agent_id in self.agents: - self.agents[agent_id]['running'] = value - - def list_agents(self): - return { - agent_id: {'task': data['task'], 'status': self.get_agent_status(agent_id)} for agent_id, data in self.agents.items() - } - - -# Create a singleton instance -agent_manager = AgentManager() - - -@app.get('/') -async def read_root(): - return FileResponse(os.path.join(STATIC_DIR, 'index.html')) - - -@app.post('/agent/run') -async def run_agent(request: TaskRequest): - try: - start_time = time.time() - if request.agent_id not in agent_manager.agents: - await agent_manager.create_agent(request.agent_id, request.task) - - agent = agent_manager.get_agent(request.agent_id) - agent_manager.set_running(request.agent_id, True) - - # Run in background task to not block - task = asyncio.create_task(agent.run()) - - # Add completion callback for debugging - def done_callback(future): - try: - future.result() - except Exception as e: - logger.error(f'Agent {request.agent_id} failed: {str(e)}') - finally: - agent_manager.set_running(request.agent_id, False) - - task.add_done_callback(done_callback) - - setup_time = time.time() - start_time - return { - 'status': 'running', - 'agent_id': request.agent_id, - 'task': request.task, - 'setup_time_ms': setup_time * 1000, - 'total_agents': len(agent_manager.agents), - } - except Exception as e: - logger.error(f'Error running agent {request.agent_id}: {str(e)}') - raise HTTPException(status_code=400, detail=str(e)) - - -@app.get('/ping') -async def ping(): - return {'status': 'ok'} - - -@app.post('/agent/{agent_id}/pause') -async def pause_agent(agent_id: str): - try: - agent = agent_manager.get_agent(agent_id) - agent.pause() - return {'status': 'paused', 'agent_id': agent_id} - except ValueError as e: - raise HTTPException(status_code=400, detail=str(e)) - - -@app.post('/agent/{agent_id}/resume') -async def resume_agent(agent_id: str): - try: - agent = agent_manager.get_agent(agent_id) - agent.resume() - return {'status': 'resumed', 'agent_id': agent_id} - except ValueError as e: - raise HTTPException(status_code=400, detail=str(e)) - - -@app.post('/agent/{agent_id}/stop') -async def stop_agent(agent_id: str): - try: - agent = agent_manager.get_agent(agent_id) - agent.stop() - agent_manager.set_running(agent_id, False) - return {'status': 'stopped', 'agent_id': agent_id} - except ValueError as e: - raise HTTPException(status_code=400, detail=str(e)) - - -@app.get('/agent/{agent_id}/status') -async def get_agent_status(agent_id: str): - try: - status = agent_manager.get_agent_status(agent_id) - task = agent_manager.agents[agent_id]['task'] if agent_id in agent_manager.agents else None - return {'status': status, 'agent_id': agent_id, 'task': task} - except Exception as e: - raise HTTPException(status_code=400, detail=str(e)) - - -@app.get('/agents') -async def list_agents(): - return agent_manager.list_agents() - - -@app.get('/logs') -async def event_stream(): - async def generate(): - while True: - if not log_queue.empty(): - with log_lock: - while not log_queue.empty(): - log_entry = log_queue.get() - yield {'event': 'log', 'data': log_entry} - await asyncio.sleep(0.1) - - return EventSourceResponse(generate()) - - -@app.get('/system/stats') -async def get_system_stats(): - return agent_manager.get_system_stats() - - -if __name__ == '__main__': - import uvicorn - - uvicorn.run(app, host='0.0.0.0', port=8000) diff --git a/examples/fastapi/static/index.html b/examples/fastapi/static/index.html deleted file mode 100644 index 3191734bc..000000000 --- a/examples/fastapi/static/index.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - Browser Agent Controller - - - -
-

Browser Agent Controller

- -
- -
- - - - -
-
- -
- Status: Not Created -
- -
-
-
-
- - - - \ No newline at end of file diff --git a/examples/fastapi/test_agents.py b/examples/fastapi/test_agents.py deleted file mode 100644 index bbc787330..000000000 --- a/examples/fastapi/test_agents.py +++ /dev/null @@ -1,44 +0,0 @@ -import asyncio -import random - -import aiohttp - - -async def test_agent(session, agent_id, task): - # Start agent - async with session.post('http://localhost:8000/agent/run', json={'agent_id': f'agent_{agent_id}', 'task': task}) as response: - print(f'Started agent_{agent_id}:', await response.json()) - - # Monitor status - while True: - async with session.get(f'http://localhost:8000/agent/{agent_id}/status') as response: - status = (await response.json())['status'] - print(f'Agent {agent_id} status:', status) - if status in ['stopped', 'error']: - break - await asyncio.sleep(2) - - -async def main(): - tasks = [ - 'Search for Python programming tutorials', - 'Find information about machine learning', - 'Look up web development resources', - 'Research data science tools', - 'Find AI programming examples', - 'Search for software testing methods', - 'Look up database optimization techniques', - 'Research cloud computing platforms', - 'Find cybersecurity best practices', - 'Search for DevOps tutorials', - ] - tasks = tasks * 10 - - async with aiohttp.ClientSession() as session: - # Start 10 agents simultaneously - agent_tasks = [test_agent(session, i, tasks[i]) for i in range(50)] - await asyncio.gather(*agent_tasks) - - -if __name__ == '__main__': - asyncio.run(main()) diff --git a/examples/custom_output.py b/examples/features/custom_output.py similarity index 100% rename from examples/custom_output.py rename to examples/features/custom_output.py diff --git a/examples/custom_system_prompt.py b/examples/features/custom_system_prompt.py similarity index 100% rename from examples/custom_system_prompt.py rename to examples/features/custom_system_prompt.py diff --git a/examples/custom_user_agent.py b/examples/features/custom_user_agent.py similarity index 82% rename from examples/custom_user_agent.py rename to examples/features/custom_user_agent.py index 642ebfa61..f832d92ad 100644 --- a/examples/custom_user_agent.py +++ b/examples/features/custom_user_agent.py @@ -17,9 +17,7 @@ from browser_use.controller.service import Controller def get_llm(provider: str): if provider == 'anthropic': - return ChatAnthropic( - model_name='claude-3-5-sonnet-20240620', timeout=25, stop=None, temperature=0.0 - ) + return ChatAnthropic(model_name='claude-3-5-sonnet-20240620', timeout=25, stop=None, temperature=0.0) elif provider == 'openai': return ChatOpenAI(model='gpt-4o', temperature=0.0) @@ -27,6 +25,7 @@ def get_llm(provider: str): raise ValueError(f'Unsupported provider: {provider}') +# NOTE: This example is to find your current user agent string to use it in the browser_context task = 'go to https://whatismyuseragent.com and find the current user agent string ' @@ -54,18 +53,13 @@ browser = Browser( ) ) -browser_context = BrowserContext( - config=BrowserContextConfig( - user_agent="foobarfoo" - ), - browser=browser -) +browser_context = BrowserContext(config=BrowserContextConfig(user_agent='foobarfoo'), browser=browser) agent = Agent( task=args.query, llm=llm, controller=controller, -# browser=browser, + # browser=browser, browser_context=browser_context, use_vision=True, max_actions_per_step=1, diff --git a/examples/initial_actions.py b/examples/features/initial_actions.py similarity index 100% rename from examples/initial_actions.py rename to examples/features/initial_actions.py diff --git a/examples/multi-tab_handling.py b/examples/features/multi-tab_handling.py similarity index 100% rename from examples/multi-tab_handling.py rename to examples/features/multi-tab_handling.py diff --git a/examples/multiple_agents_same_browser.py b/examples/features/multiple_agents_same_browser.py similarity index 100% rename from examples/multiple_agents_same_browser.py rename to examples/features/multiple_agents_same_browser.py diff --git a/examples/parallel_agents.py b/examples/features/parallel_agents.py similarity index 100% rename from examples/parallel_agents.py rename to examples/features/parallel_agents.py diff --git a/examples/pause_agent.py b/examples/features/pause_agent.py similarity index 100% rename from examples/pause_agent.py rename to examples/features/pause_agent.py diff --git a/examples/restrict_urls.py b/examples/features/restrict_urls.py similarity index 100% rename from examples/restrict_urls.py rename to examples/features/restrict_urls.py diff --git a/examples/result_processing.py b/examples/features/result_processing.py similarity index 100% rename from examples/result_processing.py rename to examples/features/result_processing.py diff --git a/examples/save_trace.py b/examples/features/save_trace.py similarity index 100% rename from examples/save_trace.py rename to examples/features/save_trace.py diff --git a/examples/validate_output.py b/examples/features/validate_output.py similarity index 98% rename from examples/validate_output.py rename to examples/features/validate_output.py index d5eada34d..afe96083e 100644 --- a/examples/validate_output.py +++ b/examples/features/validate_output.py @@ -34,7 +34,6 @@ async def done(params: DoneResult): result = ActionResult(is_done=True, extracted_content=params.model_dump_json()) print(result) # NOTE: this is clearly wrong - to demonstrate the validator - # return result return 'blablabla' diff --git a/examples/integrations/discord_api.py b/examples/integrations/discord_api.py index 32ee5c0b0..66a2640bf 100644 --- a/examples/integrations/discord_api.py +++ b/examples/integrations/discord_api.py @@ -55,9 +55,7 @@ class DiscordBot(commands.Bot): intents.members = True # Enable members intent for user info # Initialize the bot with a command prefix and intents. - super().__init__( - command_prefix='!', intents=intents - ) # You may not need prefix, just here for flexibility + super().__init__(command_prefix='!', intents=intents) # You may not need prefix, just here for flexibility # self.tree = app_commands.CommandTree(self) # Initialize command tree for slash commands. @@ -86,12 +84,8 @@ class DiscordBot(commands.Bot): print(f'Error sending start message: {e}') try: - agent_message = await self.run_agent( - message.content.replace(f'{self.prefix} ', '').strip() - ) - await message.channel.send( - content=f'{agent_message}', reference=message, mention_author=True - ) + agent_message = await self.run_agent(message.content.replace(f'{self.prefix} ', '').strip()) + await message.channel.send(content=f'{agent_message}', reference=message, mention_author=True) except Exception as e: await message.channel.send( content=f'Error during task execution: {str(e)}', diff --git a/examples/discord_example.py b/examples/integrations/discord_example.py similarity index 98% rename from examples/discord_example.py rename to examples/integrations/discord_example.py index 392ca6f92..4f62cad68 100644 --- a/examples/discord_example.py +++ b/examples/integrations/discord_example.py @@ -35,12 +35,12 @@ Five Steps to create and invite a Discord bot: import os from dotenv import load_dotenv -from integrations.discord_api import DiscordBot from langchain_google_genai import ChatGoogleGenerativeAI from pydantic import SecretStr from browser_use import BrowserConfig from browser_use.agent.service import Agent +from examples.integrations.discord_api import DiscordBot load_dotenv() diff --git a/examples/bedrock_claude.py b/examples/models/bedrock_claude.py similarity index 100% rename from examples/bedrock_claude.py rename to examples/models/bedrock_claude.py diff --git a/examples/deepseek.py b/examples/models/deepseek.py similarity index 100% rename from examples/deepseek.py rename to examples/models/deepseek.py diff --git a/examples/gemini.py b/examples/models/gemini.py similarity index 100% rename from examples/gemini.py rename to examples/models/gemini.py diff --git a/examples/amazon_search.py b/examples/models/gpt-4o.py similarity index 100% rename from examples/amazon_search.py rename to examples/models/gpt-4o.py diff --git a/examples/ollama.py b/examples/models/ollama.py similarity index 100% rename from examples/ollama.py rename to examples/models/ollama.py diff --git a/examples/qwen.py b/examples/models/qwen.py similarity index 100% rename from examples/qwen.py rename to examples/models/qwen.py diff --git a/examples/agent_browsing.ipynb b/examples/notebook/agent_browsing.ipynb similarity index 100% rename from examples/agent_browsing.ipynb rename to examples/notebook/agent_browsing.ipynb diff --git a/examples/simple.py b/examples/simple.py new file mode 100644 index 000000000..2803955b0 --- /dev/null +++ b/examples/simple.py @@ -0,0 +1,25 @@ +import asyncio + +from dotenv import load_dotenv +from langchain_openai import ChatOpenAI + +from browser_use import Agent + +load_dotenv() + +# Initialize the model +llm = ChatOpenAI( + model='gpt-4o', + temperature=0.0, +) + +# Create agent with the model +agent = Agent(task='Go to amazon.com and search for "laptop"', llm=llm) + + +async def main(): + await agent.run() + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/try.py b/examples/ui/command_line.py similarity index 100% rename from examples/try.py rename to examples/ui/command_line.py diff --git a/examples/gradio_demo.py b/examples/ui/gradio_demo.py similarity index 100% rename from examples/gradio_demo.py rename to examples/ui/gradio_demo.py diff --git a/examples/captcha.py b/examples/use-cases/captcha.py similarity index 100% rename from examples/captcha.py rename to examples/use-cases/captcha.py diff --git a/examples/check_appointment.py b/examples/use-cases/check_appointment.py similarity index 100% rename from examples/check_appointment.py rename to examples/use-cases/check_appointment.py diff --git a/examples/find_and_apply_to_jobs.py b/examples/use-cases/find_and_apply_to_jobs.py similarity index 93% rename from examples/find_and_apply_to_jobs.py rename to examples/use-cases/find_and_apply_to_jobs.py index 4f7c3af2a..fdd2eeabe 100644 --- a/examples/find_and_apply_to_jobs.py +++ b/examples/use-cases/find_and_apply_to_jobs.py @@ -33,8 +33,13 @@ import logging logger = logging.getLogger(__name__) # full screen mode controller = Controller() + +# NOTE: This is the path to your cv file CV = Path.cwd() / 'cv_04_24.pdf' +if not CV.exists(): + raise FileNotFoundError(f'You need to set the path to your cv file in the CV variable. CV file not found at {CV}') + class Job(BaseModel): title: str @@ -45,9 +50,7 @@ class Job(BaseModel): salary: Optional[str] = None -@controller.action( - 'Save jobs to file - with a score how well it fits to my profile', param_model=Job -) +@controller.action('Save jobs to file - with a score how well it fits to my profile', param_model=Job) def save_jobs(job: Job): with open('jobs.csv', 'a', newline='') as f: writer = csv.writer(f) diff --git a/examples/online_coding_agent.py b/examples/use-cases/online_coding_agent.py similarity index 100% rename from examples/online_coding_agent.py rename to examples/use-cases/online_coding_agent.py diff --git a/examples/post-twitter.py b/examples/use-cases/post-twitter.py similarity index 100% rename from examples/post-twitter.py rename to examples/use-cases/post-twitter.py diff --git a/examples/test_cv.txt b/examples/use-cases/test_cv.txt similarity index 100% rename from examples/test_cv.txt rename to examples/use-cases/test_cv.txt diff --git a/examples/twitter_cookies.txt b/examples/use-cases/twitter_cookies.txt similarity index 100% rename from examples/twitter_cookies.txt rename to examples/use-cases/twitter_cookies.txt diff --git a/examples/twitter_post_using_cookies.py b/examples/use-cases/twitter_post_using_cookies.py similarity index 91% rename from examples/twitter_post_using_cookies.py rename to examples/use-cases/twitter_post_using_cookies.py index dc2ad1bcd..050ab32ee 100644 --- a/examples/twitter_post_using_cookies.py +++ b/examples/use-cases/twitter_post_using_cookies.py @@ -23,8 +23,8 @@ browser = Browser( # chrome_instance_path='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', ) ) - -context = BrowserContext(browser=browser, config=BrowserContextConfig(cookies_file='twitter_cookies.txt')) +file_path = os.path.join(os.path.dirname(__file__), 'twitter_cookies.txt') +context = BrowserContext(browser=browser, config=BrowserContextConfig(cookies_file=file_path)) async def run_search(): diff --git a/examples/web_voyager_agent.py b/examples/use-cases/web_voyager_agent.py similarity index 100% rename from examples/web_voyager_agent.py rename to examples/use-cases/web_voyager_agent.py