1use crate::errors::ToolError;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::future::Future;
7use std::pin::Pin;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct ToolParameter {
12 #[serde(rename = "type")]
14 pub param_type: String,
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub description: Option<String>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
22pub struct ToolParameters {
23 pub properties: HashMap<String, ToolParameter>,
25 pub required: Vec<String>,
27}
28
29pub trait Tool: Send + Sync {
31 fn name(&self) -> &str;
33
34 fn description(&self) -> &str;
36
37 fn parameters(&self) -> ToolParameters;
39
40 fn call(&self, args: &str) -> impl Future<Output = Result<String, ToolError>> + Send;
43}
44
45pub trait DynTool: Send + Sync {
47 fn name(&self) -> &str;
49
50 fn description(&self) -> &str;
52
53 fn parameters(&self) -> ToolParameters;
55
56 fn call_dyn<'a>(
58 &'a self,
59 args: &'a str,
60 ) -> Pin<Box<dyn Future<Output = Result<String, ToolError>> + Send + 'a>>;
61}
62
63impl<T: Tool> DynTool for T {
64 fn name(&self) -> &str {
65 Tool::name(self)
66 }
67
68 fn description(&self) -> &str {
69 Tool::description(self)
70 }
71
72 fn parameters(&self) -> ToolParameters {
73 Tool::parameters(self)
74 }
75
76 fn call_dyn<'a>(
77 &'a self,
78 args: &'a str,
79 ) -> Pin<Box<dyn Future<Output = Result<String, ToolError>> + Send + 'a>> {
80 Box::pin(Tool::call(self, args))
81 }
82}
83
84pub struct ToolRegistry {
86 tools: HashMap<String, Box<dyn DynTool>>,
87}
88
89impl Default for ToolRegistry {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl ToolRegistry {
96 #[must_use]
98 pub fn new() -> Self {
99 Self {
100 tools: HashMap::new(),
101 }
102 }
103
104 pub fn register<T: Tool + 'static>(&mut self, tool: T) {
106 self.tools.insert(tool.name().to_string(), Box::new(tool));
107 }
108
109 #[must_use]
111 pub fn get(&self, name: &str) -> Option<&dyn DynTool> {
112 self.tools.get(name).map(std::convert::AsRef::as_ref)
113 }
114
115 #[must_use]
117 pub fn list(&self) -> Vec<&dyn DynTool> {
118 self.tools
119 .values()
120 .map(std::convert::AsRef::as_ref)
121 .collect()
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct ToolCallInfo {
128 pub id: String,
130 pub name: String,
132 pub arguments: String,
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138pub struct ToolCall {
139 pub function: ToolCallInfo,
141 #[serde(rename = "type")]
143 pub tool_type: String,
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
148pub struct ToolResult {
149 pub tool_call_id: String,
151 pub content: String,
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::errors::ToolError;
159
160 struct EchoTool;
161
162 impl Tool for EchoTool {
163 fn name(&self) -> &'static str {
164 "echo"
165 }
166
167 fn description(&self) -> &'static str {
168 "Echoes the input."
169 }
170
171 fn parameters(&self) -> ToolParameters {
172 let mut properties = HashMap::new();
173 properties.insert(
174 "input".to_string(),
175 ToolParameter {
176 param_type: "string".to_string(),
177 description: Some("The text to echo".to_string()),
178 },
179 );
180 ToolParameters {
181 properties,
182 required: vec!["input".to_string()],
183 }
184 }
185
186 fn call(&self, args: &str) -> impl Future<Output = Result<String, ToolError>> + Send {
187 let args_owned = args.to_string();
188 async move { Ok(args_owned) }
189 }
190 }
191
192 #[test]
193 fn test_tool_registry() {
194 let mut registry = ToolRegistry::new();
195 registry.register(EchoTool);
196
197 assert!(registry.get("echo").is_some());
198 assert!(registry.get("missing").is_none());
199 assert_eq!(registry.list().len(), 1);
200 }
201
202 #[tokio::test]
203 async fn test_tool_execution() {
204 let tool = EchoTool;
205 let dyn_tool: &dyn DynTool = &tool;
206
207 let result = dyn_tool.call_dyn("hello world").await.unwrap();
208 assert_eq!(result, "hello world");
209 }
210}