Skip to main content

agentik_sdk/tools/
mod.rs

1//! Tool execution and management for the Anthropic SDK.
2//!
3//! This module provides the infrastructure for registering, validating, and executing
4//! tools (functions) that Claude can call during conversations.
5//!
6//! # Examples
7//!
8//! ## Basic Tool Registration and Execution
9//!
10//! ```rust
11//! use agentik_sdk::tools::{ToolRegistry, ToolFunction};
12//! use agentik_sdk::types::{Tool, ToolUse, ToolResult};
13//! use serde_json::{json, Value};
14//! use async_trait::async_trait;
15//!
16//! // Define a tool function
17//! struct WeatherTool;
18//!
19//! #[async_trait]
20//! impl ToolFunction for WeatherTool {
21//!     async fn execute(&self, input: Value) -> Result<ToolResult, Box<dyn std::error::Error + Send + Sync>> {
22//!         let location = input["location"].as_str().unwrap_or("Unknown");
23//!         let weather_data = format!("The weather in {} is 72°F and sunny", location);
24//!         Ok(ToolResult::success("tool_use_id", weather_data))
25//!     }
26//! }
27//!
28//! // Register and use the tool
29//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
30//! let mut registry = ToolRegistry::new();
31//!
32//! let weather_tool_def = Tool::new("get_weather", "Get the current weather in a given location")
33//!     .parameter("location", "string", "The city and state")
34//!     .required("location")
35//!     .build();
36//!
37//! registry.register("get_weather", weather_tool_def, Box::new(WeatherTool))?;
38//!
39//! // Execute a tool call
40//! let tool_use = ToolUse {
41//!     id: "toolu_123".to_string(),
42//!     name: "get_weather".to_string(),
43//!     input: json!({"location": "San Francisco, CA"}),
44//! };
45//!
46//! let result = registry.execute(&tool_use).await?;
47//! # Ok(())
48//! # }
49//! ```
50
51pub mod executor;
52pub mod registry;
53pub mod conversation;
54
55use async_trait::async_trait;
56use serde_json::Value;
57use std::error::Error;
58
59pub use executor::{ToolExecutor, ToolExecutionConfig, ToolExecutionConfigBuilder};
60pub use registry::{ToolRegistry, SharedToolRegistry};
61pub use conversation::{ToolConversation, ConversationConfig, ConversationConfigBuilder};
62
63use crate::types::ToolResult;
64
65/// Trait for implementing tool functions.
66///
67/// This trait allows you to define custom tools that Claude can call.
68/// Each tool receives structured input and returns a structured result.
69#[async_trait]
70pub trait ToolFunction: Send + Sync {
71    /// Execute the tool with the given input.
72    ///
73    /// # Arguments
74    /// * `input` - JSON input parameters from Claude
75    ///
76    /// # Returns
77    /// A `ToolResult` containing the execution result, or an error if execution fails.
78    async fn execute(&self, input: Value) -> Result<ToolResult, Box<dyn Error + Send + Sync>>;
79    
80    /// Validate input before execution (optional).
81    ///
82    /// Override this method to provide custom validation logic beyond
83    /// the JSON schema validation.
84    fn validate_input(&self, _input: &Value) -> Result<(), Box<dyn Error + Send + Sync>> {
85        Ok(())
86    }
87    
88    /// Get the tool's timeout in seconds (optional).
89    ///
90    /// Override to set a custom timeout for this tool.
91    /// Default is 30 seconds.
92    fn timeout_seconds(&self) -> u64 {
93        30
94    }
95}
96
97/// Simple function wrapper for tool functions.
98///
99/// This allows you to register simple async functions as tools without
100/// implementing the full `ToolFunction` trait.
101pub struct SimpleTool<F>
102where
103    F: Fn(Value) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<ToolResult, Box<dyn Error + Send + Sync>>> + Send>> + Send + Sync,
104{
105    function: F,
106}
107
108impl<F> SimpleTool<F>
109where
110    F: Fn(Value) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<ToolResult, Box<dyn Error + Send + Sync>>> + Send>> + Send + Sync,
111{
112    /// Create a new simple tool from a function.
113    pub fn new(function: F) -> Self {
114        Self { function }
115    }
116}
117
118#[async_trait]
119impl<F> ToolFunction for SimpleTool<F>
120where
121    F: Fn(Value) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<ToolResult, Box<dyn Error + Send + Sync>>> + Send>> + Send + Sync,
122{
123    async fn execute(&self, input: Value) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
124        (self.function)(input).await
125    }
126}
127
128/// Tool execution errors.
129#[derive(Debug, thiserror::Error)]
130pub enum ToolError {
131    /// Tool not found in registry.
132    #[error("Tool '{name}' not found")]
133    NotFound { name: String },
134    
135    /// Tool validation failed.
136    #[error("Tool validation failed: {message}")]
137    ValidationFailed { message: String },
138    
139    /// Tool execution failed.
140    #[error("Tool execution failed: {source}")]
141    ExecutionFailed { 
142        #[source]
143        source: Box<dyn Error + Send + Sync> 
144    },
145    
146    /// Tool execution timed out.
147    #[error("Tool execution timed out after {seconds} seconds")]
148    Timeout { seconds: u64 },
149    
150    /// Tool registry error.
151    #[error("Tool registry error: {message}")]
152    RegistryError { message: String },
153}
154
155/// Result type for tool operations.
156pub type ToolOperationResult<T> = Result<T, ToolError>;
157
158/// Helper macro for creating simple tool functions.
159///
160/// # Example
161///
162/// ```rust
163/// use agentik_sdk::tool_function;
164/// use serde_json::{json, Value};
165///
166/// let weather_tool = tool_function!(|input: Value| async move {
167///     let location = input["location"].as_str().unwrap_or("Unknown");
168///     let result = format!("Weather in {}: 72°F and sunny", location);
169///     Ok(agentik_sdk::ToolResult::success("tool_id", result))
170/// });
171/// ```
172#[macro_export]
173macro_rules! tool_function {
174    (|$input:ident: Value| $body:expr) => {
175        $crate::tools::SimpleTool::new(move |$input: Value| {
176            Box::pin($body)
177        })
178    };
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    use crate::types::{Tool, ToolResult};
185    use serde_json::json;
186
187    struct TestTool;
188
189    #[async_trait]
190    impl ToolFunction for TestTool {
191        async fn execute(&self, input: Value) -> Result<ToolResult, Box<dyn Error + Send + Sync>> {
192            let message = input["message"].as_str().unwrap_or("Hello");
193            Ok(ToolResult::success("test_id", format!("Echo: {}", message)))
194        }
195    }
196
197    #[tokio::test]
198    async fn test_tool_function_execution() {
199        let tool = TestTool;
200        let input = json!({"message": "Hello, World!"});
201        
202        let result = tool.execute(input).await.unwrap();
203        
204        if let crate::types::ToolResultContent::Text(content) = result.content {
205            assert_eq!(content, "Echo: Hello, World!");
206        } else {
207            panic!("Expected text content");
208        }
209    }
210
211    #[tokio::test]
212    async fn test_simple_tool() {
213        let tool = SimpleTool::new(|input: Value| {
214            Box::pin(async move {
215                let number = input["number"].as_f64().unwrap_or(0.0);
216                let result = number * 2.0;
217                Ok(ToolResult::success("test_id", format!("Result: {}", result)))
218            })
219        });
220
221        let input = json!({"number": 21.0});
222        let result = tool.execute(input).await.unwrap();
223        
224        if let crate::types::ToolResultContent::Text(content) = result.content {
225            assert_eq!(content, "Result: 42");
226        } else {
227            panic!("Expected text content");
228        }
229    }
230
231    #[test]
232    fn test_tool_function_macro() {
233        let _tool = tool_function!(|input: Value| async move {
234            let value = input["test"].as_str().unwrap_or("default");
235            Ok(ToolResult::success("macro_test", format!("Processed: {}", value)))
236        });
237        
238        // Test compilation of the macro
239        assert!(true);
240    }
241}