-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathmain.py
More file actions
110 lines (93 loc) · 3.82 KB
/
Copy pathmain.py
File metadata and controls
110 lines (93 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""Persist and verify an authenticated Browserbase context with Stagehand V4."""
import asyncio
import json
import os
from browserbase import AsyncBrowserbase
from dotenv import load_dotenv
from pydantic import BaseModel, Field
from stagehand import Stagehand, browserbase
load_dotenv()
TARGET_URL = "https://www.rec.us/organizations/san-francisco-rec-park"
class UserData(BaseModel):
full_name: str = Field(min_length=1)
address: str = Field(min_length=1)
def require_env(name: str) -> str:
value = os.environ.get(name)
if not value:
raise RuntimeError(f"{name} is required")
return value
async def login_and_persist(context_id: str) -> None:
browser = await browserbase.launch(
api_key=require_env("BROWSERBASE_API_KEY"),
browser_settings={"context": {"id": context_id, "persist": True}},
)
try:
stagehand = await Stagehand.create(
browser=browser,
)
try:
pages = await browser.context.pages()
page = pages[0] if pages else await browser.context.new_page()
await page.goto(TARGET_URL, wait_until="domcontentloaded", timeout=60_000)
await stagehand.act("Click the Login button", page=page)
await stagehand.act(
"Fill the email or username field with %email%",
page=page,
variables={"email": require_env("SF_REC_PARK_EMAIL")},
)
await stagehand.act("Click the next, continue, or submit button", page=page)
await stagehand.act(
"Fill the password field with %password%",
page=page,
variables={"password": require_env("SF_REC_PARK_PASSWORD")},
)
await stagehand.act("Click the login, sign in, or submit button", page=page)
finally:
await stagehand.close()
finally:
await browser.close()
async def verify_reused_context(context_id: str) -> UserData:
browser = await browserbase.launch(
api_key=require_env("BROWSERBASE_API_KEY"),
browser_settings={"context": {"id": context_id, "persist": True}},
)
try:
stagehand = await Stagehand.create(
browser=browser,
)
try:
pages = await browser.context.pages()
page = pages[0] if pages else await browser.context.new_page()
await page.goto(TARGET_URL, wait_until="domcontentloaded", timeout=60_000)
await stagehand.act("Click the reservations button", page=page)
extracted = await stagehand.extract(
"Extract the authenticated user's full name and address",
UserData,
page=page,
)
return extracted.data
finally:
await stagehand.close()
finally:
await browser.close()
async def main() -> None:
async with AsyncBrowserbase(api_key=require_env("BROWSERBASE_API_KEY")) as api:
context = await api.contexts.create()
print("Created temporary Browserbase context")
try:
await login_and_persist(context.id)
user = await verify_reused_context(context.id)
print("Reused context reached authenticated profile data:")
print(json.dumps(user.model_dump(mode="json"), indent=2))
finally:
# The generated SDK currently sets a JSON content type on DELETE, so send an
# explicit empty object instead of an empty body.
await api.contexts.delete(context.id, extra_body={})
print("Deleted temporary Browserbase context")
if __name__ == "__main__":
try:
asyncio.run(main())
except Exception as error:
print(f"Context authentication example failed: {error}")
print("Docs: https://docs.stagehand.dev/v4/first-steps/introduction")
raise