Skip to content

Commit 7f7a3aa

Browse files
release: 3.7.0 (#312)
* feat: Add bedrock to provider enum in Zod schemas and OpenAPI spec * STG-1293: add local server multiregion example * STG-1293: document multiregion example * chore(ci): bump uv version * chore(ci): skip uploading artifacts on stainless-internal branches * feat: Add missing cdpHeaders field to v3 server openapi spec * codegen metadata * STG-1537: switch local binary downloads to server-v3 * fix(pydantic): do not pass `by_alias` unless set * fix(deps): bump minimum typing-extensions version * chore(internal): tweak CI branches * feat: [STG-1607] Yield finished SSE event instead of silently dropping it * feat: Revert broken finished SSE yield config * feat: [fix]: add `useSearch` & `toolTimeout` to stainless types * fix: sanitize endpoint path params * feat: variables for observe * release: 3.7.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: monadoid <sam.finton@gmail.com> Co-authored-by: Sam F <43347795+monadoid@users.noreply.github.com>
1 parent a30258c commit 7f7a3aa

24 files changed

+618
-75
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
name: CI
22
on:
33
push:
4-
branches-ignore:
5-
- 'generated'
6-
- 'codegen/**'
7-
- 'integrated/**'
8-
- 'stl-preview-head/**'
9-
- 'stl-preview-base/**'
4+
branches:
5+
- '**'
6+
- '!integrated/**'
7+
- '!stl-preview-head/**'
8+
- '!stl-preview-base/**'
9+
- '!generated'
10+
- '!codegen/**'
11+
- 'codegen/stl/**'
1012
pull_request:
1113
branches-ignore:
1214
- 'stl-preview-head/**'
@@ -24,7 +26,7 @@ jobs:
2426
- name: Install uv
2527
uses: astral-sh/setup-uv@v5
2628
with:
27-
version: '0.9.13'
29+
version: '0.10.2'
2830

2931
- name: Install dependencies
3032
run: uv sync --all-extras
@@ -46,7 +48,7 @@ jobs:
4648
- name: Install uv
4749
uses: astral-sh/setup-uv@v5
4850
with:
49-
version: '0.9.13'
51+
version: '0.10.2'
5052

5153
- name: Install dependencies
5254
run: uv sync --all-extras
@@ -55,14 +57,18 @@ jobs:
5557
run: uv build
5658

5759
- name: Get GitHub OIDC Token
58-
if: github.repository == 'stainless-sdks/stagehand-python'
60+
if: |-
61+
github.repository == 'stainless-sdks/stagehand-python' &&
62+
!startsWith(github.ref, 'refs/heads/stl/')
5963
id: github-oidc
6064
uses: actions/github-script@v8
6165
with:
6266
script: core.setOutput('github_token', await core.getIDToken());
6367

6468
- name: Upload tarball
65-
if: github.repository == 'stainless-sdks/stagehand-python'
69+
if: |-
70+
github.repository == 'stainless-sdks/stagehand-python' &&
71+
!startsWith(github.ref, 'refs/heads/stl/')
6672
env:
6773
URL: https://pkg.stainless.com/s
6874
AUTH: ${{ steps.github-oidc.outputs.github_token }}
@@ -80,7 +86,7 @@ jobs:
8086
- name: Install uv
8187
uses: astral-sh/setup-uv@v5
8288
with:
83-
version: '0.9.13'
89+
version: '0.10.2'
8490

8591
- name: Bootstrap
8692
run: ./scripts/bootstrap

.github/workflows/publish-pypi.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ jobs:
1717
matrix:
1818
include:
1919
- os: ubuntu-latest
20-
binary_name: stagehand-server-linux-x64
20+
binary_name: stagehand-server-v3-linux-x64
2121
output_path: src/stagehand/_sea/stagehand-linux-x64
2222
wheel_platform_tag: manylinux2014_x86_64
2323
- os: macos-latest
24-
binary_name: stagehand-server-darwin-arm64
24+
binary_name: stagehand-server-v3-darwin-arm64
2525
output_path: src/stagehand/_sea/stagehand-darwin-arm64
2626
wheel_platform_tag: macosx_11_0_arm64
2727
- os: macos-15-intel
28-
binary_name: stagehand-server-darwin-x64
28+
binary_name: stagehand-server-v3-darwin-x64
2929
output_path: src/stagehand/_sea/stagehand-darwin-x64
3030
wheel_platform_tag: macosx_10_9_x86_64
3131
- os: windows-latest
32-
binary_name: stagehand-server-win32-x64.exe
32+
binary_name: stagehand-server-v3-win32-x64.exe
3333
output_path: src/stagehand/_sea/stagehand-win32-x64.exe
3434
wheel_platform_tag: win_amd64
3535

@@ -45,7 +45,7 @@ jobs:
4545
with:
4646
version: "0.9.13"
4747

48-
- name: Resolve latest stagehand/server release
48+
- name: Resolve latest stagehand/server-v3 release
4949
id: stagehand-server-release
5050
uses: actions/github-script@v6
5151
with:
@@ -56,12 +56,12 @@ jobs:
5656
repo: 'stagehand',
5757
per_page: 100,
5858
});
59-
const release = data.find(r => typeof r.tag_name === 'string' && r.tag_name.startsWith('stagehand-server/v'));
59+
const release = data.find(r => typeof r.tag_name === 'string' && r.tag_name.startsWith('stagehand-server-v3/v'));
6060
if (!release) {
61-
core.setFailed('No stagehand-server/v* release found in browserbase/stagehand');
61+
core.setFailed('No stagehand-server-v3/v* release found in browserbase/stagehand');
6262
return;
6363
}
64-
core.info(`Using stagehand/server release tag: ${release.tag_name}`);
64+
core.info(`Using stagehand/server-v3 release tag: ${release.tag_name}`);
6565
core.setOutput('tag', release.tag_name);
6666
core.setOutput('id', String(release.id));
6767

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "3.6.0"
2+
".": "3.7.0"
33
}

