Reorder examples

This commit is contained in:
magmueller
2025-01-25 11:28:52 -08:00
parent b70754d2bb
commit c7008a70c2
44 changed files with 41 additions and 612 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -1,224 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Browser Agent Controller</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.control-panel {
display: grid;
gap: 10px;
margin-top: 20px;
}
button {
padding: 10px;
border: none;
border-radius: 4px;
background-color: #007bff;
color: white;
cursor: pointer;
transition: background-color 0.3s;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
button:hover:not(:disabled) {
background-color: #0056b3;
}
#status {
margin-top: 20px;
padding: 10px;
border-radius: 4px;
}
.status-running { background-color: #d4edda; color: #155724; }
.status-paused { background-color: #fff3cd; color: #856404; }
.status-stopped { background-color: #f8d7da; color: #721c24; }
.status-not-created { background-color: #e2e3e5; color: #383d41; }
.status-ready { background-color: #cce5ff; color: #004085; }
textarea {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}
#logContainer {
margin-top: 20px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 4px;
border: 1px solid #ddd;
height: 300px;
overflow-y: auto;
font-family: monospace;
font-size: 14px;
white-space: pre-wrap;
}
.log-entry {
margin: 2px 0;
padding: 2px 5px;
}
.button-group {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>Browser Agent Controller</h1>
<div class="control-panel">
<textarea id="taskInput" rows="3" placeholder="Enter your task here..."></textarea>
<div class="button-group">
<button id="runBtn" onclick="runAgent()">Run Agent</button>
<button id="pauseBtn" onclick="pauseAgent()" disabled>Pause</button>
<button id="resumeBtn" onclick="resumeAgent()" disabled>Resume</button>
<button id="stopBtn" onclick="stopAgent()" disabled>Stop</button>
</div>
</div>
<div id="status" class="status-not-created">
Status: Not Created
</div>
<div id="logContainer">
<div id="logs"></div>
</div>
</div>
<script>
let statusCheckInterval;
let eventSource;
function setupLogging() {
if (eventSource) {
eventSource.close();
}
eventSource = new EventSource('/logs');
const logsDiv = document.getElementById('logs');
eventSource.addEventListener('log', function(event) {
const logEntry = document.createElement('div');
logEntry.className = 'log-entry';
logEntry.textContent = event.data;
logsDiv.appendChild(logEntry);
logsDiv.scrollTop = logsDiv.scrollHeight;
});
eventSource.onerror = function(error) {
console.error('EventSource failed:', error);
eventSource.close();
setTimeout(setupLogging, 5000); // Try to reconnect after 5 seconds
};
}
async function runAgent() {
const task = document.getElementById('taskInput').value;
if (!task) {
alert('Please enter a task');
return;
}
try {
const response = await fetch('/agent/run', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ task }),
});
if (response.ok) {
startStatusCheck();
document.getElementById('taskInput').disabled = true;
}
} catch (error) {
console.error('Error:', error);
alert('Failed to run agent');
}
}
async function pauseAgent() {
try {
await fetch('/agent/pause', { method: 'POST' });
} catch (error) {
console.error('Error:', error);
alert('Failed to pause agent');
}
}
async function resumeAgent() {
try {
await fetch('/agent/resume', { method: 'POST' });
} catch (error) {
console.error('Error:', error);
alert('Failed to resume agent');
}
}
async function stopAgent() {
try {
await fetch('/agent/stop', { method: 'POST' });
document.getElementById('taskInput').disabled = false;
} catch (error) {
console.error('Error:', error);
alert('Failed to stop agent');
}
}
async function checkStatus() {
try {
const response = await fetch('/agent/status');
const data = await response.json();
const statusDiv = document.getElementById('status');
statusDiv.className = `status-${data.status}`;
statusDiv.textContent = `Status: ${data.status.charAt(0).toUpperCase() + data.status.slice(1)}`;
// Update buttons based on status
document.getElementById('runBtn').disabled = data.status === 'running';
document.getElementById('pauseBtn').disabled = data.status !== 'running';
document.getElementById('resumeBtn').disabled = data.status !== 'paused';
document.getElementById('stopBtn').disabled = data.status === 'stopped' || data.status === 'not_created';
if (data.task) {
statusDiv.textContent += `\nTask: ${data.task}`;
}
} catch (error) {
console.error('Error checking status:', error);
}
}
function startStatusCheck() {
// Check status immediately
checkStatus();
// Then check every 2 seconds
if (!statusCheckInterval) {
statusCheckInterval = setInterval(checkStatus, 2000);
}
}
// Initial setup
setupLogging();
checkStatus();
</script>
</body>
</html>

View File

@@ -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())

View File

@@ -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,

View File

@@ -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'

View File

@@ -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)}',

View File

@@ -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()

25
examples/simple.py Normal file
View File

@@ -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())

View File

@@ -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)

View File

@@ -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():