From feac39d88a841ff710d672b622f3280037589f66 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:41:09 +0000 Subject: [PATCH 1/6] fix(client): preserve hardcoded query params when merging with user params --- src/stagehand/_base_client.py | 4 +++ tests/test_client.py | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/stagehand/_base_client.py b/src/stagehand/_base_client.py index 951feb82..9af0033b 100644 --- a/src/stagehand/_base_client.py +++ b/src/stagehand/_base_client.py @@ -540,6 +540,10 @@ def _build_request( files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} diff --git a/tests/test_client.py b/tests/test_client.py index e962cdba..37ca5a76 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -505,6 +505,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: Stagehand) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Stagehand) -> None: request = client._build_request( FinalRequestOptions( @@ -1552,6 +1576,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncStagehand) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Stagehand) -> None: request = client._build_request( FinalRequestOptions( From 9463fa49cb839abbb2c6a1adb0d053e5006216a7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:22:47 +0000 Subject: [PATCH 2/6] feat: Bedrock auth passthrough --- .stats.yml | 4 +- src/stagehand/resources/sessions.py | 10 + src/stagehand/types/model_config_param.py | 101 ++++++++- src/stagehand/types/session_execute_params.py | 6 - src/stagehand/types/session_start_params.py | 193 ++++++++++++++++++ src/stagehand/types/stream_event.py | 2 +- tests/api_resources/test_sessions.py | 142 ++++++++----- 7 files changed, 397 insertions(+), 61 deletions(-) diff --git a/.stats.yml b/.stats.yml index ade26eb1..f1d6429f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-b969ce378479c79ee64c05127c0ed6c6ce2edbee017ecd037242fb618a5ebc9f.yml -openapi_spec_hash: a24aabaa5214effb679808b7f2be0ad4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-7773ef4ca29c983daafb787ee918cfa6b5b12c5bbdc088308653f2737c26e51f.yml +openapi_spec_hash: 47fc8f2540be0b6374e4230c021072d9 config_hash: 0cc516caf1432087f40654336e0fa8cd diff --git a/src/stagehand/resources/sessions.py b/src/stagehand/resources/sessions.py index a3c3c4bd..d24424c6 100644 --- a/src/stagehand/resources/sessions.py +++ b/src/stagehand/resources/sessions.py @@ -927,6 +927,7 @@ def start( browserbase_session_id: str | Omit = omit, dom_settle_timeout_ms: float | Omit = omit, experimental: bool | Omit = omit, + model_client_options: session_start_params.ModelClientOptions | Omit = omit, self_heal: bool | Omit = omit, system_prompt: str | Omit = omit, verbose: Literal[0, 1, 2] | Omit = omit, @@ -957,6 +958,9 @@ def start( dom_settle_timeout_ms: Timeout in ms to wait for DOM to settle + model_client_options: Optional provider-specific configuration for the session model (for example + Bedrock region and credentials) + self_heal: Enable self-healing for failed actions system_prompt: Custom system prompt for AI operations @@ -997,6 +1001,7 @@ def start( "browserbase_session_id": browserbase_session_id, "dom_settle_timeout_ms": dom_settle_timeout_ms, "experimental": experimental, + "model_client_options": model_client_options, "self_heal": self_heal, "system_prompt": system_prompt, "verbose": verbose, @@ -1890,6 +1895,7 @@ async def start( browserbase_session_id: str | Omit = omit, dom_settle_timeout_ms: float | Omit = omit, experimental: bool | Omit = omit, + model_client_options: session_start_params.ModelClientOptions | Omit = omit, self_heal: bool | Omit = omit, system_prompt: str | Omit = omit, verbose: Literal[0, 1, 2] | Omit = omit, @@ -1920,6 +1926,9 @@ async def start( dom_settle_timeout_ms: Timeout in ms to wait for DOM to settle + model_client_options: Optional provider-specific configuration for the session model (for example + Bedrock region and credentials) + self_heal: Enable self-healing for failed actions system_prompt: Custom system prompt for AI operations @@ -1960,6 +1969,7 @@ async def start( "browserbase_session_id": browserbase_session_id, "dom_settle_timeout_ms": dom_settle_timeout_ms, "experimental": experimental, + "model_client_options": model_client_options, "self_heal": self_heal, "system_prompt": system_prompt, "verbose": verbose, diff --git a/src/stagehand/types/model_config_param.py b/src/stagehand/types/model_config_param.py index 3ed433f7..a725892c 100644 --- a/src/stagehand/types/model_config_param.py +++ b/src/stagehand/types/model_config_param.py @@ -2,12 +2,92 @@ from __future__ import annotations -from typing import Dict -from typing_extensions import Literal, Required, Annotated, TypedDict +from typing import Dict, Union +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._utils import PropertyInfo -__all__ = ["ModelConfigParam"] +__all__ = [ + "ModelConfigParam", + "ProviderOptions", + "ProviderOptionsBedrockAPIKeyProviderOptions", + "ProviderOptionsBedrockAwsCredentialsProviderOptions", + "ProviderOptionsGoogleVertexProviderOptions", + "ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions", + "ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials", +] + + +class ProviderOptionsBedrockAPIKeyProviderOptions(TypedDict, total=False): + region: Required[str] + """AWS region for Amazon Bedrock""" + + +class ProviderOptionsBedrockAwsCredentialsProviderOptions(TypedDict, total=False): + access_key_id: Required[Annotated[str, PropertyInfo(alias="accessKeyId")]] + """AWS access key ID for Bedrock""" + + region: Required[str] + """AWS region for Amazon Bedrock""" + + secret_access_key: Required[Annotated[str, PropertyInfo(alias="secretAccessKey")]] + """AWS secret access key for Bedrock""" + + session_token: Annotated[str, PropertyInfo(alias="sessionToken")] + """Optional AWS session token for temporary credentials""" + + +class ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials(TypedDict, total=False): + auth_provider_x509_cert_url: str + + auth_uri: str + + client_email: str + + client_id: str + + client_x509_cert_url: str + + private_key: str + + private_key_id: str + + project_id: str + + token_uri: str + + type: str + + universe_domain: str + + +class ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions(TypedDict, total=False): + """Optional Google auth options for Vertex AI""" + + credentials: ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials + + +class ProviderOptionsGoogleVertexProviderOptions(TypedDict, total=False): + google_auth_options: Annotated[ + ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions, PropertyInfo(alias="googleAuthOptions") + ] + """Optional Google auth options for Vertex AI""" + + headers: Dict[str, str] + """Custom headers for Vertex AI requests""" + + location: str + """Google Cloud location for Vertex AI""" + + project: str + """Google Cloud project ID for Vertex AI""" + + +ProviderOptions: TypeAlias = Union[ + ProviderOptionsBedrockAPIKeyProviderOptions, + ProviderOptionsBedrockAwsCredentialsProviderOptions, + ProviderOptionsGoogleVertexProviderOptions, +] class ModelConfigParam(TypedDict, total=False): @@ -21,7 +101,20 @@ class ModelConfigParam(TypedDict, total=False): """Base URL for the model provider""" headers: Dict[str, str] - """Custom headers sent with every request to the model provider""" + """Custom headers for the model provider""" provider: Literal["openai", "anthropic", "google", "microsoft", "bedrock"] """AI provider for the model (or provide a baseURL endpoint instead)""" + + provider_options: Annotated[ProviderOptions, PropertyInfo(alias="providerOptions")] + """Provider-specific options passed through to the AI SDK provider constructor. + + For Bedrock: { region, accessKeyId, secretAccessKey, sessionToken }. For Vertex: + { project, location, googleAuthOptions }. + """ + + skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] + """When true, hosted sessions will not copy x-model-api-key into model.apiKey. + + Use this when auth is carried through providerOptions instead of an API key. + """ diff --git a/src/stagehand/types/session_execute_params.py b/src/stagehand/types/session_execute_params.py index 944c3856..4b64c0b2 100644 --- a/src/stagehand/types/session_execute_params.py +++ b/src/stagehand/types/session_execute_params.py @@ -76,12 +76,6 @@ class ExecuteOptions(TypedDict, total=False): max_steps: Annotated[float, PropertyInfo(alias="maxSteps")] """Maximum number of steps the agent can take""" - tool_timeout: Annotated[float, PropertyInfo(alias="toolTimeout")] - """Timeout in milliseconds for each agent tool call""" - - use_search: Annotated[bool, PropertyInfo(alias="useSearch")] - """Whether to enable the web search tool powered by Browserbase Search API""" - class SessionExecuteParamsNonStreaming(SessionExecuteParamsBase, total=False): stream_response: Annotated[Literal[False], PropertyInfo(alias="streamResponse")] diff --git a/src/stagehand/types/session_start_params.py b/src/stagehand/types/session_start_params.py index 17f55997..3eeb7fca 100644 --- a/src/stagehand/types/session_start_params.py +++ b/src/stagehand/types/session_start_params.py @@ -24,6 +24,18 @@ "BrowserbaseSessionCreateParamsProxiesProxyConfigListBrowserbaseProxyConfig", "BrowserbaseSessionCreateParamsProxiesProxyConfigListBrowserbaseProxyConfigGeolocation", "BrowserbaseSessionCreateParamsProxiesProxyConfigListExternalProxyConfig", + "ModelClientOptions", + "ModelClientOptionsBedrockAPIKeyModelClientOptions", + "ModelClientOptionsBedrockAPIKeyModelClientOptionsProviderOptions", + "ModelClientOptionsBedrockAwsCredentialsModelClientOptions", + "ModelClientOptionsBedrockAwsCredentialsModelClientOptionsProviderOptions", + "ModelClientOptionsGenericModelClientOptions", + "ModelClientOptionsGenericModelClientOptionsProviderOptions", + "ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAPIKeyProviderOptions", + "ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAwsCredentialsProviderOptions", + "ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptions", + "ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions", + "ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials", ] @@ -48,6 +60,12 @@ class SessionStartParams(TypedDict, total=False): experimental: bool + model_client_options: Annotated[ModelClientOptions, PropertyInfo(alias="modelClientOptions")] + """ + Optional provider-specific configuration for the session model (for example + Bedrock region and credentials) + """ + self_heal: Annotated[bool, PropertyInfo(alias="selfHeal")] """Enable self-healing for failed actions""" @@ -240,3 +258,178 @@ class BrowserbaseSessionCreateParams(TypedDict, total=False): timeout: float user_metadata: Annotated[Dict[str, object], PropertyInfo(alias="userMetadata")] + + +class ModelClientOptionsBedrockAPIKeyModelClientOptionsProviderOptions(TypedDict, total=False): + region: Required[str] + """AWS region for Amazon Bedrock""" + + +class ModelClientOptionsBedrockAPIKeyModelClientOptions(TypedDict, total=False): + api_key: Required[Annotated[str, PropertyInfo(alias="apiKey")]] + """Short-term Bedrock API key for bearer-token auth""" + + provider_options: Required[ + Annotated[ + ModelClientOptionsBedrockAPIKeyModelClientOptionsProviderOptions, PropertyInfo(alias="providerOptions") + ] + ] + + base_url: Annotated[str, PropertyInfo(alias="baseURL")] + """Base URL for the model provider""" + + headers: Dict[str, str] + """Custom headers for the model provider""" + + skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] + """When true, hosted sessions will not copy x-model-api-key into model.apiKey. + + Use this when auth is carried through providerOptions instead of an API key. + """ + + +class ModelClientOptionsBedrockAwsCredentialsModelClientOptionsProviderOptions(TypedDict, total=False): + access_key_id: Required[Annotated[str, PropertyInfo(alias="accessKeyId")]] + """AWS access key ID for Bedrock""" + + region: Required[str] + """AWS region for Amazon Bedrock""" + + secret_access_key: Required[Annotated[str, PropertyInfo(alias="secretAccessKey")]] + """AWS secret access key for Bedrock""" + + session_token: Annotated[str, PropertyInfo(alias="sessionToken")] + """Optional AWS session token for temporary credentials""" + + +class ModelClientOptionsBedrockAwsCredentialsModelClientOptions(TypedDict, total=False): + provider_options: Required[ + Annotated[ + ModelClientOptionsBedrockAwsCredentialsModelClientOptionsProviderOptions, + PropertyInfo(alias="providerOptions"), + ] + ] + + base_url: Annotated[str, PropertyInfo(alias="baseURL")] + """Base URL for the model provider""" + + headers: Dict[str, str] + """Custom headers for the model provider""" + + skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] + """When true, hosted sessions will not copy x-model-api-key into model.apiKey. + + Use this when auth is carried through providerOptions instead of an API key. + """ + + +class ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAPIKeyProviderOptions(TypedDict, total=False): + region: Required[str] + """AWS region for Amazon Bedrock""" + + +class ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAwsCredentialsProviderOptions( + TypedDict, total=False +): + access_key_id: Required[Annotated[str, PropertyInfo(alias="accessKeyId")]] + """AWS access key ID for Bedrock""" + + region: Required[str] + """AWS region for Amazon Bedrock""" + + secret_access_key: Required[Annotated[str, PropertyInfo(alias="secretAccessKey")]] + """AWS secret access key for Bedrock""" + + session_token: Annotated[str, PropertyInfo(alias="sessionToken")] + """Optional AWS session token for temporary credentials""" + + +class ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials( + TypedDict, total=False +): + auth_provider_x509_cert_url: str + + auth_uri: str + + client_email: str + + client_id: str + + client_x509_cert_url: str + + private_key: str + + private_key_id: str + + project_id: str + + token_uri: str + + type: str + + universe_domain: str + + +class ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions( + TypedDict, total=False +): + """Optional Google auth options for Vertex AI""" + + credentials: ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials + + +class ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptions(TypedDict, total=False): + google_auth_options: Annotated[ + ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions, + PropertyInfo(alias="googleAuthOptions"), + ] + """Optional Google auth options for Vertex AI""" + + headers: Dict[str, str] + """Custom headers for Vertex AI requests""" + + location: str + """Google Cloud location for Vertex AI""" + + project: str + """Google Cloud project ID for Vertex AI""" + + +ModelClientOptionsGenericModelClientOptionsProviderOptions: TypeAlias = Union[ + ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAPIKeyProviderOptions, + ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAwsCredentialsProviderOptions, + ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptions, +] + + +class ModelClientOptionsGenericModelClientOptions(TypedDict, total=False): + api_key: Annotated[str, PropertyInfo(alias="apiKey")] + """API key for the model provider""" + + base_url: Annotated[str, PropertyInfo(alias="baseURL")] + """Base URL for the model provider""" + + headers: Dict[str, str] + """Custom headers for the model provider""" + + provider_options: Annotated[ + ModelClientOptionsGenericModelClientOptionsProviderOptions, PropertyInfo(alias="providerOptions") + ] + """Provider-specific options passed through to the AI SDK provider constructor. + + For Bedrock: { region, accessKeyId, secretAccessKey, sessionToken }. For Vertex: + { project, location, googleAuthOptions }. + """ + + skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] + """When true, hosted sessions will not copy x-model-api-key into model.apiKey. + + Use this when auth is carried through providerOptions instead of an API key. + """ + + +ModelClientOptions: TypeAlias = Union[ + ModelClientOptionsBedrockAPIKeyModelClientOptions, + ModelClientOptionsBedrockAwsCredentialsModelClientOptions, + ModelClientOptionsGenericModelClientOptions, +] diff --git a/src/stagehand/types/stream_event.py b/src/stagehand/types/stream_event.py index 6fd47585..3d5b21ee 100644 --- a/src/stagehand/types/stream_event.py +++ b/src/stagehand/types/stream_event.py @@ -32,7 +32,7 @@ class DataStreamEventLogDataOutput(BaseModel): class StreamEvent(BaseModel): """Server-Sent Event emitted during streaming responses. - Events are sent as `event: \ndata: \n\n`, where the JSON payload has the shape `{ data, type, id }`. + Events are sent as `data: \n\n`. Key order: data (with status first), type, id. """ id: str diff --git a/tests/api_resources/test_sessions.py b/tests/api_resources/test_sessions.py index 8dfddbe2..f6058ceb 100644 --- a/tests/api_resources/test_sessions.py +++ b/tests/api_resources/test_sessions.py @@ -44,11 +44,13 @@ def test_method_act_with_all_params_overload_1(self, client: Stagehand) -> None: frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -121,11 +123,13 @@ def test_method_act_with_all_params_overload_2(self, client: Stagehand) -> None: frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -250,19 +254,23 @@ def test_method_execute_with_all_params_overload_1(self, client: Stagehand) -> N agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -271,8 +279,6 @@ def test_method_execute_with_all_params_overload_1(self, client: Stagehand) -> N "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, - "tool_timeout": 30000, - "use_search": True, }, frame_id="frameId", should_cache=True, @@ -348,19 +354,23 @@ def test_method_execute_with_all_params_overload_2(self, client: Stagehand) -> N agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -369,8 +379,6 @@ def test_method_execute_with_all_params_overload_2(self, client: Stagehand) -> N "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, - "tool_timeout": 30000, - "use_search": True, }, stream_response=True, frame_id="frameId", @@ -444,11 +452,13 @@ def test_method_extract_with_all_params_overload_1(self, client: Stagehand) -> N instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -512,11 +522,13 @@ def test_method_extract_with_all_params_overload_2(self, client: Stagehand) -> N instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -642,11 +654,13 @@ def test_method_observe_with_all_params_overload_1(self, client: Stagehand) -> N instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -716,11 +730,13 @@ def test_method_observe_with_all_params_overload_2(self, client: Stagehand) -> N instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -912,6 +928,13 @@ def test_method_start_with_all_params(self, client: Stagehand) -> None: browserbase_session_id="browserbaseSessionID", dom_settle_timeout_ms=5000, experimental=True, + model_client_options={ + "api_key": "bedrock-short-term-api-key", + "provider_options": {"region": "us-east-1"}, + "base_url": "https://api.openai.com/v1", + "headers": {"X-Custom-Header": "value"}, + "skip_api_key_fallback": True, + }, self_heal=True, system_prompt="systemPrompt", verbose=1, @@ -970,11 +993,13 @@ async def test_method_act_with_all_params_overload_1(self, async_client: AsyncSt frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -1047,11 +1072,13 @@ async def test_method_act_with_all_params_overload_2(self, async_client: AsyncSt frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -1176,19 +1203,23 @@ async def test_method_execute_with_all_params_overload_1(self, async_client: Asy agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -1197,8 +1228,6 @@ async def test_method_execute_with_all_params_overload_1(self, async_client: Asy "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, - "tool_timeout": 30000, - "use_search": True, }, frame_id="frameId", should_cache=True, @@ -1274,19 +1303,23 @@ async def test_method_execute_with_all_params_overload_2(self, async_client: Asy agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -1295,8 +1328,6 @@ async def test_method_execute_with_all_params_overload_2(self, async_client: Asy "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, - "tool_timeout": 30000, - "use_search": True, }, stream_response=True, frame_id="frameId", @@ -1370,11 +1401,13 @@ async def test_method_extract_with_all_params_overload_1(self, async_client: Asy instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -1438,11 +1471,13 @@ async def test_method_extract_with_all_params_overload_2(self, async_client: Asy instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -1568,11 +1603,13 @@ async def test_method_observe_with_all_params_overload_1(self, async_client: Asy instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -1642,11 +1679,13 @@ async def test_method_observe_with_all_params_overload_2(self, async_client: Asy instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5.4-mini", + "model_name": "openai/gpt-5-nano", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"foo": "string"}, + "headers": {"X-Custom-Header": "value"}, "provider": "openai", + "provider_options": {"region": "us-east-1"}, + "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -1838,6 +1877,13 @@ async def test_method_start_with_all_params(self, async_client: AsyncStagehand) browserbase_session_id="browserbaseSessionID", dom_settle_timeout_ms=5000, experimental=True, + model_client_options={ + "api_key": "bedrock-short-term-api-key", + "provider_options": {"region": "us-east-1"}, + "base_url": "https://api.openai.com/v1", + "headers": {"X-Custom-Header": "value"}, + "skip_api_key_fallback": True, + }, self_heal=True, system_prompt="systemPrompt", verbose=1, From 078ab5c76f13beee16e44d7eed5e3018f1cc5bd8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:32:51 +0000 Subject: [PATCH 3/6] feat: [STG-1798] feat: support Browserbase verified sessions --- .stats.yml | 6 +++--- src/stagehand/types/session_start_params.py | 8 ++++++++ tests/api_resources/test_sessions.py | 8 ++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index f1d6429f..6bde59e1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-7773ef4ca29c983daafb787ee918cfa6b5b12c5bbdc088308653f2737c26e51f.yml -openapi_spec_hash: 47fc8f2540be0b6374e4230c021072d9 -config_hash: 0cc516caf1432087f40654336e0fa8cd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-a8db51c6460b3daff67b35262517848a0d4e783c6805c2edd531b155a5db71dd.yml +openapi_spec_hash: c6e7127f211f946673d6389e1d8db1ba +config_hash: a962ae71493deb11a1c903256fb25386 diff --git a/src/stagehand/types/session_start_params.py b/src/stagehand/types/session_start_params.py index 3eeb7fca..7c66c693 100644 --- a/src/stagehand/types/session_start_params.py +++ b/src/stagehand/types/session_start_params.py @@ -193,6 +193,10 @@ class BrowserbaseSessionCreateParamsBrowserSettings(TypedDict, total=False): block_ads: Annotated[bool, PropertyInfo(alias="blockAds")] + captcha_image_selector: Annotated[str, PropertyInfo(alias="captchaImageSelector")] + + captcha_input_selector: Annotated[str, PropertyInfo(alias="captchaInputSelector")] + context: BrowserbaseSessionCreateParamsBrowserSettingsContext extension_id: Annotated[str, PropertyInfo(alias="extensionId")] @@ -201,10 +205,14 @@ class BrowserbaseSessionCreateParamsBrowserSettings(TypedDict, total=False): log_session: Annotated[bool, PropertyInfo(alias="logSession")] + os: Literal["windows", "mac", "linux", "mobile", "tablet"] + record_session: Annotated[bool, PropertyInfo(alias="recordSession")] solve_captchas: Annotated[bool, PropertyInfo(alias="solveCaptchas")] + verified: bool + viewport: BrowserbaseSessionCreateParamsBrowserSettingsViewport diff --git a/tests/api_resources/test_sessions.py b/tests/api_resources/test_sessions.py index f6058ceb..8fbfcead 100644 --- a/tests/api_resources/test_sessions.py +++ b/tests/api_resources/test_sessions.py @@ -891,6 +891,8 @@ def test_method_start_with_all_params(self, client: Stagehand) -> None: "browser_settings": { "advanced_stealth": True, "block_ads": True, + "captcha_image_selector": "captchaImageSelector", + "captcha_input_selector": "captchaInputSelector", "context": { "id": "id", "persist": True, @@ -910,8 +912,10 @@ def test_method_start_with_all_params(self, client: Stagehand) -> None: }, }, "log_session": True, + "os": "windows", "record_session": True, "solve_captchas": True, + "verified": True, "viewport": { "height": 0, "width": 0, @@ -1840,6 +1844,8 @@ async def test_method_start_with_all_params(self, async_client: AsyncStagehand) "browser_settings": { "advanced_stealth": True, "block_ads": True, + "captcha_image_selector": "captchaImageSelector", + "captcha_input_selector": "captchaInputSelector", "context": { "id": "id", "persist": True, @@ -1859,8 +1865,10 @@ async def test_method_start_with_all_params(self, async_client: AsyncStagehand) }, }, "log_session": True, + "os": "windows", "record_session": True, "solve_captchas": True, + "verified": True, "viewport": { "height": 0, "width": 0, From 3c0408675154c9f7d241c4e92e9cb82f0419d6b3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:32:13 +0000 Subject: [PATCH 4/6] feat: Revert "[STG-1573] Add providerOptions for extensible model auth (#1822)" --- .stats.yml | 4 +- src/stagehand/resources/sessions.py | 10 - src/stagehand/types/model_config_param.py | 101 +-------- src/stagehand/types/session_execute_params.py | 6 + src/stagehand/types/session_start_params.py | 193 ------------------ src/stagehand/types/stream_event.py | 2 +- tests/api_resources/test_sessions.py | 142 +++++-------- 7 files changed, 61 insertions(+), 397 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6bde59e1..d8428d42 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-a8db51c6460b3daff67b35262517848a0d4e783c6805c2edd531b155a5db71dd.yml -openapi_spec_hash: c6e7127f211f946673d6389e1d8db1ba +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-1c6caa2891a7f3bdfc0caab143f285badc9145220c9b29cd5e4cf1a9b3ac11cf.yml +openapi_spec_hash: 28c4b734a5309067c39bb4c4b709b9ab config_hash: a962ae71493deb11a1c903256fb25386 diff --git a/src/stagehand/resources/sessions.py b/src/stagehand/resources/sessions.py index d24424c6..a3c3c4bd 100644 --- a/src/stagehand/resources/sessions.py +++ b/src/stagehand/resources/sessions.py @@ -927,7 +927,6 @@ def start( browserbase_session_id: str | Omit = omit, dom_settle_timeout_ms: float | Omit = omit, experimental: bool | Omit = omit, - model_client_options: session_start_params.ModelClientOptions | Omit = omit, self_heal: bool | Omit = omit, system_prompt: str | Omit = omit, verbose: Literal[0, 1, 2] | Omit = omit, @@ -958,9 +957,6 @@ def start( dom_settle_timeout_ms: Timeout in ms to wait for DOM to settle - model_client_options: Optional provider-specific configuration for the session model (for example - Bedrock region and credentials) - self_heal: Enable self-healing for failed actions system_prompt: Custom system prompt for AI operations @@ -1001,7 +997,6 @@ def start( "browserbase_session_id": browserbase_session_id, "dom_settle_timeout_ms": dom_settle_timeout_ms, "experimental": experimental, - "model_client_options": model_client_options, "self_heal": self_heal, "system_prompt": system_prompt, "verbose": verbose, @@ -1895,7 +1890,6 @@ async def start( browserbase_session_id: str | Omit = omit, dom_settle_timeout_ms: float | Omit = omit, experimental: bool | Omit = omit, - model_client_options: session_start_params.ModelClientOptions | Omit = omit, self_heal: bool | Omit = omit, system_prompt: str | Omit = omit, verbose: Literal[0, 1, 2] | Omit = omit, @@ -1926,9 +1920,6 @@ async def start( dom_settle_timeout_ms: Timeout in ms to wait for DOM to settle - model_client_options: Optional provider-specific configuration for the session model (for example - Bedrock region and credentials) - self_heal: Enable self-healing for failed actions system_prompt: Custom system prompt for AI operations @@ -1969,7 +1960,6 @@ async def start( "browserbase_session_id": browserbase_session_id, "dom_settle_timeout_ms": dom_settle_timeout_ms, "experimental": experimental, - "model_client_options": model_client_options, "self_heal": self_heal, "system_prompt": system_prompt, "verbose": verbose, diff --git a/src/stagehand/types/model_config_param.py b/src/stagehand/types/model_config_param.py index a725892c..3ed433f7 100644 --- a/src/stagehand/types/model_config_param.py +++ b/src/stagehand/types/model_config_param.py @@ -2,92 +2,12 @@ from __future__ import annotations -from typing import Dict, Union -from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict +from typing import Dict +from typing_extensions import Literal, Required, Annotated, TypedDict from .._utils import PropertyInfo -__all__ = [ - "ModelConfigParam", - "ProviderOptions", - "ProviderOptionsBedrockAPIKeyProviderOptions", - "ProviderOptionsBedrockAwsCredentialsProviderOptions", - "ProviderOptionsGoogleVertexProviderOptions", - "ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions", - "ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials", -] - - -class ProviderOptionsBedrockAPIKeyProviderOptions(TypedDict, total=False): - region: Required[str] - """AWS region for Amazon Bedrock""" - - -class ProviderOptionsBedrockAwsCredentialsProviderOptions(TypedDict, total=False): - access_key_id: Required[Annotated[str, PropertyInfo(alias="accessKeyId")]] - """AWS access key ID for Bedrock""" - - region: Required[str] - """AWS region for Amazon Bedrock""" - - secret_access_key: Required[Annotated[str, PropertyInfo(alias="secretAccessKey")]] - """AWS secret access key for Bedrock""" - - session_token: Annotated[str, PropertyInfo(alias="sessionToken")] - """Optional AWS session token for temporary credentials""" - - -class ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials(TypedDict, total=False): - auth_provider_x509_cert_url: str - - auth_uri: str - - client_email: str - - client_id: str - - client_x509_cert_url: str - - private_key: str - - private_key_id: str - - project_id: str - - token_uri: str - - type: str - - universe_domain: str - - -class ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions(TypedDict, total=False): - """Optional Google auth options for Vertex AI""" - - credentials: ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials - - -class ProviderOptionsGoogleVertexProviderOptions(TypedDict, total=False): - google_auth_options: Annotated[ - ProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions, PropertyInfo(alias="googleAuthOptions") - ] - """Optional Google auth options for Vertex AI""" - - headers: Dict[str, str] - """Custom headers for Vertex AI requests""" - - location: str - """Google Cloud location for Vertex AI""" - - project: str - """Google Cloud project ID for Vertex AI""" - - -ProviderOptions: TypeAlias = Union[ - ProviderOptionsBedrockAPIKeyProviderOptions, - ProviderOptionsBedrockAwsCredentialsProviderOptions, - ProviderOptionsGoogleVertexProviderOptions, -] +__all__ = ["ModelConfigParam"] class ModelConfigParam(TypedDict, total=False): @@ -101,20 +21,7 @@ class ModelConfigParam(TypedDict, total=False): """Base URL for the model provider""" headers: Dict[str, str] - """Custom headers for the model provider""" + """Custom headers sent with every request to the model provider""" provider: Literal["openai", "anthropic", "google", "microsoft", "bedrock"] """AI provider for the model (or provide a baseURL endpoint instead)""" - - provider_options: Annotated[ProviderOptions, PropertyInfo(alias="providerOptions")] - """Provider-specific options passed through to the AI SDK provider constructor. - - For Bedrock: { region, accessKeyId, secretAccessKey, sessionToken }. For Vertex: - { project, location, googleAuthOptions }. - """ - - skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] - """When true, hosted sessions will not copy x-model-api-key into model.apiKey. - - Use this when auth is carried through providerOptions instead of an API key. - """ diff --git a/src/stagehand/types/session_execute_params.py b/src/stagehand/types/session_execute_params.py index 4b64c0b2..944c3856 100644 --- a/src/stagehand/types/session_execute_params.py +++ b/src/stagehand/types/session_execute_params.py @@ -76,6 +76,12 @@ class ExecuteOptions(TypedDict, total=False): max_steps: Annotated[float, PropertyInfo(alias="maxSteps")] """Maximum number of steps the agent can take""" + tool_timeout: Annotated[float, PropertyInfo(alias="toolTimeout")] + """Timeout in milliseconds for each agent tool call""" + + use_search: Annotated[bool, PropertyInfo(alias="useSearch")] + """Whether to enable the web search tool powered by Browserbase Search API""" + class SessionExecuteParamsNonStreaming(SessionExecuteParamsBase, total=False): stream_response: Annotated[Literal[False], PropertyInfo(alias="streamResponse")] diff --git a/src/stagehand/types/session_start_params.py b/src/stagehand/types/session_start_params.py index 7c66c693..5123c507 100644 --- a/src/stagehand/types/session_start_params.py +++ b/src/stagehand/types/session_start_params.py @@ -24,18 +24,6 @@ "BrowserbaseSessionCreateParamsProxiesProxyConfigListBrowserbaseProxyConfig", "BrowserbaseSessionCreateParamsProxiesProxyConfigListBrowserbaseProxyConfigGeolocation", "BrowserbaseSessionCreateParamsProxiesProxyConfigListExternalProxyConfig", - "ModelClientOptions", - "ModelClientOptionsBedrockAPIKeyModelClientOptions", - "ModelClientOptionsBedrockAPIKeyModelClientOptionsProviderOptions", - "ModelClientOptionsBedrockAwsCredentialsModelClientOptions", - "ModelClientOptionsBedrockAwsCredentialsModelClientOptionsProviderOptions", - "ModelClientOptionsGenericModelClientOptions", - "ModelClientOptionsGenericModelClientOptionsProviderOptions", - "ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAPIKeyProviderOptions", - "ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAwsCredentialsProviderOptions", - "ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptions", - "ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions", - "ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials", ] @@ -60,12 +48,6 @@ class SessionStartParams(TypedDict, total=False): experimental: bool - model_client_options: Annotated[ModelClientOptions, PropertyInfo(alias="modelClientOptions")] - """ - Optional provider-specific configuration for the session model (for example - Bedrock region and credentials) - """ - self_heal: Annotated[bool, PropertyInfo(alias="selfHeal")] """Enable self-healing for failed actions""" @@ -266,178 +248,3 @@ class BrowserbaseSessionCreateParams(TypedDict, total=False): timeout: float user_metadata: Annotated[Dict[str, object], PropertyInfo(alias="userMetadata")] - - -class ModelClientOptionsBedrockAPIKeyModelClientOptionsProviderOptions(TypedDict, total=False): - region: Required[str] - """AWS region for Amazon Bedrock""" - - -class ModelClientOptionsBedrockAPIKeyModelClientOptions(TypedDict, total=False): - api_key: Required[Annotated[str, PropertyInfo(alias="apiKey")]] - """Short-term Bedrock API key for bearer-token auth""" - - provider_options: Required[ - Annotated[ - ModelClientOptionsBedrockAPIKeyModelClientOptionsProviderOptions, PropertyInfo(alias="providerOptions") - ] - ] - - base_url: Annotated[str, PropertyInfo(alias="baseURL")] - """Base URL for the model provider""" - - headers: Dict[str, str] - """Custom headers for the model provider""" - - skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] - """When true, hosted sessions will not copy x-model-api-key into model.apiKey. - - Use this when auth is carried through providerOptions instead of an API key. - """ - - -class ModelClientOptionsBedrockAwsCredentialsModelClientOptionsProviderOptions(TypedDict, total=False): - access_key_id: Required[Annotated[str, PropertyInfo(alias="accessKeyId")]] - """AWS access key ID for Bedrock""" - - region: Required[str] - """AWS region for Amazon Bedrock""" - - secret_access_key: Required[Annotated[str, PropertyInfo(alias="secretAccessKey")]] - """AWS secret access key for Bedrock""" - - session_token: Annotated[str, PropertyInfo(alias="sessionToken")] - """Optional AWS session token for temporary credentials""" - - -class ModelClientOptionsBedrockAwsCredentialsModelClientOptions(TypedDict, total=False): - provider_options: Required[ - Annotated[ - ModelClientOptionsBedrockAwsCredentialsModelClientOptionsProviderOptions, - PropertyInfo(alias="providerOptions"), - ] - ] - - base_url: Annotated[str, PropertyInfo(alias="baseURL")] - """Base URL for the model provider""" - - headers: Dict[str, str] - """Custom headers for the model provider""" - - skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] - """When true, hosted sessions will not copy x-model-api-key into model.apiKey. - - Use this when auth is carried through providerOptions instead of an API key. - """ - - -class ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAPIKeyProviderOptions(TypedDict, total=False): - region: Required[str] - """AWS region for Amazon Bedrock""" - - -class ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAwsCredentialsProviderOptions( - TypedDict, total=False -): - access_key_id: Required[Annotated[str, PropertyInfo(alias="accessKeyId")]] - """AWS access key ID for Bedrock""" - - region: Required[str] - """AWS region for Amazon Bedrock""" - - secret_access_key: Required[Annotated[str, PropertyInfo(alias="secretAccessKey")]] - """AWS secret access key for Bedrock""" - - session_token: Annotated[str, PropertyInfo(alias="sessionToken")] - """Optional AWS session token for temporary credentials""" - - -class ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials( - TypedDict, total=False -): - auth_provider_x509_cert_url: str - - auth_uri: str - - client_email: str - - client_id: str - - client_x509_cert_url: str - - private_key: str - - private_key_id: str - - project_id: str - - token_uri: str - - type: str - - universe_domain: str - - -class ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions( - TypedDict, total=False -): - """Optional Google auth options for Vertex AI""" - - credentials: ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptionsCredentials - - -class ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptions(TypedDict, total=False): - google_auth_options: Annotated[ - ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptionsGoogleAuthOptions, - PropertyInfo(alias="googleAuthOptions"), - ] - """Optional Google auth options for Vertex AI""" - - headers: Dict[str, str] - """Custom headers for Vertex AI requests""" - - location: str - """Google Cloud location for Vertex AI""" - - project: str - """Google Cloud project ID for Vertex AI""" - - -ModelClientOptionsGenericModelClientOptionsProviderOptions: TypeAlias = Union[ - ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAPIKeyProviderOptions, - ModelClientOptionsGenericModelClientOptionsProviderOptionsBedrockAwsCredentialsProviderOptions, - ModelClientOptionsGenericModelClientOptionsProviderOptionsGoogleVertexProviderOptions, -] - - -class ModelClientOptionsGenericModelClientOptions(TypedDict, total=False): - api_key: Annotated[str, PropertyInfo(alias="apiKey")] - """API key for the model provider""" - - base_url: Annotated[str, PropertyInfo(alias="baseURL")] - """Base URL for the model provider""" - - headers: Dict[str, str] - """Custom headers for the model provider""" - - provider_options: Annotated[ - ModelClientOptionsGenericModelClientOptionsProviderOptions, PropertyInfo(alias="providerOptions") - ] - """Provider-specific options passed through to the AI SDK provider constructor. - - For Bedrock: { region, accessKeyId, secretAccessKey, sessionToken }. For Vertex: - { project, location, googleAuthOptions }. - """ - - skip_api_key_fallback: Annotated[bool, PropertyInfo(alias="skipApiKeyFallback")] - """When true, hosted sessions will not copy x-model-api-key into model.apiKey. - - Use this when auth is carried through providerOptions instead of an API key. - """ - - -ModelClientOptions: TypeAlias = Union[ - ModelClientOptionsBedrockAPIKeyModelClientOptions, - ModelClientOptionsBedrockAwsCredentialsModelClientOptions, - ModelClientOptionsGenericModelClientOptions, -] diff --git a/src/stagehand/types/stream_event.py b/src/stagehand/types/stream_event.py index 3d5b21ee..6fd47585 100644 --- a/src/stagehand/types/stream_event.py +++ b/src/stagehand/types/stream_event.py @@ -32,7 +32,7 @@ class DataStreamEventLogDataOutput(BaseModel): class StreamEvent(BaseModel): """Server-Sent Event emitted during streaming responses. - Events are sent as `data: \n\n`. Key order: data (with status first), type, id. + Events are sent as `event: \ndata: \n\n`, where the JSON payload has the shape `{ data, type, id }`. """ id: str diff --git a/tests/api_resources/test_sessions.py b/tests/api_resources/test_sessions.py index 8fbfcead..83a69942 100644 --- a/tests/api_resources/test_sessions.py +++ b/tests/api_resources/test_sessions.py @@ -44,13 +44,11 @@ def test_method_act_with_all_params_overload_1(self, client: Stagehand) -> None: frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -123,13 +121,11 @@ def test_method_act_with_all_params_overload_2(self, client: Stagehand) -> None: frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -254,23 +250,19 @@ def test_method_execute_with_all_params_overload_1(self, client: Stagehand) -> N agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -279,6 +271,8 @@ def test_method_execute_with_all_params_overload_1(self, client: Stagehand) -> N "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, + "tool_timeout": 30000, + "use_search": True, }, frame_id="frameId", should_cache=True, @@ -354,23 +348,19 @@ def test_method_execute_with_all_params_overload_2(self, client: Stagehand) -> N agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -379,6 +369,8 @@ def test_method_execute_with_all_params_overload_2(self, client: Stagehand) -> N "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, + "tool_timeout": 30000, + "use_search": True, }, stream_response=True, frame_id="frameId", @@ -452,13 +444,11 @@ def test_method_extract_with_all_params_overload_1(self, client: Stagehand) -> N instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -522,13 +512,11 @@ def test_method_extract_with_all_params_overload_2(self, client: Stagehand) -> N instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -654,13 +642,11 @@ def test_method_observe_with_all_params_overload_1(self, client: Stagehand) -> N instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -730,13 +716,11 @@ def test_method_observe_with_all_params_overload_2(self, client: Stagehand) -> N instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -932,13 +916,6 @@ def test_method_start_with_all_params(self, client: Stagehand) -> None: browserbase_session_id="browserbaseSessionID", dom_settle_timeout_ms=5000, experimental=True, - model_client_options={ - "api_key": "bedrock-short-term-api-key", - "provider_options": {"region": "us-east-1"}, - "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, - "skip_api_key_fallback": True, - }, self_heal=True, system_prompt="systemPrompt", verbose=1, @@ -997,13 +974,11 @@ async def test_method_act_with_all_params_overload_1(self, async_client: AsyncSt frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -1076,13 +1051,11 @@ async def test_method_act_with_all_params_overload_2(self, async_client: AsyncSt frame_id="frameId", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "timeout": 30000, "variables": { @@ -1207,23 +1180,19 @@ async def test_method_execute_with_all_params_overload_1(self, async_client: Asy agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -1232,6 +1201,8 @@ async def test_method_execute_with_all_params_overload_1(self, async_client: Asy "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, + "tool_timeout": 30000, + "use_search": True, }, frame_id="frameId", should_cache=True, @@ -1307,23 +1278,19 @@ async def test_method_execute_with_all_params_overload_2(self, async_client: Asy agent_config={ "cua": True, "execution_model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "mode": "cua", "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "provider": "openai", "system_prompt": "systemPrompt", @@ -1332,6 +1299,8 @@ async def test_method_execute_with_all_params_overload_2(self, async_client: Asy "instruction": "Log in with username 'demo' and password 'test123', then navigate to settings", "highlight_cursor": True, "max_steps": 20, + "tool_timeout": 30000, + "use_search": True, }, stream_response=True, frame_id="frameId", @@ -1405,13 +1374,11 @@ async def test_method_extract_with_all_params_overload_1(self, async_client: Asy instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -1475,13 +1442,11 @@ async def test_method_extract_with_all_params_overload_2(self, async_client: Asy instruction="Extract all product names and prices from the page", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "#main-content", "timeout": 30000, @@ -1607,13 +1572,11 @@ async def test_method_observe_with_all_params_overload_1(self, async_client: Asy instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -1683,13 +1646,11 @@ async def test_method_observe_with_all_params_overload_2(self, async_client: Asy instruction="Find all clickable navigation links", options={ "model": { - "model_name": "openai/gpt-5-nano", + "model_name": "openai/gpt-5.4-mini", "api_key": "sk-some-openai-api-key", "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, + "headers": {"foo": "string"}, "provider": "openai", - "provider_options": {"region": "us-east-1"}, - "skip_api_key_fallback": True, }, "selector": "nav", "timeout": 30000, @@ -1885,13 +1846,6 @@ async def test_method_start_with_all_params(self, async_client: AsyncStagehand) browserbase_session_id="browserbaseSessionID", dom_settle_timeout_ms=5000, experimental=True, - model_client_options={ - "api_key": "bedrock-short-term-api-key", - "provider_options": {"region": "us-east-1"}, - "base_url": "https://api.openai.com/v1", - "headers": {"X-Custom-Header": "value"}, - "skip_api_key_fallback": True, - }, self_heal=True, system_prompt="systemPrompt", verbose=1, From b8706575ab0f95b9e6781ee3685f9b79e0fe6036 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 07:53:15 +0000 Subject: [PATCH 5/6] fix: ensure file data are only sent as 1 parameter --- src/stagehand/_utils/_utils.py | 5 +++-- tests/test_extract_files.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/stagehand/_utils/_utils.py b/src/stagehand/_utils/_utils.py index 1c50ff6a..b49804f3 100644 --- a/src/stagehand/_utils/_utils.py +++ b/src/stagehand/_utils/_utils.py @@ -86,8 +86,9 @@ def _extract_items( index += 1 if is_dict(obj): try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]): item = obj.pop(key) else: item = obj[key] diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index 0d751b46..873eaf74 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -35,6 +35,15 @@ def test_multiple_files() -> None: assert query == {"documents": [{}, {}]} +def test_top_level_file_array() -> None: + query = {"files": [b"file one", b"file two"], "title": "hello"} + assert extract_files(query, paths=[["files", ""]]) == [ + ("files[]", b"file one"), + ("files[]", b"file two"), + ] + assert query == {"title": "hello"} + + @pytest.mark.parametrize( "query,paths,expected", [ From 3fc3bb1ddb986e9cce1f2c588398e15d9ccd1f61 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 07:53:32 +0000 Subject: [PATCH 6/6] release: 3.20.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/stagehand/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2ebc4e90..f80372ae 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.19.5" + ".": "3.20.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index bbaf6ba1..510bf324 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 3.20.0 (2026-04-11) + +Full Changelog: [v3.19.5...v3.20.0](https://github.com/browserbase/stagehand-python/compare/v3.19.5...v3.20.0) + +### Features + +* [STG-1798] feat: support Browserbase verified sessions ([078ab5c](https://github.com/browserbase/stagehand-python/commit/078ab5c76f13beee16e44d7eed5e3018f1cc5bd8)) +* Bedrock auth passthrough ([9463fa4](https://github.com/browserbase/stagehand-python/commit/9463fa49cb839abbb2c6a1adb0d053e5006216a7)) +* Revert "[STG-1573] Add providerOptions for extensible model auth ([#1822](https://github.com/browserbase/stagehand-python/issues/1822))" ([3c04086](https://github.com/browserbase/stagehand-python/commit/3c0408675154c9f7d241c4e92e9cb82f0419d6b3)) + + +### Bug Fixes + +* **client:** preserve hardcoded query params when merging with user params ([feac39d](https://github.com/browserbase/stagehand-python/commit/feac39d88a841ff710d672b622f3280037589f66)) +* ensure file data are only sent as 1 parameter ([b870657](https://github.com/browserbase/stagehand-python/commit/b8706575ab0f95b9e6781ee3685f9b79e0fe6036)) + ## 3.19.5 (2026-04-03) Full Changelog: [v3.19.4...v3.19.5](https://github.com/browserbase/stagehand-python/compare/v3.19.4...v3.19.5) diff --git a/pyproject.toml b/pyproject.toml index 96fa8963..d3411cde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "stagehand" -version = "3.19.5" +version = "3.20.0" description = "The official Python library for the stagehand API" dynamic = ["readme"] license = "MIT" diff --git a/src/stagehand/_version.py b/src/stagehand/_version.py index 15e655a3..b5e0b8a6 100644 --- a/src/stagehand/_version.py +++ b/src/stagehand/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "stagehand" -__version__ = "3.19.5" # x-release-please-version +__version__ = "3.20.0" # x-release-please-version