Skip to main content

agent_air_runtime/controller/tools/
web_search.rs

1//! Web Search tool implementation
2//!
3//! This is a wrapper around Claude's built-in web search tool.
4//! The actual execution happens on Claude's side, so the exec function is a no-op.
5
6use std::collections::HashMap;
7use std::future::Future;
8use std::pin::Pin;
9
10use super::types::{
11    DisplayConfig, DisplayResult, Executable, ResultContentType, ToolContext, ToolType,
12};
13
14/// Web Search tool name constant.
15pub const WEB_SEARCH_TOOL_NAME: &str = "web_search";
16
17/// Web Search tool description constant.
18pub const WEB_SEARCH_TOOL_DESCRIPTION: &str =
19    "Performs web searches to find current information from the internet.";
20
21/// Web Search tool JSON schema constant.
22pub const WEB_SEARCH_TOOL_SCHEMA: &str = r#"{
23    "type": "object",
24    "properties": {
25        "query": {
26            "type": "string",
27            "description": "The search query"
28        }
29    },
30    "required": ["query"]
31}"#;
32
33/// Tool that performs web searches using Claude's built-in capability.
34pub struct WebSearchTool;
35
36impl WebSearchTool {
37    /// Create a new WebSearchTool instance.
38    pub fn new() -> Self {
39        Self
40    }
41}
42
43impl Default for WebSearchTool {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl Executable for WebSearchTool {
50    fn name(&self) -> &str {
51        WEB_SEARCH_TOOL_NAME
52    }
53
54    fn description(&self) -> &str {
55        WEB_SEARCH_TOOL_DESCRIPTION
56    }
57
58    fn input_schema(&self) -> &str {
59        WEB_SEARCH_TOOL_SCHEMA
60    }
61
62    fn tool_type(&self) -> ToolType {
63        ToolType::WebSearch
64    }
65
66    fn execute(
67        &self,
68        _context: ToolContext,
69        _input: HashMap<String, serde_json::Value>,
70    ) -> Pin<Box<dyn Future<Output = Result<String, String>> + Send>> {
71        // This is a no-op because Claude handles web search natively.
72        // We never actually run this - it's just to satisfy the Executable trait.
73        Box::pin(async move {
74            // This should never be called as Claude handles web search natively
75            Ok(String::new())
76        })
77    }
78
79    fn display_config(&self) -> DisplayConfig {
80        DisplayConfig {
81            display_name: "Web Search".to_string(),
82            display_title: Box::new(|input| {
83                input
84                    .get("query")
85                    .and_then(|v| v.as_str())
86                    .unwrap_or("")
87                    .to_string()
88            }),
89            display_content: Box::new(|_input, _result| {
90                // Web search results are handled by citations, return minimal content
91                DisplayResult {
92                    content: "Search completed (results shown via citations)".to_string(),
93                    content_type: ResultContentType::PlainText,
94                    is_truncated: false,
95                    full_length: 0,
96                }
97            }),
98        }
99    }
100
101    fn compact_summary(&self, input: &HashMap<String, serde_json::Value>, _result: &str) -> String {
102        let query = input
103            .get("query")
104            .and_then(|v| v.as_str())
105            .unwrap_or("unknown");
106        format!("[WebSearch: {}]", query)
107    }
108}