Skip to content

Commit ae19586

Browse files
committed
consolidate base-client
1 parent f2e66d5 commit ae19586

File tree

7 files changed

+97
-214
lines changed

7 files changed

+97
-214
lines changed

stagehand/base.py

Lines changed: 0 additions & 169 deletions
This file was deleted.

stagehand/browser.py

Whitespace-only changes.

stagehand/client.py

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import httpx
1212
from browserbase import Browserbase
13+
from browserbase.types import SessionCreateParams as BrowserbaseSessionCreateParams
1314
from dotenv import load_dotenv
1415
from playwright.async_api import (
1516
BrowserContext,
@@ -18,18 +19,17 @@
1819
)
1920
from playwright.async_api import Page as PlaywrightPage
2021

21-
from .base import StagehandBase
2222
from .config import StagehandConfig
2323
from .context import StagehandContext
2424
from .llm import LLMClient
2525
from .metrics import StagehandFunctionName, StagehandMetrics
2626
from .page import StagehandPage
27-
from .utils import StagehandLogger, convert_dict_keys_to_camel_case
27+
from .utils import StagehandLogger, convert_dict_keys_to_camel_case, default_log_handler
2828

2929
load_dotenv()
3030

3131

32-
class Stagehand(StagehandBase):
32+
class Stagehand:
3333
"""
3434
Python client for interacting with a running Stagehand server and Browserbase remote headless browser.
3535
@@ -42,6 +42,7 @@ class Stagehand(StagehandBase):
4242

4343
def __init__(
4444
self,
45+
*,
4546
config: Optional[StagehandConfig] = None,
4647
server_url: Optional[str] = None,
4748
session_id: Optional[str] = None,
@@ -61,7 +62,8 @@ def __init__(
6162
system_prompt: Optional[str] = None,
6263
use_rich_logging: bool = True,
6364
env: Literal["BROWSERBASE", "LOCAL"] = None,
64-
local_browser_launch_options: Optional[dict[str, Any]] = None,
65+
local_browser_launch_options: Optional[dict[str, Any]] = dict(),
66+
browserbase_session_create_params: Optional[BrowserbaseSessionCreateParams] = None,
6567
):
6668
"""
6769
Initialize the Stagehand client.
@@ -89,24 +91,74 @@ def __init__(
8991
local_browser_launch_options (Optional[dict[str, Any]]): Options for launching the local browser context
9092
when env="LOCAL". See Playwright's launch_persistent_context documentation.
9193
Common keys: 'headless', 'user_data_dir', 'downloads_path', 'viewport', 'locale', 'proxy', 'args', 'cdp_url'.
94+
browserbase_session_create_params (Optional[BrowserbaseSessionCreateParams]): Params for Browserbase session creation.
9295
"""
93-
super().__init__(
94-
config=config,
95-
server_url=server_url,
96-
session_id=session_id,
97-
browserbase_api_key=browserbase_api_key,
98-
browserbase_project_id=browserbase_project_id,
99-
model_api_key=model_api_key,
100-
on_log=on_log,
101-
verbose=verbose,
102-
model_name=model_name,
103-
dom_settle_timeout_ms=dom_settle_timeout_ms,
104-
timeout_settings=timeout_settings,
105-
stream_response=stream_response,
106-
model_client_options=model_client_options,
107-
self_heal=self_heal,
108-
wait_for_captcha_solves=wait_for_captcha_solves,
109-
system_prompt=system_prompt,
96+
# Initialize configuration from config object or individual parameters
97+
self.server_url = server_url or os.getenv("STAGEHAND_SERVER_URL")
98+
99+
if config:
100+
self.browserbase_api_key = (
101+
config.api_key
102+
or browserbase_api_key
103+
or os.getenv("BROWSERBASE_API_KEY")
104+
)
105+
self.browserbase_project_id = (
106+
config.project_id
107+
or browserbase_project_id
108+
or os.getenv("BROWSERBASE_PROJECT_ID")
109+
)
110+
self.session_id = config.browserbase_session_id or session_id
111+
self.model_name = config.model_name or model_name
112+
self.dom_settle_timeout_ms = (
113+
config.dom_settle_timeout_ms or dom_settle_timeout_ms
114+
)
115+
self.self_heal = (
116+
config.self_heal if config.self_heal is not None else self_heal
117+
)
118+
self.wait_for_captcha_solves = (
119+
config.wait_for_captcha_solves
120+
if config.wait_for_captcha_solves is not None
121+
else wait_for_captcha_solves
122+
)
123+
self.system_prompt = config.system_prompt or system_prompt
124+
self.browserbase_session_create_params = (
125+
config.browserbase_session_create_params
126+
or browserbase_session_create_params
127+
)
128+
self.verbose = config.verbose if config.verbose is not None else verbose
129+
else:
130+
self.browserbase_api_key = browserbase_api_key or os.getenv(
131+
"BROWSERBASE_API_KEY"
132+
)
133+
self.browserbase_project_id = browserbase_project_id or os.getenv(
134+
"BROWSERBASE_PROJECT_ID"
135+
)
136+
self.session_id = session_id
137+
self.model_name = model_name
138+
self.dom_settle_timeout_ms = dom_settle_timeout_ms
139+
self.self_heal = self_heal
140+
self.wait_for_captcha_solves = wait_for_captcha_solves
141+
self.system_prompt = system_prompt
142+
self.browserbase_session_create_params = browserbase_session_create_params
143+
self.verbose = verbose
144+
145+
# Handle model-related settings directly
146+
self.model_api_key = model_api_key or os.getenv("MODEL_API_KEY")
147+
self.model_client_options = model_client_options or {}
148+
if self.model_api_key and "apiKey" not in self.model_client_options:
149+
self.model_client_options["apiKey"] = self.model_api_key
150+
151+
# Handle streaming response setting directly
152+
self.streamed_response = (
153+
stream_response if stream_response is not None else True
154+
)
155+
156+
self.on_log = on_log or default_log_handler
157+
self.timeout_settings = timeout_settings or httpx.Timeout(
158+
connect=180.0,
159+
read=180.0,
160+
write=180.0,
161+
pool=180.0,
110162
)
111163

112164
self.env = env.upper() if env else "BROWSERBASE"
@@ -127,6 +179,11 @@ def __init__(
127179
if self.env not in ["BROWSERBASE", "LOCAL"]:
128180
raise ValueError("env must be either 'BROWSERBASE' or 'LOCAL'")
129181

182+
# Initialize the centralized logger with the specified verbosity
183+
self.logger = StagehandLogger(
184+
verbose=self.verbose, external_logger=on_log, use_rich=use_rich_logging
185+
)
186+
130187
# If using BROWSERBASE, session_id or creation params are needed
131188
if self.env == "BROWSERBASE":
132189
if not self.session_id:
@@ -155,18 +212,7 @@ def __init__(
155212
"browserbase_project_id is required for BROWSERBASE env with existing session_id (or set BROWSERBASE_PROJECT_ID in env)."
156213
)
157214

158-
# Initialize the centralized logger with the specified verbosity
159-
self.logger = StagehandLogger(
160-
verbose=self.verbose, external_logger=on_log, use_rich=use_rich_logging
161-
)
162-
163215
self.httpx_client = httpx_client
164-
self.timeout_settings = timeout_settings or httpx.Timeout(
165-
connect=180.0,
166-
read=180.0,
167-
write=180.0,
168-
pool=180.0,
169-
)
170216
self._client: Optional[httpx.AsyncClient] = (
171217
None # Used for server communication in BROWSERBASE
172218
)

stagehand/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class StagehandConfig(BaseModel):
7777
description="System prompt to use for LLM interactions",
7878
)
7979
local_browser_launch_options: Optional[dict[str, Any]] = Field(
80-
None,
80+
{},
8181
alias="localBrowserLaunchOptions",
8282
description="Local browser launch options",
8383
)

stagehand/context.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,14 @@ async def wrapped_new_page(*args, **kwargs):
8787

8888
return wrapped_new_page
8989
elif name == "pages":
90-
# Wrap the pages method to return StagehandPage objects
91-
def wrapped_pages(*args, **kwargs):
92-
pw_pages = self._context.pages(*args, **kwargs)
93-
# This is synchronous, so we can't await here
94-
# We'll return the unwrapped pages and let the caller handle wrapping if needed
95-
return pw_pages
90+
async def wrapped_pages():
91+
pw_pages = self._context.pages
92+
# Return StagehandPage objects
93+
result = []
94+
for pw_page in pw_pages:
95+
stagehand_page = await self.get_stagehand_page(pw_page)
96+
result.append(stagehand_page)
97+
return result
9698

9799
return wrapped_pages
98100
return attr

stagehand/handlers/act_handler_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ async def handle_possible_page_navigation(
444444
},
445445
)
446446

447-
if new_opened_tab:
447+
if new_opened_tab and new_opened_tab.url != 'about:blank':
448448
logger.info(
449449
message="new page detected (new tab) with URL",
450450
category="action",

stagehand/sync/context.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,15 @@ def wrapped_new_page(*args, **kwargs):
8787

8888
return wrapped_new_page
8989
elif name == "pages":
90-
# Wrap the pages method to return StagehandPage objects
91-
def wrapped_pages(*args, **kwargs):
92-
pw_pages = self._context.pages(*args, **kwargs)
93-
# We'll return the unwrapped pages and let the caller handle wrapping if needed
94-
return pw_pages
90+
# Handle pages as a property, not a method
91+
def wrapped_pages():
92+
pw_pages = self._context.pages # This is a property, not a method
93+
# Return SyncStagehandPage objects
94+
result = []
95+
for pw_page in pw_pages:
96+
stagehand_page = self.get_stagehand_page(pw_page)
97+
result.append(stagehand_page)
98+
return result
9599

96100
return wrapped_pages
97101
return attr

0 commit comments

Comments
 (0)