forked from browserbase/stagehand-rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbasic.rs
More file actions
178 lines (155 loc) · 5.85 KB
/
basic.rs
File metadata and controls
178 lines (155 loc) · 5.85 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use stagehand_sdk::{
ActResponseEvent, AgentConfig, AgentExecuteOptions, Env, ExecuteResponseEvent, ExtractResponseEvent, Model, ModelConfiguration, NavigateResponseEvent, ObserveResponseEvent, Stagehand, TransportChoice, V3Options
};
use futures::StreamExt;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug)]
struct Comment {
text: String,
author: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Load environment variables from .env file
dotenvy::dotenv().ok();
println!("=== Stagehand Rust SDK Example ===\n");
// Environment variables required:
// - BROWSERBASE_API_KEY
// - BROWSERBASE_PROJECT_ID
// - A model API key (OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, etc.)
// 1. Initialize client with API keys (from environment variables)
println!("1. Connecting to Stagehand...");
let mut stagehand = Stagehand::connect(TransportChoice::default_rest()).await?;
println!(" Connected!\n");
// 2. Start session with model_name (NO deprecated headers)
println!("2. Starting browser session...");
let opts = V3Options {
env: Some(Env::Browserbase),
model: Some(Model::String("openai/gpt-5-nano".into())),
verbose: Some(2),
..Default::default()
};
stagehand.start(opts).await?;
println!(" Session ID: {:?}\n", stagehand.session_id());
// 3. Navigate to https://news.ycombinator.com
println!("3. Navigating to Hacker News...");
let mut nav_stream = stagehand
.navigate("https://news.ycombinator.com", None, None)
.await?;
while let Some(res) = nav_stream.next().await {
if let Ok(response) = res {
if let Some(NavigateResponseEvent::Success(success)) = response.event {
println!(" Navigation success: {}\n", success);
}
}
}
// 4. Observe to find "link to view comments for the top post"
println!("4. Finding link to view comments for the top post...");
let mut observe_stream = stagehand
.observe(
Some("Find the link to view comments for the top post".to_string()),
None,
Some(60_000),
None,
None,
)
.await?;
let mut elements_json = String::new();
while let Some(res) = observe_stream.next().await {
if let Ok(response) = res {
if let Some(ObserveResponseEvent::ElementsJson(json)) = response.event {
elements_json = json;
println!(" Found elements!\n");
}
}
}
// 5. Act on the first action from observe results
println!("5. Clicking on the comments link...");
let mut act_stream = stagehand
.act(
"Click on the comments link for the top post",
None,
HashMap::new(),
Some(60_000),
None,
)
.await?;
while let Some(res) = act_stream.next().await {
if let Ok(response) = res {
if let Some(ActResponseEvent::Success(success)) = response.event {
println!(" Click success: {}\n", success);
}
}
}
// 6. Extract top comment text + author using JSON schema
println!("6. Extracting top comment and author...");
let schema = serde_json::json!({
"type": "object",
"properties": {
"text": { "type": "string", "description": "The text content of the top comment" },
"author": { "type": "string", "description": "The username of the comment author" }
},
"required": ["text", "author"]
});
let mut extract_stream = stagehand
.extract(
"Extract the text and author of the top comment on the page",
schema,
None,
Some(60_000),
None,
None,
)
.await?;
let mut comment_data = String::new();
while let Some(res) = extract_stream.next().await {
if let Ok(response) = res {
if let Some(ExtractResponseEvent::DataJson(json)) = response.event {
comment_data = json.clone();
if let Ok(comment) = serde_json::from_str::<Comment>(&json) {
println!(" Top comment by {}: {}\n", comment.author, comment.text);
}
}
}
}
// 7. Execute autonomous agent to find author's profile (GitHub/LinkedIn/website)
println!("7. Finding author's profile using autonomous agent...");
let agent_config = AgentConfig {
provider: None,
model: Some(ModelConfiguration::String("openai/gpt-5-nano".into())),
system_prompt: None,
cua: None,
};
let comment: Comment = serde_json::from_str(&comment_data)?;
let execute_options = AgentExecuteOptions {
instruction: format!(
"Find the profile page for the Hacker News user '{}'. Look for links to their GitHub, LinkedIn, or personal website.",
comment.author
),
max_steps: Some(10),
highlight_cursor: None,
};
let mut execute_stream = stagehand
.execute(agent_config, execute_options, None)
.await?;
while let Some(res) = execute_stream.next().await {
if let Ok(response) = res {
match response.event {
Some(ExecuteResponseEvent::Log(log)) => {
println!(" [Agent Log] {:?}", log);
}
Some(ExecuteResponseEvent::ResultJson(result)) => {
println!(" Agent result: {}\n", result);
}
_ => {}
}
}
}
// 8. End session
println!("8. Closing session...");
stagehand.end().await?;
println!(" Session closed successfully!\n");
println!("=== Example completed! ===");
Ok(())
}