Metadata-Version: 2.5
Name: stagehand-openai-sdk
Version: 0.1.0
Summary: Stagehand driver for OpenAI's browser toolset: give GPT a browser through the OpenAI SDK.
Project-URL: Homepage, https://github.com/browserbase/openai-cua-toolset
Project-URL: Repository, https://github.com/browserbase/openai-cua-toolset
Author: Browserbase
License-Expression: MIT
License-File: LICENSE
Keywords: browser,browser-toolset,browserbase,computer-use,gpt,openai,stagehand
Classifier: Framework :: AsyncIO
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: browserbase<2,>=1.15
Requires-Dist: idna>=3.7
Requires-Dist: openai<4,>=3.31.0
Requires-Dist: opentelemetry-api<2,>=1.35
Requires-Dist: pydantic<3,>=2.12
Requires-Dist: stagehand<5,>=4.1.0
Requires-Dist: websockets>=16.1.1
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
Description-Content-Type: text/markdown

# stagehand-openai-sdk

Give GPT a browser through the OpenAI SDK. `StagehandBrowserDriver` implements the driver contract of OpenAI's
browser toolset (`AsyncBrowserToolset` from `openai.lib.browser`) on top of Stagehand: every member (navigation,
page reading with refs, clicks and typing, forms, screenshots and zoom, tabs, waits, plus the opt-ins below),
across iframes and open shadow roots, against a local Chrome, a Chromium you already run, or a Browserbase
session. The OpenAI SDK's `AsyncResponsesToolRunner` runs the model's tool loop; this package runs the browser.

