Skip to content

Commit 6dcb4fd

Browse files
committed
docs: add Stagehand wrapper example
1 parent bfdb476 commit 6dcb4fd

3 files changed

Lines changed: 89 additions & 0 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ await page.extract("get the headline") # auto-recorded
162162
page.bt_run.close()
163163
```
164164

165+
For a no-network wrapper demo that does not require a Stagehand install, run
166+
`python examples/stagehand_wrapper_example.py`.
167+
165168
### Skyvern integration
166169

167170
Wrap a Skyvern-shaped client to record high-level task and workflow calls.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Run the Stagehand wrapper without installing Stagehand.
2+
3+
This uses a fake Stagehand-shaped page so the example is deterministic:
4+
5+
python examples/stagehand_wrapper_example.py
6+
7+
Then open the BrowserTrace UI:
8+
9+
browsertrace
10+
"""
11+
12+
from __future__ import annotations
13+
14+
import asyncio
15+
import os
16+
17+
from browsertrace import Tracer
18+
from browsertrace.integrations.stagehand import wrap_stagehand
19+
20+
21+
class DemoStagehandPage:
22+
url = "https://shop.example.test/cart"
23+
24+
async def screenshot(self) -> bytes:
25+
return b"\x89PNG\r\n\x1a\n" + b"\x00" * 16
26+
27+
async def act(self, instruction: str) -> dict[str, str]:
28+
await asyncio.sleep(0)
29+
return {"status": "completed", "instruction": instruction}
30+
31+
async def extract(self, instruction: str) -> dict[str, str]:
32+
await asyncio.sleep(0)
33+
return {
34+
"status": "completed",
35+
"instruction": instruction,
36+
"order_total": "$42.00",
37+
}
38+
39+
40+
async def main() -> None:
41+
tracer = Tracer(home=os.environ.get("BROWSERTRACE_HOME"))
42+
page = wrap_stagehand(
43+
DemoStagehandPage(),
44+
tracer,
45+
name="demo: stagehand checkout flow",
46+
)
47+
48+
await page.act("click the checkout button")
49+
await page.extract("extract the order total")
50+
page.bt_run.close()
51+
52+
print(f"BrowserTrace run id: {page.bt_run.id}")
53+
print("Recorded Stagehand calls: act, extract")
54+
print("Open the local UI with: browsertrace")
55+
56+
57+
if __name__ == "__main__":
58+
asyncio.run(main())

tests/test_examples.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,31 @@ def test_skyvern_wrapper_example_creates_completed_trace(tmp_path, monkeypatch):
5555
assert json.loads(step[2])["kwargs"]["url"] == "https://example.com/invoice"
5656
assert json.loads(step[3])["task_run_id"] == "tsk_demo_001"
5757
assert json.loads(step[4])["skyvern_run_id"] == "tsk_demo_001"
58+
59+
60+
def test_stagehand_wrapper_example_creates_completed_trace(tmp_path, monkeypatch):
61+
monkeypatch.setenv("BROWSERTRACE_HOME", str(tmp_path))
62+
63+
runpy.run_path("examples/stagehand_wrapper_example.py", run_name="__main__")
64+
65+
with sqlite3.connect(tmp_path / "db.sqlite") as c:
66+
run = c.execute(
67+
"SELECT id, name, status, error FROM runs ORDER BY started_at DESC LIMIT 1"
68+
).fetchone()
69+
steps = c.execute(
70+
"SELECT action, url, status, model_input, screenshot_path "
71+
"FROM steps WHERE run_id=? ORDER BY step_index",
72+
(run[0],),
73+
).fetchall()
74+
75+
assert run[1] == "demo: stagehand checkout flow"
76+
assert run[2] == "completed"
77+
assert run[3] is None
78+
assert [step[0] for step in steps] == [
79+
"act: click the checkout button",
80+
"extract: extract the order total",
81+
]
82+
assert steps[0][1] == "https://shop.example.test/cart"
83+
assert steps[0][2] == "ok"
84+
assert json.loads(steps[0][3])["method"] == "act"
85+
assert steps[0][4].endswith(".png")

0 commit comments

Comments
 (0)