diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac2db40..694af83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: '0.9.13' + version: '0.10.2' - name: Install dependencies run: uv sync --all-extras @@ -46,7 +46,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: '0.9.13' + version: '0.10.2' - name: Install dependencies run: uv sync --all-extras @@ -80,7 +80,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: '0.9.13' + version: '0.10.2' - name: Bootstrap run: ./scripts/bootstrap diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dc70380..2a266f8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.6.0" + ".": "3.7.0" } diff --git a/.stats.yml b/.stats.yml index ea8da3c..c61740e 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-87b4d9e349de9d33d5d89439f7ac9507133700a9f072fdf0d756471961768d2c.yml -openapi_spec_hash: 0f6ae6d10a0227a3482914728cf901ba -config_hash: 7baf2daccae5913216bb74c52d63eaff +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-a4e672f457dd99336f4b2a113fd7c7c6c9db0941b38d57cff6e3641549a6c4ed.yml +openapi_spec_hash: eae9c8561e420db8e4d238c1e59617fb +config_hash: 2a565ad6662259a2e90fa5f1f5095525 diff --git a/CHANGELOG.md b/CHANGELOG.md index e7755e9..477888b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 3.7.0 (2026-02-27) + +Full Changelog: [v3.6.0...v3.7.0](https://github.com/browserbase/stagehand-python/compare/v3.6.0...v3.7.0) + +### Features + +* Add bedrock to provider enum in Zod schemas and OpenAPI spec ([5ace8c9](https://github.com/browserbase/stagehand-python/commit/5ace8c9ee306ff8cc09c258500c24b6b55e9562b)) + + +### Chores + +* **ci:** bump uv version ([84a841c](https://github.com/browserbase/stagehand-python/commit/84a841cd29648432d713f10b37a530e18942a3f0)) + ## 3.6.0 (2026-02-25) Full Changelog: [v3.5.0...v3.6.0](https://github.com/browserbase/stagehand-python/compare/v3.5.0...v3.6.0) diff --git a/README.md b/README.md index 011544e..bd9c052 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,8 @@ Examples and dependencies: - `examples/byob_example.py`: Playwright + Playwright browsers - `examples/pydoll_tab_example.py`: `pydoll-python` (Python 3.10+) +Multiregion support: see `examples/local_server_multiregion_browser_example.py`. + Run any example: ```bash diff --git a/examples/local_server_multiregion_browser_example.py b/examples/local_server_multiregion_browser_example.py new file mode 100644 index 0000000..ca6ac89 --- /dev/null +++ b/examples/local_server_multiregion_browser_example.py @@ -0,0 +1,173 @@ +""" +Example: local Stagehand server + multiregion Browserbase browser in eu-central-1. + +What this demonstrates: +- Run Stagehand server locally (SEA) with Browserbase cloud browser +- Request a Browserbase session in eu-central-1 +- Attach Playwright to the same browser via CDP (`cdp_url`) +- Stream SSE events by default for observe/act/extract/execute +- Run the full flow: start → observe → act → extract → agent/execute → end + +Environment variables required: +- MODEL_API_KEY +- BROWSERBASE_API_KEY +- BROWSERBASE_PROJECT_ID + +Optional: +- STAGEHAND_BASE_URL (defaults to http://127.0.0.1:3000 when server="local") +""" + +from __future__ import annotations + +import os +import sys +from typing import Any, Optional + +from env import load_example_env + +from stagehand import Stagehand + + +def _print_stream_events(stream: Any, label: str) -> object | None: + result_payload: object | None = None + for event in stream: + if event.type == "log": + print(f"[{label}][log] {event.data.message}") + continue + + status = event.data.status + print(f"[{label}][system] status={status}") + if status == "finished": + result_payload = event.data.result + elif status == "error": + error_message = event.data.error or "unknown error" + raise RuntimeError(f"{label} stream reported error: {error_message}") + + return result_payload + + +def main() -> None: + load_example_env() + model_api_key = os.environ.get("MODEL_API_KEY") + if not model_api_key: + sys.exit("Set the MODEL_API_KEY environment variable to run this example.") + + bb_api_key = os.environ.get("BROWSERBASE_API_KEY") + bb_project_id = os.environ.get("BROWSERBASE_PROJECT_ID") + if not bb_api_key or not bb_project_id: + sys.exit( + "Set BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID to run this example." + ) + + try: + from playwright.sync_api import sync_playwright # type: ignore[import-not-found] + except Exception: + sys.exit( + "Playwright is not installed. Install it with:\n" + " uv pip install playwright\n" + "and ensure browsers are installed (e.g. `playwright install chromium`)." + ) + + session_id: Optional[str] = None + + with Stagehand( + server="local", + browserbase_api_key=bb_api_key, + browserbase_project_id=bb_project_id, + model_api_key=model_api_key, + local_openai_api_key=model_api_key, + local_ready_timeout_s=30.0, + ) as client: + print("⏳ Starting Stagehand session (local server + Browserbase browser)...") + session = client.sessions.start( + model_name="anthropic/claude-sonnet-4-6", + browser={"type": "browserbase"}, + browserbase_session_create_params={"region": "eu-central-1"}, + verbose=2, + ) + session_id = session.id + + cdp_url = session.data.cdp_url + if not cdp_url: + sys.exit( + "No cdp_url returned from the API for this session; cannot attach Playwright." + ) + + print(f"✅ Session started: {session_id}") + print("🔌 Connecting Playwright to the same browser over CDP...") + + try: + with sync_playwright() as p: + browser = p.chromium.connect_over_cdp(cdp_url) + try: + context = browser.contexts[0] if browser.contexts else browser.new_context() + page = context.pages[0] if context.pages else context.new_page() + + page.goto("https://example.com", wait_until="domcontentloaded") + + print("👀 Stagehand.observe(page=...) with SSE streaming...") + observe_stream = session.observe( + instruction="Find the most relevant click target on this page", + page=page, + stream_response=True, + x_stream_response="true", + ) + observe_result = _print_stream_events(observe_stream, "observe") + + actions = observe_result or [] + if not actions: + print("No actions found; ending session.") + return + + print("🖱️ Stagehand.act(page=...) with SSE streaming...") + act_stream = session.act( + input=actions[0], + page=page, + stream_response=True, + x_stream_response="true", + ) + _ = _print_stream_events(act_stream, "act") + + print("🧠 Stagehand.extract(page=...) with SSE streaming...") + extract_stream = session.extract( + instruction="Extract the page title and the primary heading (h1) text", + schema={ + "type": "object", + "properties": { + "title": {"type": "string"}, + "h1": {"type": "string"}, + }, + "required": ["title", "h1"], + "additionalProperties": False, + }, + page=page, + stream_response=True, + x_stream_response="true", + ) + extracted = _print_stream_events(extract_stream, "extract") + print("Extracted:", extracted) + + print("🤖 Stagehand.execute(page=...) with SSE streaming...") + execute_stream = session.execute( + agent_config={"model": "anthropic/claude-opus-4-6"}, + execute_options={ + "instruction": ( + "Open the 'Learn more' link if present and summarize the destination in one sentence." + ), + "max_steps": 5, + }, + page=page, + stream_response=True, + x_stream_response="true", + ) + execute_result = _print_stream_events(execute_stream, "execute") + print("Execute result:", execute_result) + finally: + browser.close() + finally: + session.end() + print("✅ Session ended.") + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 9578300..280bbe8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "stagehand" -version = "3.6.0" +version = "3.7.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 914bc18..5c32ea2 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.6.0" # x-release-please-version +__version__ = "3.7.0" # x-release-please-version diff --git a/src/stagehand/types/model_config_param.py b/src/stagehand/types/model_config_param.py index 4699e67..4c71107 100644 --- a/src/stagehand/types/model_config_param.py +++ b/src/stagehand/types/model_config_param.py @@ -19,5 +19,5 @@ class ModelConfigParam(TypedDict, total=False): base_url: Annotated[str, PropertyInfo(alias="baseURL")] """Base URL for the model provider""" - provider: Literal["openai", "anthropic", "google", "microsoft"] + provider: Literal["openai", "anthropic", "google", "microsoft", "bedrock"] """AI provider for the model (or provide a baseURL endpoint instead)""" diff --git a/src/stagehand/types/session_execute_params.py b/src/stagehand/types/session_execute_params.py index d1afa80..86c367b 100644 --- a/src/stagehand/types/session_execute_params.py +++ b/src/stagehand/types/session_execute_params.py @@ -62,7 +62,7 @@ class AgentConfig(TypedDict, total=False): 'anthropic/claude-4.5-opus') """ - provider: Literal["openai", "anthropic", "google", "microsoft"] + provider: Literal["openai", "anthropic", "google", "microsoft", "bedrock"] """AI provider for the agent (legacy, use model: openai/gpt-5-nano instead)""" system_prompt: Annotated[str, PropertyInfo(alias="systemPrompt")] diff --git a/uv.lock b/uv.lock index 4558482..6f941a3 100644 --- a/uv.lock +++ b/uv.lock @@ -1354,7 +1354,7 @@ wheels = [ [[package]] name = "stagehand" -version = "3.5.0" +version = "3.6.0" source = { editable = "." } dependencies = [ { name = "anyio" },