Skip to content

Commit 7575ada

Browse files
monadoidstainless-app[bot]
authored andcommitted
Add local browser SSE example
1 parent 8b6ae88 commit 7575ada

2 files changed

Lines changed: 210 additions & 1 deletion

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,16 @@ export MODEL_API_KEY="your-openai-api-key"
149149
bundle exec ruby examples/local_browser_example.rb
150150
```
151151

152-
Playwright local example:
152+
Playwright local example (SSE streaming):
153153

154154
```bash
155155
gem install playwright-ruby-client
156156
npm install playwright
157157
./node_modules/.bin/playwright install chromium
158158
export MODEL_API_KEY="your-openai-api-key"
159159
bundle exec ruby examples/local_playwright_example.rb
160+
161+
bundle exec ruby examples/local_browser_playwright_example.rb
160162
```
161163

162164
Playwright remote example:
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#!/usr/bin/env ruby
2+
# typed: ignore
3+
# frozen_string_literal: true
4+
5+
require "bundler/setup"
6+
require "stagehand"
7+
8+
# Example: Using Playwright with Stagehand local mode (local browser).
9+
#
10+
# Prerequisites:
11+
# - Set MODEL_API_KEY or OPENAI_API_KEY environment variable
12+
# - Set BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID (can be any value in local mode)
13+
# - Install Playwright (outside this gem):
14+
# gem install playwright-ruby-client
15+
# npm install playwright
16+
# ./node_modules/.bin/playwright install chromium
17+
#
18+
# Run:
19+
# bundle exec ruby examples/local_browser_playwright_example.rb
20+
21+
begin
22+
require("playwright")
23+
rescue LoadError
24+
warn("Playwright is not installed. Run: gem install playwright-ruby-client")
25+
exit(1)
26+
end
27+
28+
model_key = ENV["MODEL_API_KEY"] || ENV["OPENAI_API_KEY"]
29+
browserbase_api_key = ENV["BROWSERBASE_API_KEY"].to_s
30+
browserbase_project_id = ENV["BROWSERBASE_PROJECT_ID"].to_s
31+
32+
missing = []
33+
missing << "MODEL_API_KEY" if model_key.to_s.empty?
34+
missing << "BROWSERBASE_API_KEY" if browserbase_api_key.empty?
35+
missing << "BROWSERBASE_PROJECT_ID" if browserbase_project_id.empty?
36+
37+
unless missing.empty?
38+
warn("Set #{missing.join(', ')} to run the local Playwright example.")
39+
exit(1)
40+
end
41+
42+
def print_stream_event(label, event)
43+
case event.type
44+
when :log
45+
puts("[#{label}] log: #{event.data.message}")
46+
when :system
47+
status = event.data.status
48+
if event.data.respond_to?(:error) && event.data.error
49+
puts("[#{label}] system #{status}: #{event.data.error}")
50+
elsif event.data.respond_to?(:result) && !event.data.result.nil?
51+
puts("[#{label}] system #{status}: #{event.data.result}")
52+
else
53+
puts("[#{label}] system #{status}")
54+
end
55+
else
56+
puts("[#{label}] event: #{event.inspect}")
57+
end
58+
end
59+
60+
def stream_with_result(label, stream)
61+
puts("#{label} stream:")
62+
result = nil
63+
stream.each do |event|
64+
print_stream_event(label, event)
65+
if event.type == :system && event.data.respond_to?(:result) && !event.data.result.nil?
66+
result = event.data.result
67+
end
68+
end
69+
result
70+
end
71+
72+
client = Stagehand::Client.new(
73+
browserbase_api_key: browserbase_api_key,
74+
browserbase_project_id: browserbase_project_id,
75+
model_api_key: model_key,
76+
server: "local"
77+
)
78+
79+
session_id = nil
80+
81+
begin
82+
# rubocop:disable Metrics/BlockLength
83+
Playwright.create(playwright_cli_executable_path: "./node_modules/.bin/playwright") do |playwright|
84+
browser_server = playwright.chromium.launch_server(headless: true)
85+
cdp_url = browser_server.ws_endpoint
86+
87+
start_response = client.sessions.start(
88+
model_name: "openai/gpt-5-nano",
89+
browser: {
90+
type: :local,
91+
launch_options: {
92+
cdp_url: cdp_url
93+
}
94+
}
95+
)
96+
session_id = start_response.data.session_id
97+
98+
puts("Session started: #{session_id}")
99+
puts("Connecting Playwright over CDP...")
100+
101+
browser = playwright.chromium.connect_over_cdp(cdp_url)
102+
begin
103+
context = browser.contexts.first || browser.new_context
104+
page = context.pages.first || context.new_page
105+
page.goto("https://example.com")
106+
page.wait_for_load_state(state: "domcontentloaded")
107+
108+
observe_stream = client.sessions.observe_streaming(
109+
session_id,
110+
instruction: "Find all clickable links on this page"
111+
)
112+
observe_result = stream_with_result("Observe", observe_stream)
113+
if observe_result.nil?
114+
observe_response = client.sessions.observe(
115+
session_id,
116+
instruction: "Find all clickable links on this page"
117+
)
118+
observe_result = observe_response.data.result
119+
end
120+
puts("Found #{observe_result.length} possible actions")
121+
122+
act_input = "Click the 'Learn more' link"
123+
act_stream = client.sessions.act_streaming(
124+
session_id,
125+
input: act_input
126+
)
127+
act_result = stream_with_result("Act", act_stream)
128+
if act_result.nil?
129+
act_response = client.sessions.act(
130+
session_id,
131+
input: act_input
132+
)
133+
act_result = act_response.data.result
134+
end
135+
act_message = act_result.is_a?(Hash) ? (act_result[:message] || act_result["message"]) : act_result
136+
puts("Act completed: #{act_message}")
137+
138+
extract_stream = client.sessions.extract_streaming(
139+
session_id,
140+
instruction: "Extract the main heading and any links on this page",
141+
schema: {
142+
type: "object",
143+
properties: {
144+
heading: {type: "string"},
145+
links: {type: "array", items: {type: "string"}}
146+
}
147+
}
148+
)
149+
extract_result = stream_with_result("Extract", extract_stream)
150+
if extract_result.nil?
151+
extract_response = client.sessions.extract(
152+
session_id,
153+
instruction: "Extract the main heading and any links on this page",
154+
schema: {
155+
type: "object",
156+
properties: {
157+
heading: {type: "string"},
158+
links: {type: "array", items: {type: "string"}}
159+
}
160+
}
161+
)
162+
extract_result = extract_response.data.result
163+
end
164+
puts("Extracted: #{extract_result}")
165+
166+
execute_stream = client.sessions.execute_streaming(
167+
session_id,
168+
execute_options: {
169+
instruction: "Click the 'Learn more' link if available and summarize the destination.",
170+
max_steps: 5
171+
},
172+
agent_config: {
173+
model: {
174+
model_name: "openai/gpt-5-nano",
175+
api_key: model_key
176+
},
177+
cua: false
178+
}
179+
)
180+
execute_result = stream_with_result("Execute", execute_stream)
181+
if execute_result.nil?
182+
execute_response = client.sessions.execute(
183+
session_id,
184+
execute_options: {
185+
instruction: "Click the 'Learn more' link if available and summarize the destination.",
186+
max_steps: 5
187+
},
188+
agent_config: {
189+
model: {
190+
model_name: "openai/gpt-5-nano",
191+
api_key: model_key
192+
},
193+
cua: false
194+
}
195+
)
196+
execute_result = execute_response.data.result
197+
end
198+
puts("Execute result: #{execute_result}")
199+
ensure
200+
browser.close
201+
browser_server.close
202+
end
203+
end
204+
# rubocop:enable Metrics/BlockLength
205+
ensure
206+
client.sessions.end(session_id) if session_id
207+
end

0 commit comments

Comments
 (0)