Skip to content

Commit ef9483c

Browse files
authored
enable env browserbase for multi-region (#100)
* enable env browserbase for multi-region * address comments * update .env.example
1 parent 0c54d5c commit ef9483c

File tree

8 files changed

+73
-37
lines changed

8 files changed

+73
-37
lines changed

.env.example

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
MODEL_API_KEY = "your-favorite-llm-api-key"
22
BROWSERBASE_API_KEY = "browserbase-api-key"
33
BROWSERBASE_PROJECT_ID = "browserbase-project-id"
4-
STAGEHAND_API_URL = "api_url"
54
STAGEHAND_ENV= "LOCAL or BROWSERBASE"

.github/workflows/publish.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ jobs:
2323
runs-on: ubuntu-latest
2424
steps:
2525
- name: Check out repository
26-
uses: actions/checkout@v3
26+
uses: actions/checkout@v4
2727
with:
2828
fetch-depth: 0
2929

3030
- name: Set up Python
3131
uses: actions/setup-python@v4
3232
with:
33-
python-version: '3.10'
33+
python-version: '3.11'
3434

3535
- name: Install dependencies
3636
run: |
3737
python -m pip install --upgrade pip
38-
pip install build twine wheel setuptools ruff black tomllib
38+
pip install build twine wheel setuptools ruff black
3939
pip install -r requirements.txt
4040
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
4141

stagehand/agent/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(self, stagehand_client, **kwargs):
3636
self.stagehand = stagehand_client
3737
self.config = AgentConfig(**kwargs) if kwargs else AgentConfig()
3838
self.logger = self.stagehand.logger
39-
if self.stagehand.env == "BROWSERBASE":
39+
if self.stagehand.use_api:
4040
if self.config.model in MODEL_TO_PROVIDER_MAP:
4141
self.provider = MODEL_TO_PROVIDER_MAP[self.config.model]
4242
else:
@@ -120,7 +120,7 @@ async def execute(
120120

121121
instruction = options.instruction
122122

123-
if self.stagehand.env == "LOCAL":
123+
if not self.stagehand.use_api:
124124
self.logger.info(
125125
f"Agent starting execution for instruction: '{instruction}'",
126126
category="agent",

stagehand/api.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ async def _create_session(self):
4141
},
4242
}
4343
),
44-
"proxies": True,
4544
}
4645

4746
# Add the new parameters if they have values

stagehand/browser.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Any, Optional
77

