brainwires_tool_system/
web.rs1use anyhow::Result;
2use reqwest::Client;
3use serde::Deserialize;
4use serde_json::{Value, json};
5use std::collections::HashMap;
6
7use brainwires_core::{Tool, ToolContext, ToolInputSchema, ToolResult};
8
9pub struct WebTool;
11
12impl WebTool {
13 pub fn get_tools() -> Vec<Tool> {
15 vec![Self::fetch_url_tool()]
16 }
17
18 fn fetch_url_tool() -> Tool {
19 let mut properties = HashMap::new();
20 properties.insert(
21 "url".to_string(),
22 json!({"type": "string", "description": "URL to fetch"}),
23 );
24 Tool {
25 name: "fetch_url".to_string(),
26 description: "Fetch content from a URL on the internet.".to_string(),
27 input_schema: ToolInputSchema::object(properties, vec!["url".to_string()]),
28 requires_approval: false,
29 ..Default::default()
30 }
31 }
32
33 #[tracing::instrument(name = "tool.execute", skip(input, _context), fields(tool_name))]
35 pub async fn execute(
36 tool_use_id: &str,
37 tool_name: &str,
38 input: &Value,
39 _context: &ToolContext,
40 ) -> ToolResult {
41 let result = match tool_name {
42 "fetch_url" => Self::fetch_url(input).await,
43 _ => Err(anyhow::anyhow!("Unknown web tool: {}", tool_name)),
44 };
45 match result {
46 Ok(output) => ToolResult::success(tool_use_id.to_string(), output),
47 Err(e) => ToolResult::error(
48 tool_use_id.to_string(),
49 format!("Web operation failed: {}", e),
50 ),
51 }
52 }
53
54 async fn fetch_url(input: &Value) -> Result<String> {
55 #[derive(Deserialize)]
56 struct Input {
57 url: String,
58 }
59 let params: Input = serde_json::from_value(input.clone())?;
60 let client = Client::new();
61 let response = client.get(¶ms.url).send().await?;
62 let text = response.text().await?;
63 Ok(format!(
64 "URL: {}\nContent length: {} bytes\n\n{}",
65 params.url,
66 text.len(),
67 text
68 ))
69 }
70
71 pub async fn fetch_url_content(url: &str) -> Result<String> {
73 let client = Client::new();
74 let response = client.get(url).send().await?;
75 let text = response.text().await?;
76 Ok(format!(
77 "URL: {}\nContent length: {} bytes\n\n{}",
78 url,
79 text.len(),
80 text
81 ))
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 fn create_test_context() -> ToolContext {
90 ToolContext {
91 working_directory: ".".to_string(),
92 ..Default::default()
93 }
94 }
95
96 #[test]
97 fn test_get_tools() {
98 let tools = WebTool::get_tools();
99 assert_eq!(tools.len(), 1);
100 assert_eq!(tools[0].name, "fetch_url");
101 }
102
103 #[tokio::test]
104 async fn test_execute_unknown_tool() {
105 let context = create_test_context();
106 let input = json!({"url": "https://example.com"});
107 let result = WebTool::execute("1", "unknown_tool", &input, &context).await;
108 assert!(result.is_error);
109 }
110}