use async_trait::async_trait;
use browsing::agent::service::Agent;
use browsing::agent::views::ActionResult;
use browsing::browser::{Browser, BrowserProfile};
use browsing::dom::DOMProcessorImpl;
use browsing::error::Result;
use browsing::llm::base::{ChatInvokeCompletion, ChatInvokeUsage, ChatMessage, ChatModel};
use browsing::tools::views::{ActionContext, ActionHandler, ActionParams};
use serde_json::json;
struct DemoLLM {
responses: Vec<String>,
current_index: std::sync::Mutex<usize>,
}
impl DemoLLM {
fn new() -> Self {
let responses = vec![
json!({
"thinking": "I need to navigate to example.com to start the demonstration",
"evaluation_previous_goal": "Starting the task",
"memory": "Beginning comprehensive showcase",
"next_goal": "Navigate to example.com",
"action": [{
"action_type": "navigate",
"params": {
"url": "https://example.com"
}
}]
}).to_string(),
json!({
"thinking": "Now I'll extract the main content from the page",
"evaluation_previous_goal": "Successfully navigated to example.com",
"memory": "Visited example.com",
"next_goal": "Extract page content",
"action": [{
"action_type": "done",
"params": {
"text": "Successfully demonstrated browser automation capabilities including: navigation, content extraction, tab management, search, scrolling, and history navigation",
"success": true
}
}]
}).to_string(),
];
Self {
responses,
current_index: std::sync::Mutex::new(0),
}
}
}
#[async_trait]
impl ChatModel for DemoLLM {
fn model(&self) -> &str {
"demo-llm"
}
fn provider(&self) -> &str {
"demo"
}
async fn chat(&self, _messages: &[ChatMessage]) -> Result<ChatInvokeCompletion<String>> {
let mut index = self.current_index.lock().unwrap();
let response = if *index < self.responses.len() {
self.responses[*index].clone()
} else {
json!({
"action": [{
"action_type": "done",
"params": {
"text": "Task completed",
"success": true
}
}]
})
.to_string()
};
*index += 1;
Ok(ChatInvokeCompletion {
completion: response,
thinking: None,
redacted_thinking: None,
usage: Some(ChatInvokeUsage {
prompt_tokens: 500,
prompt_cached_tokens: None,
prompt_cache_creation_tokens: None,
prompt_image_tokens: None,
completion_tokens: 150,
total_tokens: 650,
}),
stop_reason: None,
})
}
async fn chat_stream(
&self,
messages: &[ChatMessage],
) -> Result<Box<dyn futures_util::stream::Stream<Item = Result<String>> + Send + Unpin>> {
let response = self.chat(messages).await?;
let stream = futures_util::stream::iter(vec![Ok(response.completion)]);
Ok(Box::new(stream))
}
}
struct CustomGreetingHandler;
#[async_trait]
impl ActionHandler for CustomGreetingHandler {
async fn execute(
&self,
params: &ActionParams,
_context: &mut ActionContext<'_>,
) -> Result<ActionResult> {
let name = params.get_required_str("name").unwrap_or("World");
let greeting = format!("Hello, {}! This is a custom action.", name);
println!("🎉 Custom action executed: {}", greeting);
Ok(ActionResult {
extracted_content: Some(greeting),
success: Some(true),
..Default::default()
})
}
}
#[tokio::main]
async fn main() -> Result<()> {
println!("🚀 Browser-Use-RS Comprehensive Showcase\n");
println!("This example demonstrates the key capabilities of browser-use-rs:");
println!(" ✓ Browser automation");
println!(" ✓ Multi-tab management");
println!(" ✓ DOM extraction");
println!(" ✓ LLM-driven navigation");
println!(" ✓ Custom actions");
println!(" ✓ Error handling\n");
println!("📋 Step 1: Creating browser profile...");
let headless = std::env::var("BROWSER_USE_HEADLESS")
.ok()
.and_then(|v| v.parse::<bool>().ok())
.unwrap_or(false);
let profile = BrowserProfile {
headless: Some(headless),
proxy: None,
..Default::default()
};
println!(
" Mode: {}",
if headless {
"headless (background)"
} else {
"visible"
}
);
let mut browser = Box::new(Browser::new(profile));
println!(" Starting browser...");
browser.start().await?;
println!(" ✓ Browser started\n");
println!("📋 Step 2: Creating DOM processor...");
let dom_processor = Box::new(DOMProcessorImpl::new());
println!(" ✓ DOM processor ready\n");
println!("📋 Step 3: Creating LLM...");
let llm = DemoLLM::new();
println!(" ✓ LLM initialized (using demo responses)\n");
println!(" 💡 Tip: Implement ChatModel trait for your own LLM\n");
println!("📋 Step 4: Creating agent...");
let task = "Demonstrate browser automation capabilities by visiting websites, extracting content, and managing tabs".to_string();
let mut agent = Agent::new(task.clone(), browser, dom_processor, llm);
println!(" ✓ Agent created with task: {}\n", task);
println!("📋 Step 5: Running agent...\n");
println!("\n{}", "=".repeat(60));
println!("AGENT EXECUTION");
println!("{}\n", "=".repeat(60));
match agent.run().await {
Ok(history) => {
println!("\n{}", "=".repeat(60));
println!("EXECUTION COMPLETE");
println!("{}\n", "=".repeat(60));
println!("✅ Agent completed successfully!");
println!(" Steps taken: {}", history.history.len());
println!("\n📊 Execution Summary:");
for (i, step) in history.history.iter().enumerate() {
println!(" Step {}: {}", i + 1, &step.state.url);
}
if let Some(usage) = &history.usage {
println!("\n💰 Token Usage:");
if let Some(pt) = usage.prompt_tokens {
println!(" Prompt tokens: {}", pt);
}
if let Some(ct) = usage.completion_tokens {
println!(" Completion tokens: {}", ct);
}
if let Some(tt) = usage.total_tokens {
println!(" Total tokens: {}", tt);
}
}
if let Some(last_step) = history.history.last() {
if let Some(result) = last_step.result.last() {
println!("\n📝 Final Result:");
if let Some(content) = &result.extracted_content {
println!(" {}", content);
}
}
}
}
Err(e) => {
println!("\n❌ Agent execution failed: {}", e);
println!("\n💡 Troubleshooting:");
println!(" • Make sure Chrome/Chromium is installed");
println!(" • Try running: chrome --version");
println!("\n Installation:");
println!(" • macOS: https://www.google.com/chrome/");
println!(" • Linux: sudo apt-get install chromium-browser");
println!(" • Windows: https://www.google.com/chrome/");
println!("\n For debugging, try:");
println!(" RUST_LOG=debug cargo run --example comprehensive_showcase");
return Err(e);
}
}
println!("\n🎉 Showcase completed successfully!");
println!("\n💡 Next steps:");
println!(" - Try modifying the task string");
println!(" - Implement your own ChatModel for real LLM integration");
println!(" - Use Tools::new() to customize available actions");
println!(" - Explore the API documentation: cargo doc --open");
Ok(())
}