88
from browserbase import Browserbase
9+
from browserbase.types import SessionCreateParams as BrowserbaseSessionCreateParams
910
from playwright.async_api import (
1011
Browser,
1112
BrowserContext,
@@ -40,11 +41,30 @@ async def connect_browserbase_browser(
4041
# Connect to remote browser via Browserbase SDK and CDP
4142
bb = Browserbase(api_key=browserbase_api_key)
4243
try:
43-
session = bb.sessions.retrieve(session_id)
44-
if session.status != "RUNNING":
45-
raise RuntimeError(
46-
f"Browserbase session {session_id} is not running (status: {session.status})"
44+
if session_id:
45+
session = bb.sessions.retrieve(session_id)
46+
if session.status != "RUNNING":
47+
raise RuntimeError(
48+
f"Browserbase session {session_id} is not running (status: {session.status})"
49+
)
50+
else:
51+
browserbase_session_create_params = (
52+
BrowserbaseSessionCreateParams(
53+
project_id=stagehand_instance.browserbase_project_id,
54+
browser_settings={
55+
"viewport": {
56+
"width": 1024,
57+
"height": 768,
58+
},
59+
},
60+
)
61+
if not stagehand_instance.browserbase_session_create_params
62+
else stagehand_instance.browserbase_session_create_params
4763
)
64+
session = bb.sessions.create(**browserbase_session_create_params)
65+
if not session.id:
66+
raise Exception("Could not create Browserbase session")
67+
stagehand_instance.session_id = session.id
4868
connect_url = session.connectUrl
4969
except Exception as e:
5070
logger.error(f"Error retrieving or validating Browserbase session: {str(e)}")

stagehand/config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ class StagehandConfig(BaseModel):
9696
alias="localBrowserLaunchOptions",
9797
description="Local browser launch options",
9898
)
99+
use_api: Optional[bool] = Field(
100+
True,
101+
alias=None,
102+
description="Whether to use the Stagehand API",
103+
)
104+
experimental: Optional[bool] = Field(
105+
False,
106+
alias=None,
107+
description="Whether to use experimental features",
108+
)
99109

100110
model_config = ConfigDict(populate_by_name=True)
101111

stagehand/main.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,23 @@ def __init__(
168168
self._playwright_page: Optional[PlaywrightPage] = None
169169
self.page: Optional[StagehandPage] = None
170170
self.context: Optional[StagehandContext] = None
171+
self.use_api = self.config.use_api
172+
self.experimental = self.config.experimental
173+
if self.experimental:
174+
self.use_api = False
175+
if (
176+
self.browserbase_session_create_params
177+
and self.browserbase_session_create_params.get("region")
178+
and self.browserbase_session_create_params.get("region") != "us-west-2"
179+
):
180+
self.use_api = False
171181

172182
self._initialized = False # Flag to track if init() has run
173183
self._closed = False # Flag to track if resources have been closed
174184

175185
# Setup LLM client if LOCAL mode
176186
self.llm = None
177-
if self.env == "LOCAL":
187+
if not self.use_api:
178188
self.llm = LLMClient(
179189
stagehand_logger=self.logger,
180190
api_key=self.model_api_key,
@@ -385,15 +395,16 @@ async def init(self):
385395

386396
if self.env == "BROWSERBASE":
387397
# Create session if we don't have one
388-
if not self.session_id:
389-
await self._create_session() # Uses self._client and api_url
390-
self.logger.debug(
391-
f"Created new Browserbase session via Stagehand server: {self.session_id}"
392-
)
393-
else:
394-
self.logger.debug(
395-
f"Using existing Browserbase session: {self.session_id}"
396-
)
398+
if self.use_api:
399+
if not self.session_id:
400+
await self._create_session() # Uses self._client and api_url
401+
self.logger.debug(
402+
f"Created new Browserbase session via Stagehand server: {self.session_id}"
403+
)
404+
else:
405+
self.logger.debug(
406+
f"Using existing Browserbase session: {self.session_id}"
407+
)
397408

398409
# Connect to remote browser
399410
try:
@@ -470,8 +481,8 @@ async def close(self):
470481

471482
self.logger.debug("Closing resources...")
472483

473-
if self.env == "BROWSERBASE":
474-
# --- BROWSERBASE Cleanup ---
484+
if self.use_api:
485+
# --- BROWSERBASE Cleanup (API) ---
475486
# End the session on the server if we have a session ID
476487
if self.session_id and self._client: # Check if client was initialized
477488
try:

stagehand/page.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ async def goto(
8080
Returns:
8181
The result from the Stagehand server's navigation execution.
8282
"""
83-
if self._stagehand.env == "LOCAL":
83+
if not self._stagehand.use_api:
8484
await self._page.goto(
8585
url, referer=referer, timeout=timeout, wait_until=wait_until
8686
)
@@ -142,7 +142,7 @@ async def act(
142142
)
143143

144144
# TODO: Temporary until we move api based logic to client
145-
if self._stagehand.env == "LOCAL":
145+
if not self._stagehand.use_api:
146146
# TODO: revisit passing user_provided_instructions
147147
if not hasattr(self, "_observe_handler"):
148148
# TODO: revisit handlers initialization on page creation
@@ -207,7 +207,7 @@ async def observe(
207207
payload = options_obj.model_dump(exclude_none=True, by_alias=True)
208208

209209
# If in LOCAL mode, use local implementation
210-
if self._stagehand.env == "LOCAL":
210+
if not self._stagehand.use_api:
211211
self._stagehand.logger.debug(
212212
"observe", category="observe", auxiliary=payload
213213
)
@@ -324,8 +324,7 @@ async def extract(
324324
else:
325325
schema_to_validate_with = DefaultExtractSchema
326326

327-
# If in LOCAL mode, use local implementation
328-
if self._stagehand.env == "LOCAL":
327+
if not self._stagehand.use_api:
329328
# If we don't have an extract handler yet, create one
330329
if not hasattr(self, "_extract_handler"):
331330
self._extract_handler = ExtractHandler(
@@ -391,18 +390,16 @@ async def screenshot(self, options: Optional[dict] = None) -> str:
391390
Returns:
392391
str: Base64-encoded screenshot data.
393392
"""
394-
if self._stagehand.env == "LOCAL":
395-
self._stagehand.logger.info(
396-
"Local execution of screenshot is not implemented"
397-
)
398-
return None
399393
payload = options or {}
400394

401-
lock = self._stagehand._get_lock_for_session()
402-
async with lock:
403-
result = await self._stagehand._execute("screenshot", payload)
395+
if self._stagehand.use_api:
396+
lock = self._stagehand._get_lock_for_session()
397+
async with lock:
398+
result = await self._stagehand._execute("screenshot", payload)
404399

405-
return result
400+
return result
401+
else:
402+
return await self._page.screenshot(options)
406403

407404
# Method to get or initialize the persistent CDP client
408405
async def get_cdp_client(self) -> CDPSession:

0 commit comments

Comments
 (0)