From 76edbd1c5ca1fb47cb50acfd2a1553c39c83f7d3 Mon Sep 17 00:00:00 2001 From: Derek Meegan Date: Mon, 28 Jul 2025 12:22:44 -0700 Subject: [PATCH 1/4] relax browserbase params and add pre hook to validator to add project id to browserbase params before field validation --- stagehand/config.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/stagehand/config.py b/stagehand/config.py index 8805e40b..bfe73955 100644 --- a/stagehand/config.py +++ b/stagehand/config.py @@ -1,8 +1,8 @@ import os -from typing import Any, Callable, Literal, Optional +from typing import Any, Callable, Literal, Optional, Union from browserbase.types import SessionCreateParams as BrowserbaseSessionCreateParams -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator from stagehand.schemas import AvailableModel @@ -71,7 +71,7 @@ class StagehandConfig(BaseModel): alias="domSettleTimeoutMs", description="Timeout for DOM to settle (in ms)", ) - browserbase_session_create_params: Optional[BrowserbaseSessionCreateParams] = Field( + browserbase_session_create_params: Optional[Union[BrowserbaseSessionCreateParams, dict[str, Any]]] = Field( None, alias="browserbaseSessionCreateParams", description="Browserbase session create params", @@ -117,6 +117,17 @@ class StagehandConfig(BaseModel): ) model_config = ConfigDict(populate_by_name=True) + + @field_validator('browserbase_session_create_params', mode='before') + @classmethod + def validate_browserbase_params(cls, v, info): + """Validate and convert browserbase session create params.""" + if isinstance(v, dict) and 'project_id' not in v: + values = info.data + project_id = values.get('project_id') or values.get('projectId') + if project_id: + v = {**v, 'project_id': project_id} + return v def with_overrides(self, **overrides) -> "StagehandConfig": """ From cdc5c49b167b49b67ef93ae528a7658bd6547d80 Mon Sep 17 00:00:00 2001 From: Chris Read Date: Sun, 7 Sep 2025 10:27:07 -0700 Subject: [PATCH 2/4] fix 1) formatting 2) test for api key --- stagehand/config.py | 14 ++++++++------ tests/integration/local/test_core_local.py | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/stagehand/config.py b/stagehand/config.py index bfe73955..e69faa20 100644 --- a/stagehand/config.py +++ b/stagehand/config.py @@ -71,7 +71,9 @@ class StagehandConfig(BaseModel): alias="domSettleTimeoutMs", description="Timeout for DOM to settle (in ms)", ) - browserbase_session_create_params: Optional[Union[BrowserbaseSessionCreateParams, dict[str, Any]]] = Field( + browserbase_session_create_params: Optional[ + Union[BrowserbaseSessionCreateParams, dict[str, Any]] + ] = Field( None, alias="browserbaseSessionCreateParams", description="Browserbase session create params", @@ -117,16 +119,16 @@ class StagehandConfig(BaseModel): ) model_config = ConfigDict(populate_by_name=True) - - @field_validator('browserbase_session_create_params', mode='before') + + @field_validator("browserbase_session_create_params", mode="before") @classmethod def validate_browserbase_params(cls, v, info): """Validate and convert browserbase session create params.""" - if isinstance(v, dict) and 'project_id' not in v: + if isinstance(v, dict) and "project_id" not in v: values = info.data - project_id = values.get('project_id') or values.get('projectId') + project_id = values.get("project_id") or values.get("projectId") if project_id: - v = {**v, 'project_id': project_id} + v = {**v, "project_id": project_id} return v def with_overrides(self, **overrides) -> "StagehandConfig": diff --git a/tests/integration/local/test_core_local.py b/tests/integration/local/test_core_local.py index 0eb11aba..c1190ea3 100644 --- a/tests/integration/local/test_core_local.py +++ b/tests/integration/local/test_core_local.py @@ -20,7 +20,7 @@ def local_config(self): self_heal=True, wait_for_captcha_solves=False, system_prompt="You are a browser automation assistant for testing purposes.", - model_client_options={"apiKey": os.getenv("MODEL_API_KEY")}, + model_client_options={"apiKey": os.getenv("MODEL_API_KEY") or os.getenv("OPENAI_API_KEY")}, use_api=False, ) From b6cdd49891de2108d726a88368513ed2ae36e49e Mon Sep 17 00:00:00 2001 From: Chris Read Date: Wed, 10 Sep 2025 01:41:48 -0700 Subject: [PATCH 3/4] add changeset --- .changeset/inventive-tangible-hyena.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/inventive-tangible-hyena.md diff --git a/.changeset/inventive-tangible-hyena.md b/.changeset/inventive-tangible-hyena.md new file mode 100644 index 00000000..cec9d424 --- /dev/null +++ b/.changeset/inventive-tangible-hyena.md @@ -0,0 +1,5 @@ +--- +"stagehand": patch +--- + +remove duplicate project id if already passed to Stagehand From cd9d4e68714beef30b8fc7e58726f81fc85c74f8 Mon Sep 17 00:00:00 2001 From: Miguel <36487034+miguelg719@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:07:43 -0700 Subject: [PATCH 4/4] Update tests/integration/local/test_core_local.py --- tests/integration/local/test_core_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/local/test_core_local.py b/tests/integration/local/test_core_local.py index c1190ea3..0eb11aba 100644 --- a/tests/integration/local/test_core_local.py +++ b/tests/integration/local/test_core_local.py @@ -20,7 +20,7 @@ def local_config(self): self_heal=True, wait_for_captcha_solves=False, system_prompt="You are a browser automation assistant for testing purposes.", - model_client_options={"apiKey": os.getenv("MODEL_API_KEY") or os.getenv("OPENAI_API_KEY")}, + model_client_options={"apiKey": os.getenv("MODEL_API_KEY")}, use_api=False, )