.stats.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 8
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-87b4d9e349de9d33d5d89439f7ac9507133700a9f072fdf0d756471961768d2c.yml
3-
openapi_spec_hash: 0f6ae6d10a0227a3482914728cf901ba
4-
config_hash: 7baf2daccae5913216bb74c52d63eaff
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-975ca868b31b1e45fb00b31a53d9df1ceec8663f6c2851bf40fdaa84171afadc.yml
3+
openapi_spec_hash: 37891379e0f47e5c69769fbaa1064dab
4+
config_hash: 0209737a4ab2a71afececb0ff7459998

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# Changelog
22

3+
## 3.7.0 (2026-03-23)
4+
5+
Full Changelog: [v3.6.0...v3.7.0](https://github.com/browserbase/stagehand-python/compare/v3.6.0...v3.7.0)
6+
7+
### Features
8+
9+
* [fix]: add `useSearch` & `toolTimeout` to stainless types ([0faf1df](https://github.com/browserbase/stagehand-python/commit/0faf1dfb3d6b98e727bf0c904e272da3857863e8))
10+
* [STG-1607] Yield finished SSE event instead of silently dropping it ([0bba1e9](https://github.com/browserbase/stagehand-python/commit/0bba1e9c657cffbbe306a2bc6520ab2992295773))
11+
* Add bedrock to provider enum in Zod schemas and OpenAPI spec ([5ace8c9](https://github.com/browserbase/stagehand-python/commit/5ace8c9ee306ff8cc09c258500c24b6b55e9562b))
12+
* Add missing cdpHeaders field to v3 server openapi spec ([7c17bc2](https://github.com/browserbase/stagehand-python/commit/7c17bc237e604ae38ca6772deb02a378d5117f81))
13+
* Revert broken finished SSE yield config ([21189cf](https://github.com/browserbase/stagehand-python/commit/21189cf82aab6b07caf9a322f28355279b2a3f45))
14+
* variables for observe ([93ef310](https://github.com/browserbase/stagehand-python/commit/93ef31098a0cb2b4acfd157ae4c45cedc2f2e58c))
15+
16+
17+
### Bug Fixes
18+
19+
* **deps:** bump minimum typing-extensions version ([c8cce20](https://github.com/browserbase/stagehand-python/commit/c8cce20e513c91cff638742df3f19c477aeab795))
20+
* **pydantic:** do not pass `by_alias` unless set ([b045b1a](https://github.com/browserbase/stagehand-python/commit/b045b1ab4d93aed7de6388fe31e768470d080fd4))
21+
* sanitize endpoint path params ([5201ec1](https://github.com/browserbase/stagehand-python/commit/5201ec149265832d89100c9c5292985e2e65c0f8))
22+
23+
24+
### Chores
25+
26+
* **ci:** bump uv version ([84a841c](https://github.com/browserbase/stagehand-python/commit/84a841cd29648432d713f10b37a530e18942a3f0))
27+
* **ci:** skip uploading artifacts on stainless-internal branches ([291b296](https://github.com/browserbase/stagehand-python/commit/291b296a33c5be942422946051925ad6c500679b))
28+
* **internal:** tweak CI branches ([1d33bbf](https://github.com/browserbase/stagehand-python/commit/1d33bbf69c610c4627e31345d5ae65828dfcc483))
29+
330
## 3.6.0 (2026-02-25)
431

532
Full Changelog: [v3.5.0...v3.6.0](https://github.com/browserbase/stagehand-python/compare/v3.5.0...v3.6.0)

CONTRIBUTING.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,19 @@ $ uv run python scripts/download-binary.py
4040

4141
This will:
4242
- Detect your platform (macOS, Linux, Windows) and architecture (x64, arm64)
43-
- Download the latest stagehand-server binary from GitHub releases
43+
- Download the latest stagehand-server-v3 binary from GitHub releases
4444
- Place it in `bin/sea/` where the SDK expects to find it
4545

4646
### Manual download (alternative)
4747

4848
You can also manually download from [GitHub releases](https://github.com/browserbase/stagehand/releases):
4949

50-
1. Find the latest `stagehand/server vX.X.X` release
50+
1. Find the latest `stagehand/server-v3 vX.X.X` release
5151
2. Download the binary for your platform:
52-
- macOS ARM: `stagehand-server-darwin-arm64`
53-
- macOS Intel: `stagehand-server-darwin-x64`
54-
- Linux: `stagehand-server-linux-x64` or `stagehand-server-linux-arm64`
55-
- Windows: `stagehand-server-win32-x64.exe` or `stagehand-server-win32-arm64.exe`
52+
- macOS ARM: `stagehand-server-v3-darwin-arm64`
53+
- macOS Intel: `stagehand-server-v3-darwin-x64`
54+
- Linux: `stagehand-server-v3-linux-x64` or `stagehand-server-v3-linux-arm64`
55+
- Windows: `stagehand-server-v3-win32-x64.exe` or `stagehand-server-v3-win32-arm64.exe`
5656
3. Rename it to match the expected format (remove `-server` from the name):
5757
- `stagehand-darwin-arm64`, `stagehand-linux-x64`, `stagehand-win32-x64.exe`, etc.
5858
4. Place it in `bin/sea/` directory

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ Examples and dependencies:
112112
- `examples/byob_example.py`: Playwright + Playwright browsers
113113
- `examples/pydoll_tab_example.py`: `pydoll-python` (Python 3.10+)
114114

115+
Multiregion support: see `examples/local_server_multiregion_browser_example.py`.
116+
115117
Run any example:
116118

117119
```bash

RELEASE_WORKFLOWS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ This repo publishes the `stagehand` Python package to PyPI when a **GitHub Relea
1111
- Publishes the GitHub Release + git tag.
1212
4. Wait for GitHub Actions to publish to PyPI (automatic).
1313
- Trigger: GitHub Release `published` event runs `.github/workflows/publish-pypi.yml`.
14-
- Builds platform wheels that embed the Stagehand server binary (downloaded from the latest `stagehand-server/v*` GitHub Release in `browserbase/stagehand`), then publishes to PyPI.
14+
- Builds platform wheels that embed the Stagehand server binary (downloaded from the latest `stagehand-server-v3/v*` GitHub Release in `browserbase/stagehand`), then publishes to PyPI.
1515

1616
## Important implementation notes
1717

1818
- **Server binary bundling into wheels**
19-
- `.github/workflows/publish-pypi.yml` downloads the prebuilt Stagehand server SEA binary from the latest `stagehand-server/v*` GitHub Release in `browserbase/stagehand`, then places it into `src/stagehand/_sea/*` before running `uv build --wheel`.
19+
- `.github/workflows/publish-pypi.yml` downloads the prebuilt Stagehand server SEA binary from the latest `stagehand-server-v3/v*` GitHub Release in `browserbase/stagehand`, then places it into `src/stagehand/_sea/*` before running `uv build --wheel`.
2020
- **Stagehand server version selection (current behavior)**
21-
- `publish-pypi.yml` resolves the latest GitHub Release tag matching `stagehand-server/v*` from `browserbase/stagehand` and downloads the matching `stagehand-server-<platform>` release asset for each wheel build.
21+
- `publish-pypi.yml` resolves the latest GitHub Release tag matching `stagehand-server-v3/v*` from `browserbase/stagehand` and downloads the matching `stagehand-server-v3-<platform>` release asset for each wheel build.
2222
- **Secrets**
2323
- PyPI publish uses `secrets.STAGEHAND_PYPI_TOKEN || secrets.PYPI_TOKEN`.
2424
- `.github/workflows/release-doctor.yml` runs `bin/check-release-environment` on qualifying PRs and fails if `PYPI_TOKEN` is missing.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
"""
2+
Example: local Stagehand server + multiregion Browserbase browser in eu-central-1.
3+
4+
What this demonstrates:
5+
- Run Stagehand server locally (SEA) with Browserbase cloud browser
6+
- Request a Browserbase session in eu-central-1
7+
- Attach Playwright to the same browser via CDP (`cdp_url`)
8+
- Stream SSE events by default for observe/act/extract/execute
9+
- Run the full flow: start → observe → act → extract → agent/execute → end
10+
11+
Environment variables required:
12+
- MODEL_API_KEY
13+
- BROWSERBASE_API_KEY
14+
- BROWSERBASE_PROJECT_ID
15+
16+
Optional:
17+
- STAGEHAND_BASE_URL (defaults to http://127.0.0.1:3000 when server="local")
18+
"""
19+
20+
from __future__ import annotations
21+
22+
import os
23+
import sys
24+
from typing import Any, Optional
25+
26+
from env import load_example_env
27+
28+
from stagehand import Stagehand
29+
30+
31+
def _print_stream_events(stream: Any, label: str) -> object | None:
32+
result_payload: object | None = None
33+
for event in stream:
34+
if event.type == "log":
35+
print(f"[{label}][log] {event.data.message}")
36+
continue
37+
38+
status = event.data.status
39+
print(f"[{label}][system] status={status}")
40+
if status == "finished":
41+
result_payload = event.data.result
42+
elif status == "error":
43+
error_message = event.data.error or "unknown error"
44+
raise RuntimeError(f"{label} stream reported error: {error_message}")
45+
46+
return result_payload
47+
48+
49+
def main() -> None:
50+
load_example_env()
51+
model_api_key = os.environ.get("MODEL_API_KEY")
52+
if not model_api_key:
53+
sys.exit("Set the MODEL_API_KEY environment variable to run this example.")
54+
55+
bb_api_key = os.environ.get("BROWSERBASE_API_KEY")
56+
bb_project_id = os.environ.get("BROWSERBASE_PROJECT_ID")
57+
if not bb_api_key or not bb_project_id:
58+
sys.exit(
59+
"Set BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID to run this example."
60+
)
61+
62+
try:
63+
from playwright.sync_api import sync_playwright # type: ignore[import-not-found]
64+
except Exception:
65+
sys.exit(
66+
"Playwright is not installed. Install it with:\n"
67+
" uv pip install playwright\n"
68+
"and ensure browsers are installed (e.g. `playwright install chromium`)."
69+
)
70+
71+
session_id: Optional[str] = None
72+
73+
with Stagehand(
74+
server="local",
75+
browserbase_api_key=bb_api_key,
76+
browserbase_project_id=bb_project_id,
77+
model_api_key=model_api_key,
78+
local_openai_api_key=model_api_key,
79+
local_ready_timeout_s=30.0,
80+
) as client:
81+
print("⏳ Starting Stagehand session (local server + Browserbase browser)...")
82+
session = client.sessions.start(
83+
model_name="anthropic/claude-sonnet-4-6",
84+
browser={"type": "browserbase"},
85+
browserbase_session_create_params={"region": "eu-central-1"},
86+
verbose=2,
87+
)
88+
session_id = session.id
89+
90+
cdp_url = session.data.cdp_url
91+
if not cdp_url:
92+
sys.exit(
93+
"No cdp_url returned from the API for this session; cannot attach Playwright."
94+
)
95+
96+
print(f"✅ Session started: {session_id}")
97+
print("🔌 Connecting Playwright to the same browser over CDP...")
98+
99+
try:
100+
with sync_playwright() as p:
101+
browser = p.chromium.connect_over_cdp(cdp_url)
102+
try:
103+
context = browser.contexts[0] if browser.contexts else browser.new_context()
104+
page = context.pages[0] if context.pages else context.new_page()
105+
106+
page.goto("https://example.com", wait_until="domcontentloaded")
107+
108+
print("👀 Stagehand.observe(page=...) with SSE streaming...")
109+
observe_stream = session.observe(
110+
instruction="Find the most relevant click target on this page",
111+
page=page,
112+
stream_response=True,
113+
x_stream_response="true",
114+
)
115+
observe_result = _print_stream_events(observe_stream, "observe")
116+
117+
actions = observe_result or []
118+
if not actions:
119+
print("No actions found; ending session.")
120+
return
121+
122+
print("🖱️ Stagehand.act(page=...) with SSE streaming...")
123+
act_stream = session.act(
124+
input=actions[0],
125+
page=page,
126+
stream_response=True,
127+
x_stream_response="true",
128+
)
129+
_ = _print_stream_events(act_stream, "act")
130+
131+
print("🧠 Stagehand.extract(page=...) with SSE streaming...")
132+
extract_stream = session.extract(
133+
instruction="Extract the page title and the primary heading (h1) text",
134+
schema={
135+
"type": "object",
136+
"properties": {
137+
"title": {"type": "string"},
138+
"h1": {"type": "string"},
139+
},
140+
"required": ["title", "h1"],
141+
"additionalProperties": False,
142+
},
143+
page=page,
144+
stream_response=True,
145+
x_stream_response="true",
146+
)
147+
extracted = _print_stream_events(extract_stream, "extract")
148+
print("Extracted:", extracted)
149+
150+
print("🤖 Stagehand.execute(page=...) with SSE streaming...")
151+
execute_stream = session.execute(
152+
agent_config={"model": "anthropic/claude-opus-4-6"},
153+
execute_options={
154+
"instruction": (
155+
"Open the 'Learn more' link if present and summarize the destination in one sentence."
156+
),
157+
"max_steps": 5,
158+
},
159+
page=page,
160+
stream_response=True,
161+
x_stream_response="true",
162+
)
163+
execute_result = _print_stream_events(execute_stream, "execute")
164+
print("Execute result:", execute_result)
165+
finally:
166+
browser.close()
167+
finally:
168+
session.end()
169+
print("✅ Session ended.")
170+
171+
172+
if __name__ == "__main__":
173+
main()

0 commit comments

Comments
 (0)