forked from browser-use/browser-use
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbrowsers.py
More file actions
288 lines (243 loc) · 10.5 KB
/
browsers.py
File metadata and controls
288 lines (243 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# pyright: reportMissingImports=false
# We do this because we need to install the other browser packages but dont want them in our main package dependencies
import asyncio
import json
import logging
import os
from pathlib import Path
import requests
from browser_use import BrowserProfile, BrowserSession
from eval.task_types import Task
logger = logging.getLogger(__name__)
# Check for Anchor Browser API key
ANCHOR_BROWSER_API_KEY = os.getenv('ANCHOR_BROWSER_API_KEY')
if ANCHOR_BROWSER_API_KEY:
logger.info('ANCHOR_BROWSER_API_KEY is set. Tasks can use Anchor Browser.')
else:
logger.warning('ANCHOR_BROWSER_API_KEY is not set. Anchor Browser will not be available.')
# Check for Brightdata CDP URL
BRIGHTDATA_CDP_URL = os.getenv('BRIGHTDATA_CDP_URL')
if BRIGHTDATA_CDP_URL:
logger.info('BRIGHTDATA_CDP_URL is set. Tasks can use Brightdata browser.')
else:
logger.warning('BRIGHTDATA_CDP_URL is not set. Brightdata browser will not be available.')
# Check for Browserbase API key
BROWSERBASE_API_KEY = os.getenv('BROWSERBASE_API_KEY')
BROWSERBASE_PROJECT_ID = os.getenv('BROWSERBASE_PROJECT_ID')
if BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID:
logger.info('BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID are set. Tasks can use Browserbase.')
else:
logger.warning('BROWSERBASE_API_KEY or BROWSERBASE_PROJECT_ID are not set. Browserbase will not be available.')
# Check for Hyperbrowser API key
HYPERBROWSER_API_KEY = os.getenv('HYPERBROWSER_API_KEY')
if HYPERBROWSER_API_KEY:
logger.info('HYPERBROWSER_API_KEY is set. Tasks can use Hyperbrowser.')
else:
logger.warning('HYPERBROWSER_API_KEY is not set. Hyperbrowser will not be available.')
def create_anchor_browser_session(headless: bool = False) -> str:
"""Create an Anchor Browser session and return CDP URL"""
if not ANCHOR_BROWSER_API_KEY:
raise ValueError('ANCHOR_BROWSER_API_KEY must be set')
browser_configuration = {
'session': {'proxy': {'type': 'anchor_mobile', 'active': True, 'country_code': 'us'}},
'browser': {
'adblock': {'active': True},
'captcha_solver': {'active': True},
'headless': {'active': headless},
'extra_stealth': {'active': True},
},
}
try:
response = requests.post(
'https://api.anchorbrowser.io/v1/sessions',
headers={
'anchor-api-key': ANCHOR_BROWSER_API_KEY,
'Content-Type': 'application/json',
},
json=browser_configuration,
timeout=10,
)
response.raise_for_status()
session_data = response.json()['data']
session_id = session_data['id']
return f'wss://connect.anchorbrowser.io?apiKey={ANCHOR_BROWSER_API_KEY}&sessionId={session_id}'
except requests.RequestException as e:
logger.error(f'Failed to create Anchor Browser session: {type(e).__name__}: {e}')
raise
except KeyError as e:
logger.error(f'Unexpected response format from Anchor Browser API: {e}')
raise
def create_browserbase_session() -> str:
"""Create a Browserbase session and return CDP URL"""
if not BROWSERBASE_API_KEY or not BROWSERBASE_PROJECT_ID:
raise ValueError('BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID must be set')
try:
from browserbase import Browserbase
except ImportError:
raise ImportError(
'browserbase package is required for Browserbase functionality. Install it with: pip install browserbase'
)
try:
bb = Browserbase(api_key=BROWSERBASE_API_KEY)
session = bb.sessions.create(
project_id=BROWSERBASE_PROJECT_ID,
proxies=True,
)
return session.connect_url
except Exception as e:
logger.error(f'Failed to create Browserbase session: {type(e).__name__}: {e}')
raise
async def create_hyperbrowser_session() -> str:
"""Create a Hyperbrowser session and return WebSocket endpoint"""
if not HYPERBROWSER_API_KEY:
raise ValueError('HYPERBROWSER_API_KEY must be set')
try:
from hyperbrowser import AsyncHyperbrowser
from hyperbrowser.models import CreateSessionParams
except ImportError:
raise ImportError(
'hyperbrowser package is required for Hyperbrowser functionality. Install it with: pip install hyperbrowser'
)
try:
client = AsyncHyperbrowser(api_key=HYPERBROWSER_API_KEY)
session = await client.sessions.create(
params=CreateSessionParams(
use_stealth=True,
)
)
await client.close()
return session.ws_endpoint or ''
except Exception as e:
logger.error(f'Failed to create Hyperbrowser session: {type(e).__name__}: {e}')
raise
async def setup_browser_session(
task: Task,
headless: bool,
highlight_elements: bool = True,
browser: str = 'local',
default_navigation_timeout: int | None = None,
default_timeout: int | None = None,
minimum_wait_page_load_time: float | None = None,
wait_for_network_idle_page_load_time: float | None = None,
maximum_wait_page_load_time: float | None = None,
wait_between_actions: float | None = None,
stealth: bool = False,
) -> BrowserSession:
"""Setup browser session for the task"""
# Validate browser option
valid_browsers = ['local', 'anchor-browser', 'brightdata', 'browserbase', 'hyperbrowser', 'browser-use']
if browser not in valid_browsers:
logger.warning(f'Browser setup: Invalid browser option "{browser}". Falling back to local browser.')
browser = 'local'
cdp_url = None
if browser == 'anchor-browser':
if ANCHOR_BROWSER_API_KEY:
try:
logger.debug(f'Browser setup: Creating Anchor Browser session for task {task.task_id}')
cdp_url = await asyncio.to_thread(create_anchor_browser_session, headless)
except Exception as e:
logger.error(
f'Browser setup: Failed to create Anchor Browser session for task {task.task_id}: {type(e).__name__}: {e}'
)
logger.info(f'Browser setup: Falling back to local browser for task {task.task_id}')
cdp_url = None
else:
logger.warning(
f'Browser setup: Anchor Browser requested but ANCHOR_BROWSER_API_KEY not set. Using local browser for task {task.task_id}'
)
elif browser == 'brightdata':
if BRIGHTDATA_CDP_URL:
logger.debug(f'Browser setup: Using Brightdata CDP URL for task {task.task_id}')
cdp_url = BRIGHTDATA_CDP_URL
else:
logger.warning(
f'Browser setup: Brightdata requested but BRIGHTDATA_CDP_URL not set. Using local browser for task {task.task_id}'
)
elif browser == 'browserbase':
if BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID:
try:
logger.debug(f'Browser setup: Creating Browserbase session for task {task.task_id}')
cdp_url = await asyncio.to_thread(create_browserbase_session)
except Exception as e:
logger.error(
f'Browser setup: Failed to create Browserbase session for task {task.task_id}: {type(e).__name__}: {e}'
)
logger.info(f'Browser setup: Falling back to local browser for task {task.task_id}')
cdp_url = None
else:
logger.warning(
f'Browser setup: Browserbase requested but BROWSERBASE_API_KEY or BROWSERBASE_PROJECT_ID not set. Using local browser for task {task.task_id}'
)
elif browser == 'hyperbrowser':
if HYPERBROWSER_API_KEY:
try:
logger.debug(f'Browser setup: Creating Hyperbrowser session for task {task.task_id}')
cdp_url = await create_hyperbrowser_session()
except Exception as e:
logger.error(
f'Browser setup: Failed to create Hyperbrowser session for task {task.task_id}: {type(e).__name__}: {e}'
)
logger.info(f'Browser setup: Falling back to local browser for task {task.task_id}')
cdp_url = None
else:
logger.warning(
f'Browser setup: Hyperbrowser requested but HYPERBROWSER_API_KEY not set. Using local browser for task {task.task_id}'
)
elif browser == 'browser-use':
logger.warning(f'Browser setup: Browser-use not implemented yet. Falling back to local browser for task {task.task_id}')
profile_kwargs = {
'user_data_dir': None, # Incognito mode - no persistent state
'headless': headless,
'chromium_sandbox': False, # running in docker
'highlight_elements': highlight_elements, # Control element highlighting (passed to profile)
'keep_alive': True,
'stealth': stealth,
# higher timeouts = higher success rates on long tail of slow sites or if on a slow CI server
# ignore_https_errors=True, # some eval tasks have http:// or broken https sites in them
}
# Add timeout parameters if provided
if default_navigation_timeout is not None:
profile_kwargs['default_navigation_timeout'] = default_navigation_timeout
if default_timeout is not None:
profile_kwargs['default_timeout'] = default_timeout
if minimum_wait_page_load_time is not None:
profile_kwargs['minimum_wait_page_load_time'] = minimum_wait_page_load_time
if wait_for_network_idle_page_load_time is not None:
profile_kwargs['wait_for_network_idle_page_load_time'] = wait_for_network_idle_page_load_time
if maximum_wait_page_load_time is not None:
profile_kwargs['maximum_wait_page_load_time'] = maximum_wait_page_load_time
if wait_between_actions is not None:
profile_kwargs['wait_between_actions'] = wait_between_actions
if hasattr(task, 'login_cookie') and task.login_cookie:
# For login tasks, configure storage_state to save cookies to JSON file
# Don't set user_data_dir=None for login tasks to avoid conflict
task_folder = Path(f'saved_trajectories/{task.task_id}')
task_folder.mkdir(parents=True, exist_ok=True)
storage_state_path = task_folder / 'storage_state.json'
# Create empty storage state file if it doesn't exist to avoid FileNotFoundError
if not storage_state_path.exists():
storage_state_path.write_text(json.dumps({'cookies': [], 'origins': []}))
profile_kwargs['storage_state'] = str(storage_state_path)
# Remove user_data_dir=None for login tasks to avoid conflict with storage_state
profile_kwargs.pop('user_data_dir', None)
downloads_dir_path = task_folder / 'downloads'
downloads_dir_path.mkdir(parents=True, exist_ok=True)
profile_kwargs['downloads_path'] = str(downloads_dir_path)
logger.debug(f'Login task {task.task_id}: Configured to save cookies to {storage_state_path}')
profile = BrowserProfile(**profile_kwargs)
if cdp_url:
logger.debug(f'Browser setup: Using CDP Browser for task {task.task_id}')
browser_session = BrowserSession(browser_profile=profile, cdp_url=cdp_url)
else:
# Use local browser
logger.debug(f'Browser setup: Initializing BrowserSession for task {task.task_id}')
browser_session = BrowserSession(browser_profile=profile)
# Start browser session
await browser_session.start()
logger.debug(f'Browser setup: Browser session started for task {task.task_id}')
# Navigate to task starting url if provided
# if task.website:
# logger.debug(f'Browser setup: Navigating to {task.website} for task {task.task_id}')
# await browser_session.navigate(task.website)
logger.debug(f'Browser setup: Setup completed for task {task.task_id}')
return browser_session