Skip to main content

potato_type/tools/
mod.rs

1pub mod async_tool;
2pub use async_tool::AsyncTool;
3
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::sync::Arc;
7
8use crate::TypeError;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct AgentToolDefinition {
12    pub name: String,
13    pub description: String,
14    pub parameters: serde_json::Value, // JSON Schema for parameters
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ToolCall {
19    pub tool_name: String,
20    /// Optional call ID — set by OpenAI (tool_calls[].id) and Anthropic (ToolUseBlock.id)
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub call_id: Option<String>,
23    pub arguments: serde_json::Value,
24}
25
26#[derive(Debug)]
27pub struct ToolRegistry {
28    tools: HashMap<String, Box<dyn Tool + Send + Sync>>,
29    async_tools: HashMap<String, Arc<dyn AsyncTool>>,
30}
31
32pub trait Tool: Send + Sync + std::fmt::Debug {
33    fn name(&self) -> &str;
34    fn description(&self) -> &str;
35    fn parameter_schema(&self) -> serde_json::Value;
36    fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, TypeError>;
37}
38
39impl ToolRegistry {
40    pub fn new() -> Self {
41        Self {
42            tools: HashMap::new(),
43            async_tools: HashMap::new(),
44        }
45    }
46
47    pub fn register_tool(&mut self, tool: Box<dyn Tool + Send + Sync>) {
48        self.tools.insert(tool.name().to_string(), tool);
49    }
50
51    pub fn register_async_tool(&mut self, tool: Arc<dyn AsyncTool>) {
52        self.async_tools.insert(tool.name().to_string(), tool);
53    }
54
55    pub fn execute(&self, call: &ToolCall) -> Result<serde_json::Value, TypeError> {
56        self.tools
57            .get(&call.tool_name)
58            .ok_or_else(|| TypeError::Error(format!("Tool {} not found", call.tool_name)))?
59            .execute(call.arguments.clone())
60    }
61
62    pub fn get_definitions(&self) -> Vec<AgentToolDefinition> {
63        self.tools
64            .values()
65            .map(|tool| AgentToolDefinition {
66                name: tool.name().to_string(),
67                description: tool.description().to_string(),
68                parameters: tool.parameter_schema(),
69            })
70            .collect()
71    }
72
73    /// Get definitions for both sync and async tools (used by the agentic loop)
74    pub fn get_all_definitions(&self) -> Vec<AgentToolDefinition> {
75        let mut defs: Vec<AgentToolDefinition> = self
76            .tools
77            .values()
78            .map(|tool| AgentToolDefinition {
79                name: tool.name().to_string(),
80                description: tool.description().to_string(),
81                parameters: tool.parameter_schema(),
82            })
83            .collect();
84
85        for tool in self.async_tools.values() {
86            defs.push(AgentToolDefinition {
87                name: tool.name().to_string(),
88                description: tool.description().to_string(),
89                parameters: tool.parameter_schema(),
90            });
91        }
92
93        defs
94    }
95
96    /// Try to get an async tool clone (Arc) for the given name.
97    /// Returns None if not found as async tool.
98    pub fn get_async_tool(&self, name: &str) -> Option<Arc<dyn AsyncTool>> {
99        self.async_tools.get(name).cloned()
100    }
101
102    /// Check if a sync tool exists by name.
103    pub fn has_sync_tool(&self, name: &str) -> bool {
104        self.tools.contains_key(name)
105    }
106
107    /// Get the number of registered tools (sync + async)
108    pub fn len(&self) -> usize {
109        self.tools.len() + self.async_tools.len()
110    }
111
112    /// Check if the registry is empty
113    pub fn is_empty(&self) -> bool {
114        self.tools.is_empty() && self.async_tools.is_empty()
115    }
116}
117
118impl Default for ToolRegistry {
119    fn default() -> Self {
120        Self::new()
121    }
122}
123
124#[derive(Debug)]
125pub struct ToolCallInfo {
126    pub name: String,
127    pub arguments: serde_json::Value,
128    pub call_id: Option<String>,
129    pub result: Option<serde_json::Value>,
130}