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