Quickstarts for TypeScript and Python, with local Chrome and Browserbase variants:
[browserbase/openai-cua-toolset](https://github.com/browserbase/openai-cua-toolset).

## Install

```bash
pip install stagehand-openai-sdk
```

Python 3.11 or later. The distribution is `stagehand-openai-sdk`; the import is `stagehand_openai_sdk`. It
depends on `openai>=3.31.0,<4`, the releases that ship the browser toolset helpers. The Stagehand runtime this
driver needs is bundled in the package (Stagehand `4.2.0a3`), so there is nothing else to install.
The package also installs the published `stagehand` for code that imports Stagehand directly; the driver always
uses the bundled copy. Local browsers must be Chromium-based (Chrome, Chromium, Edge).

## Quick start

The driver is async only, so it pairs with `AsyncOpenAI` and `AsyncResponsesToolRunner`.

```python
import asyncio

from openai import AsyncOpenAI
from openai.lib.responses_tool_runner import AsyncResponsesToolRunner
from stagehand_openai_sdk import StagehandBrowserDriver, create_browser_toolset


async def main() -> None:
    driver = await StagehandBrowserDriver.launch(
        headless=False,  # watch the browser work; set True to hide it
        allowed_domains=["example.com", "iana.org"],
    )
    # OpenAI's AsyncBrowserToolset over the driver: the default members, a URL policy derived from the domain
    # lists, and the default confirm (a liveness check, not human consent).
    browser = create_browser_toolset(driver)
    try:
        async with AsyncOpenAI() as client:
            runner = AsyncResponsesToolRunner(browser=browser)
            response = await runner.run(
                client,
                {"model": "gpt-6-luna", "store": False, "input": "Open example.com and tell me the page heading."},
                max_steps=20,
            )
            print(response.output_text)
    finally:
        await driver.close()  # releases the browser; the toolset never closes the driver


asyncio.run(main())
```

Other browsers:

```python
await StagehandBrowserDriver.browserbase(allowed_domains=[...])  # a Browserbase session (BROWSERBASE_API_KEY)
await StagehandBrowserDriver.connect(cdp_url="ws://127.0.0.1:9222/devtools/browser/...", allowed_domains=[...])  # a running Chromium
await StagehandBrowserDriver.attach(stagehand)  # a Stagehand you already own; close() leaves it running
```

`create_browser_toolset` is a convenience. You can also build the toolset yourself:
`AsyncBrowserToolset(driver, confirm=..., url_policy=...)`, with `make_url_policy(driver)` for the domain lists.

## Options

Driver options are keyword arguments to any factory; toolset options go to `create_browser_toolset(driver, ...)`.

| Option | What it does |
| --- | --- |
| `allowed_domains` / `blocked_domains` | Hosts, IP literals, CIDR ranges or single-label hosts. A domain entry includes its subdomains and the block list wins. Enforced inside the browser on every HTTP(S) request (navigations, redirects, sub-resources, popups, workers) and WebSocket handshakes, and checked on `navigate` / `new_tab` before anything is sent. |
| `headless`, `chrome_path`, `viewport` | Local launch settings (viewport defaults to `(1280, 720)`). |
| `mask_selectors` | Elements painted over in every screenshot. |
| `privacy` | `PrivacyOptions(hide_values=...)`: which field values read as `[value hidden]` (default: password, file, card and one-time-code fields). |
| `screenshot` | `ScreenshotOptions(format, quality, detail, upload, ...)`; `upload` returns a Files API `file_id` instead of a data URL. |
| `uploads` / `downloads` | Grant resolvers for `upload_file` / `download_file` (see below). Off by default. |
| `tracer` | An OpenTelemetry tracer: one `browser.<member>` span per call (`pip install "stagehand-openai-sdk[otel]"`). |
| `timeouts`, `logging` | Every runtime call is bounded; logs go through your sink with typed text struck out. |
| `members` / `additional_members` | The exact member set, or opt-ins added to the defaults (toolset option). |
| `confirm`, `url_policy` | Your consent hook, and a policy AND-ed with the one derived from the domain lists (toolset options). |

## Enable optional tools

The default members cover reading, clicking, typing, forms, screenshots, tabs and waits. The rest are opt-ins:

| Member | What it adds | Needs your own `confirm` |
| --- | --- | --- |
| `click_at`, `hover_at`, `drag_path`, `hold_keys` | Pointer and keyboard by viewport coordinates, for canvases and custom widgets | no |
| `read_page_scoped` | Lists page scopes (landmarks, forms, dialogs, tables, iframes) and reads one subtree | no |
| `set_checked` | Sets a checkbox or radio to a state, idempotently | no |
| `run_script` | Runs JavaScript in the page | yes |
| `upload_file` / `download_file` | Moves files through single-use grants your code resolves | yes |
| `read_diagnostics` | Redacted console and network lines | yes |

Add opt-ins with `additional_members`, or pass the exact set with `members` (not both). Selecting `run_script`,
`upload_file`, `download_file` or `read_diagnostics` without a `confirm` raises `TypeError`. Your `confirm`
(an `async def`) replaces the default for every member, so approve the ones you consider safe and decide on the
rest:

```python
from stagehand_openai_sdk import (
    DownloadGrant,
    DownloadOptions,
    StagehandBrowserDriver,
    UploadOptions,
    create_browser_toolset,
)


async def resolve_upload(grant_id: str, tab_id: str | None):
    return "/srv/agent-files/report.pdf" if grant_id == "report" else None  # a path, a PayloadGrant, or None


async def deliver(path: str, candidate) -> None:
    ...  # copy `path` somewhere


async def resolve_download(grant_id: str, tab_id: str | None):
    if grant_id != "invoice":
        return None
    return DownloadGrant(accept=lambda c: c.name.endswith(".pdf"), deliver=deliver)


async def confirm(ctx: dict, **_) -> bool:
    if ctx["member"] in ("run_script", "upload_file", "download_file"):
        return await ask_a_person(ctx["member"], ctx["arguments"])  # your UI or policy
    return True


driver = await StagehandBrowserDriver.launch(
    headless=False,
    allowed_domains=["example.com"],
    uploads=UploadOptions(roots=["/srv/agent-files"], resolve_grant=resolve_upload),  # paths must sit inside roots
    downloads=DownloadOptions(resolve_grant=resolve_download),
)
browser = create_browser_toolset(
    driver,
    additional_members=["click_at", "read_page_scoped", "set_checked", "run_script", "upload_file", "download_file"],
    confirm=confirm,
)
```

Tell the model which grant ids it may use (in your instructions). It names one as `grant_id`; your resolver turns
it into a file or refuses it (`None`). Grants are single use. Downloads land in a per-driver quarantine directory
and reach your disk only through `deliver`.

## Limitations

- Domain lists cover the browser's HTTP(S) requests and WebSocket handshakes, not WebRTC or other non-HTTP
  traffic, so they are not complete network isolation. Run untrusted tasks in an isolated browser and treat
  page content as untrusted.
- JavaScript dialogs (`alert`, `confirm`, `prompt`, `beforeunload`) are answered automatically and reported to
  the model as a dialog change; the message is never shown. The OpenAI toolset has no member to accept a dialog.
- Async only: there is no sync driver.
- Local browsers must be Chromium-based. Firefox and WebKit are not supported.
- `download_file` is not available in Browserbase sessions.
- The default `confirm` checks only that the driver is alive and the tab exists. Pass your own for anything that
  needs a person or a policy decision.
- If the browser connection is lost, close the toolset and create a new driver.

## Examples

See [browserbase/openai-cua-toolset](https://github.com/browserbase/openai-cua-toolset) for runnable examples.

## License

MIT. Includes Stagehand (MIT, Browserbase) under `stagehand_openai_sdk/_vendor/stagehand`.
