stagehand-middleware 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: stagehand-middleware
3
+ Version: 0.1.0
4
+ Summary: Stagehand's facade tools (run, snapshot, screenshot) for Python hosts
5
+ Author: Browserbase
6
+ License-Expression: MIT
7
+ Requires-Dist: jsonschema>=4.23,<5
8
+ Requires-Dist: stagehand>=4.3.0,<5
9
+ Requires-Dist: tree-sitter>=0.25,<0.27
10
+ Requires-Dist: tree-sitter-javascript>=0.25,<0.26
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+
14
+ # stagehand-middleware (Python)
15
+
16
+ The facade's `run`, `snapshot` and `screenshot` tools for Python, over an existing
17
+ `stagehand.Stagehand`. It is a thin host for the facade bundle that
18
+ `@browserbasehq/stagehand-integrations` builds (`dist/facade-bundle.json`): the
19
+ JavaScript sent to `experimental_batch` and the model-facing contract (tool names,
20
+ descriptions, input schemas, error messages and result caps) come from that
21
+ bundle, so agents see the same tools as with the TypeScript facade.
22
+
23
+ ```python
24
+ from stagehand import Stagehand
25
+ from stagehand_middleware.facade import StagehandFacadeTools, facade_instructions, facade_tools
26
+
27
+ stagehand = await Stagehand.create(...)
28
+ tools = StagehandFacadeTools(stagehand, artifact_root="artifacts")
29
+
30
+ await tools.run(code="await page.goto('https://example.com'); return await page.title();")
31
+ tree = await tools.snapshot()
32
+ await tools.run(actions=[{"op": "click", "id": "0-7"}])
33
+ image = await tools.screenshot(type="jpeg", quality=40)
34
+
35
+ facade_tools() # tools/list entries to register with an agent framework
36
+ facade_instructions() # the agent instructions for the system prompt
37
+ ```
38
+
39
+ - `run` code runs inside an async function with Playwright-shaped `page`,
40
+ `context` and `browser`. An error it throws is raised as
41
+ `StagehandFacadeExecutionError` (with the JavaScript `name` and `stack`).
42
+ `StagehandFacadeInputError` covers bad input and contract errors (stale
43
+ snapshot IDs, malformed run results, artifact paths and caps, a closed facade).
44
+ Other failures keep their own types: an error from the batch or RPC call
45
+ itself (for example `stagehand.rpc_client.RPCError`) propagates unchanged, a snapshot or
46
+ screenshot past its capture deadline raises `TimeoutError`, and a failed close
47
+ raises `StagehandFacadeCleanupError` (or an `ExceptionGroup` with the run's own
48
+ error). Code that does not parse as an async function body raises
49
+ `SyntaxError` before anything is sent.
50
+ - Screenshots saved with a `path` are written under `artifact_root` (default: the
51
+ current directory). A path must stay within it and must not pass through a
52
+ symlink.
53
+ - `browser.close()` in run code closes the Stagehand and its browser once the
54
+ call finishes (pass `on_close_requested` to own that cleanup); later calls fail.
55
+ - `quiet_wait=True` (experimental, off by default) makes a `page.waitForTimeout(ms)`
56
+ longer than 400 ms in run code return once the page is quiet (DOM loaded, no
57
+ mutation in the main document for 400 ms; shadow roots are not observed),
58
+ never later than `ms`. Shorter waits sleep in full.
59
+
60
+ This host has no keeper tab and does not latch session loss; the TypeScript
61
+ `StagehandFacadeTools` does both.
62
+
63
+ ## Development
64
+
65
+ `src/stagehand_middleware/facade/_bundle.json` is a copy of the built bundle.
66
+ After changing the TypeScript facade, rebuild the integrations package and run
67
+ `pnpm run sync:python` in `packages/integrations/core`;
68
+ `tests/facade-python-bundle.test.ts` and
69
+ `node scripts/sync-python-facade-bundle.mjs --check` (which CI runs in the
70
+ Python job) fail while the copy is stale. The package also checks the copy's
71
+ `sha256` when it loads it.
72
+
73
+ ```bash
74
+ uv run ruff format --check
75
+ uv run ruff check
76
+ uv run ty check
77
+ uv run pytest
78
+ ```
79
+
80
+ `tests/test_conformance.py` runs the cases in `../conformance` against this host.
81
+ `tests/test_browser.py` drives a local Chrome end to end; it is skipped unless
82
+ `STAGEHAND_INTEGRATIONS_BROWSER_TESTS=1` (set `CHROME_PATH` if the Stagehand SDK
83
+ does not find Chrome).
@@ -0,0 +1,70 @@
1
+ # stagehand-middleware (Python)
2
+
3
+ The facade's `run`, `snapshot` and `screenshot` tools for Python, over an existing
4
+ `stagehand.Stagehand`. It is a thin host for the facade bundle that
5
+ `@browserbasehq/stagehand-integrations` builds (`dist/facade-bundle.json`): the
6
+ JavaScript sent to `experimental_batch` and the model-facing contract (tool names,
7
+ descriptions, input schemas, error messages and result caps) come from that
8
+ bundle, so agents see the same tools as with the TypeScript facade.
9
+
10
+ ```python
11
+ from stagehand import Stagehand
12
+ from stagehand_middleware.facade import StagehandFacadeTools, facade_instructions, facade_tools
13
+
14
+ stagehand = await Stagehand.create(...)
15
+ tools = StagehandFacadeTools(stagehand, artifact_root="artifacts")
16
+
17
+ await tools.run(code="await page.goto('https://example.com'); return await page.title();")
18
+ tree = await tools.snapshot()
19
+ await tools.run(actions=[{"op": "click", "id": "0-7"}])
20
+ image = await tools.screenshot(type="jpeg", quality=40)
21
+
22
+ facade_tools() # tools/list entries to register with an agent framework
23
+ facade_instructions() # the agent instructions for the system prompt
24
+ ```
25
+
26
+ - `run` code runs inside an async function with Playwright-shaped `page`,
27
+ `context` and `browser`. An error it throws is raised as
28
+ `StagehandFacadeExecutionError` (with the JavaScript `name` and `stack`).
29
+ `StagehandFacadeInputError` covers bad input and contract errors (stale
30
+ snapshot IDs, malformed run results, artifact paths and caps, a closed facade).
31
+ Other failures keep their own types: an error from the batch or RPC call
32
+ itself (for example `stagehand.rpc_client.RPCError`) propagates unchanged, a snapshot or
33
+ screenshot past its capture deadline raises `TimeoutError`, and a failed close
34
+ raises `StagehandFacadeCleanupError` (or an `ExceptionGroup` with the run's own
35
+ error). Code that does not parse as an async function body raises
36
+ `SyntaxError` before anything is sent.
37
+ - Screenshots saved with a `path` are written under `artifact_root` (default: the
38
+ current directory). A path must stay within it and must not pass through a
39
+ symlink.
40
+ - `browser.close()` in run code closes the Stagehand and its browser once the
41
+ call finishes (pass `on_close_requested` to own that cleanup); later calls fail.
42
+ - `quiet_wait=True` (experimental, off by default) makes a `page.waitForTimeout(ms)`
43
+ longer than 400 ms in run code return once the page is quiet (DOM loaded, no
44
+ mutation in the main document for 400 ms; shadow roots are not observed),
45
+ never later than `ms`. Shorter waits sleep in full.
46
+
47
+ This host has no keeper tab and does not latch session loss; the TypeScript
48
+ `StagehandFacadeTools` does both.
49
+
50
+ ## Development
51
+
52
+ `src/stagehand_middleware/facade/_bundle.json` is a copy of the built bundle.
53
+ After changing the TypeScript facade, rebuild the integrations package and run
54
+ `pnpm run sync:python` in `packages/integrations/core`;
55
+ `tests/facade-python-bundle.test.ts` and
56
+ `node scripts/sync-python-facade-bundle.mjs --check` (which CI runs in the
57
+ Python job) fail while the copy is stale. The package also checks the copy's
58
+ `sha256` when it loads it.
59
+
60
+ ```bash
61
+ uv run ruff format --check
62
+ uv run ruff check
63
+ uv run ty check
64
+ uv run pytest
65
+ ```
66
+
67
+ `tests/test_conformance.py` runs the cases in `../conformance` against this host.
68
+ `tests/test_browser.py` drives a local Chrome end to end; it is skipped unless
69
+ `STAGEHAND_INTEGRATIONS_BROWSER_TESTS=1` (set `CHROME_PATH` if the Stagehand SDK
70
+ does not find Chrome).
@@ -0,0 +1,66 @@
1
+ [project]
2
+ name = "stagehand-middleware"
3
+ version = "0.1.0"
4
+ description = "Stagehand's facade tools (run, snapshot, screenshot) for Python hosts"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "jsonschema>=4.23,<5",
9
+ "stagehand>=4.3.0,<5",
10
+ "tree-sitter>=0.25,<0.27",
11
+ "tree-sitter-javascript>=0.25,<0.26",
12
+ ]
13
+ license = "MIT"
14
+
15
+ [[project.authors]]
16
+ name = "Browserbase"
17
+
18
+ [build-system]
19
+ requires = ["uv_build>=0.11.29,<0.12.0"]
20
+ build-backend = "uv_build"
21
+
22
+ [tool.uv.build-backend]
23
+ module-name = "stagehand_middleware"
24
+
25
+ [tool.uv.sources.stagehand]
26
+ path = "../../../sdk-python"
27
+ editable = true
28
+
29
+ [tool.ruff]
30
+ target-version = "py311"
31
+ line-length = 100
32
+ preview = true
33
+
34
+ [tool.ruff.lint]
35
+ select = [
36
+ "ANN",
37
+ "E",
38
+ "F",
39
+ "I",
40
+ "PYI",
41
+ "UP",
42
+ ]
43
+
44
+ [tool.ty.environment]
45
+ python-version = "3.11"
46
+
47
+ [tool.ty.rules]
48
+ missing-type-argument = "error"
49
+ possibly-unresolved-reference = "error"
50
+ unsound-return-statement = "error"
51
+ dynamic-function-decorator-return = "error"
52
+ missing-override-decorator = "error"
53
+ unused-ignore-comment = "error"
54
+
55
+ [tool.pytest.ini_options]
56
+ asyncio_mode = "auto"
57
+ testpaths = ["tests"]
58
+ strict = true
59
+
60
+ [dependency-groups]
61
+ dev = [
62
+ "pytest==9.0.3",
63
+ "pytest-asyncio==1.3.0",
64
+ "ruff==0.15.7",
65
+ "ty==0.0.82",
66
+ ]
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "stagehand-middleware"
3
+ version = "0.1.0"
4
+ description = "Stagehand's facade tools (run, snapshot, screenshot) for Python hosts"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "jsonschema>=4.23,<5",
9
+ "stagehand>=4.3.0,<5",
10
+ "tree-sitter>=0.25,<0.27",
11
+ "tree-sitter-javascript>=0.25,<0.26",
12
+ ]
13
+ license = "MIT"
14
+ authors = [{ name = "Browserbase" }]
15
+
16
+ [build-system]
17
+ requires = ["uv_build>=0.11.29,<0.12.0"]
18
+ build-backend = "uv_build"
19
+
20
+ [tool.uv.build-backend]
21
+ module-name = "stagehand_middleware"
22
+
23
+ [tool.uv.sources]
24
+ stagehand = { path = "../../../sdk-python", editable = true }
25
+
26
+ [tool.ruff]
27
+ target-version = "py311"
28
+ line-length = 100
29
+ preview = true
30
+
31
+ [tool.ruff.lint]
32
+ select = ["ANN", "E", "F", "I", "PYI", "UP"]
33
+
34
+ [tool.ty.environment]
35
+ python-version = "3.11"
36
+
37
+ [tool.ty.rules]
38
+ missing-type-argument = "error"
39
+ possibly-unresolved-reference = "error"
40
+ unsound-return-statement = "error"
41
+ dynamic-function-decorator-return = "error"
42
+ missing-override-decorator = "error"
43
+ unused-ignore-comment = "error"
44
+
45
+ [tool.pytest.ini_options]
46
+ asyncio_mode = "auto"
47
+ testpaths = ["tests"]
48
+ strict = true
49
+
50
+ [dependency-groups]
51
+ dev = ["pytest==9.0.3", "pytest-asyncio==1.3.0", "ruff==0.15.7", "ty==0.0.82"]
@@ -0,0 +1 @@
1
+ """Shared integration surfaces for Stagehand; see `stagehand_middleware.facade`."""
@@ -0,0 +1,30 @@
1
+ """Stagehand's facade tools for Python: one bundle of browser-side sources and
2
+ model-facing contract, shared with the TypeScript facade."""
3
+
4
+ from .bundle import (
5
+ StagehandFacadeBundleError,
6
+ ToolSet,
7
+ contract_error,
8
+ facade_bundle,
9
+ facade_instructions,
10
+ facade_tools,
11
+ )
12
+ from .tools import (
13
+ StagehandFacadeCleanupError,
14
+ StagehandFacadeExecutionError,
15
+ StagehandFacadeInputError,
16
+ StagehandFacadeTools,
17
+ )
18
+
19
+ __all__ = [
20
+ "StagehandFacadeBundleError",
21
+ "StagehandFacadeCleanupError",
22
+ "StagehandFacadeExecutionError",
23
+ "StagehandFacadeInputError",
24
+ "StagehandFacadeTools",
25
+ "ToolSet",
26
+ "contract_error",
27
+ "facade_bundle",
28
+ "facade_instructions",
29
+ "facade_tools",
30
+ ]