1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Arc;
9use tracing::{debug, info};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ToolMetadata {
17 pub id: String,
19 pub name: String,
21 pub description: String,
23 pub input_schema: serde_json::Value,
25 pub output_schema: serde_json::Value,
27 pub available: bool,
29}
30
31#[async_trait::async_trait]
36pub trait ToolInvoker: Send + Sync {
37 async fn invoke(&self, input: serde_json::Value) -> Result<serde_json::Value, String>;
47
48 fn metadata(&self) -> ToolMetadata;
50}
51
52pub struct ToolRegistry {
70 tools: HashMap<String, Arc<dyn ToolInvoker>>,
71 metadata: HashMap<String, ToolMetadata>,
72}
73
74impl ToolRegistry {
75 pub fn new() -> Self {
81 Self {
82 tools: HashMap::new(),
83 metadata: HashMap::new(),
84 }
85 }
86
87 pub fn register(&mut self, tool: Arc<dyn ToolInvoker>) {
95 let metadata = tool.metadata();
96 let tool_id = metadata.id.clone();
97 let tool_name = metadata.name.clone();
98
99 debug!(tool_id = %tool_id, tool_name = %tool_name, "Registering tool");
100
101 self.tools.insert(tool_id.clone(), tool);
102 self.metadata.insert(tool_id.clone(), metadata);
103
104 info!(tool_id = %tool_id, tool_name = %tool_name, "Tool registered successfully");
105 }
106
107 pub fn find_tool(&self, tool_id: &str) -> Option<Arc<dyn ToolInvoker>> {
117 self.tools.get(tool_id).cloned()
118 }
119
120 pub fn all_tools(&self) -> Vec<Arc<dyn ToolInvoker>> {
126 self.tools.values().cloned().collect()
127 }
128
129 pub fn tool_count(&self) -> usize {
135 self.tools.len()
136 }
137
138 pub fn get_tool_metadata(&self, tool_id: &str) -> Option<ToolMetadata> {
148 self.metadata.get(tool_id).cloned()
149 }
150
151 pub fn all_tool_metadata(&self) -> Vec<ToolMetadata> {
157 self.metadata.values().cloned().collect()
158 }
159
160 pub fn has_tool(&self, tool_id: &str) -> bool {
170 self.tools.contains_key(tool_id)
171 }
172
173 pub async fn invoke_tool(
184 &self,
185 tool_id: &str,
186 input: serde_json::Value,
187 ) -> Result<serde_json::Value, String> {
188 let tool = self
189 .find_tool(tool_id)
190 .ok_or_else(|| format!("Tool not found: {}", tool_id))?;
191
192 debug!(tool_id = %tool_id, "Invoking tool");
193 let result = tool.invoke(input).await?;
194 debug!(tool_id = %tool_id, "Tool invocation completed");
195
196 Ok(result)
197 }
198
199 pub fn discover_builtin_tools(&mut self) -> Result<(), String> {
204 info!("Discovering built-in tools");
205
206 debug!("Built-in tool discovery completed");
209 Ok(())
210 }
211
212 pub fn load_configuration(
218 &mut self,
219 config: HashMap<String, serde_json::Value>,
220 ) -> Result<(), String> {
221 info!(config_count = config.len(), "Loading tool configuration");
222 debug!("Tool configuration loaded successfully");
225 Ok(())
226 }
227
228 pub fn available_tool_ids(&self) -> Vec<String> {
234 let mut ids: Vec<String> = self.tools.keys().cloned().collect();
235 ids.sort();
236 ids
237 }
238}
239
240impl Default for ToolRegistry {
241 fn default() -> Self {
242 Self::new()
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 struct TestTool {
251 id: String,
252 name: String,
253 description: String,
254 }
255
256 #[async_trait::async_trait]
257 impl ToolInvoker for TestTool {
258 async fn invoke(&self, input: serde_json::Value) -> Result<serde_json::Value, String> {
259 Ok(serde_json::json!({
260 "success": true,
261 "input": input
262 }))
263 }
264
265 fn metadata(&self) -> ToolMetadata {
266 ToolMetadata {
267 id: self.id.clone(),
268 name: self.name.clone(),
269 description: self.description.clone(),
270 input_schema: serde_json::json!({}),
271 output_schema: serde_json::json!({}),
272 available: true,
273 }
274 }
275 }
276
277 #[test]
278 fn test_register_tool() {
279 let mut registry = ToolRegistry::new();
280 let tool = Arc::new(TestTool {
281 id: "test-tool".to_string(),
282 name: "Test Tool".to_string(),
283 description: "A test tool".to_string(),
284 });
285
286 registry.register(tool);
287 assert_eq!(registry.tool_count(), 1);
288 }
289
290 #[test]
291 fn test_find_tool_by_id() {
292 let mut registry = ToolRegistry::new();
293 let tool = Arc::new(TestTool {
294 id: "test-tool".to_string(),
295 name: "Test Tool".to_string(),
296 description: "A test tool".to_string(),
297 });
298
299 registry.register(tool);
300 let found = registry.find_tool("test-tool");
301 assert!(found.is_some());
302 }
303
304 #[test]
305 fn test_find_tool_not_found() {
306 let registry = ToolRegistry::new();
307 let found = registry.find_tool("nonexistent");
308 assert!(found.is_none());
309 }
310
311 #[test]
312 fn test_get_tool_metadata() {
313 let mut registry = ToolRegistry::new();
314 let tool = Arc::new(TestTool {
315 id: "test-tool".to_string(),
316 name: "Test Tool".to_string(),
317 description: "A test tool for metadata".to_string(),
318 });
319
320 registry.register(tool);
321 let metadata = registry.get_tool_metadata("test-tool");
322 assert!(metadata.is_some());
323
324 let meta = metadata.unwrap();
325 assert_eq!(meta.id, "test-tool");
326 assert_eq!(meta.name, "Test Tool");
327 assert_eq!(meta.description, "A test tool for metadata");
328 }
329
330 #[test]
331 fn test_all_tool_metadata() {
332 let mut registry = ToolRegistry::new();
333 let tool1 = Arc::new(TestTool {
334 id: "tool-1".to_string(),
335 name: "Tool 1".to_string(),
336 description: "First tool".to_string(),
337 });
338 let tool2 = Arc::new(TestTool {
339 id: "tool-2".to_string(),
340 name: "Tool 2".to_string(),
341 description: "Second tool".to_string(),
342 });
343
344 registry.register(tool1);
345 registry.register(tool2);
346
347 let all_metadata = registry.all_tool_metadata();
348 assert_eq!(all_metadata.len(), 2);
349 }
350
351 #[test]
352 fn test_has_tool() {
353 let mut registry = ToolRegistry::new();
354 let tool = Arc::new(TestTool {
355 id: "test-tool".to_string(),
356 name: "Test Tool".to_string(),
357 description: "A test tool".to_string(),
358 });
359
360 registry.register(tool);
361 assert!(registry.has_tool("test-tool"));
362 assert!(!registry.has_tool("nonexistent"));
363 }
364
365 #[tokio::test]
366 async fn test_invoke_tool() {
367 let mut registry = ToolRegistry::new();
368 let tool = Arc::new(TestTool {
369 id: "test-tool".to_string(),
370 name: "Test Tool".to_string(),
371 description: "A test tool".to_string(),
372 });
373
374 registry.register(tool);
375
376 let input = serde_json::json!({"test": "data"});
377 let result = registry.invoke_tool("test-tool", input).await;
378 assert!(result.is_ok());
379
380 let output = result.unwrap();
381 assert_eq!(output["success"], true);
382 }
383
384 #[tokio::test]
385 async fn test_invoke_tool_not_found() {
386 let registry = ToolRegistry::new();
387 let input = serde_json::json!({"test": "data"});
388 let result = registry.invoke_tool("nonexistent", input).await;
389 assert!(result.is_err());
390 }
391
392 #[test]
393 fn test_available_tool_ids() {
394 let mut registry = ToolRegistry::new();
395 let tool1 = Arc::new(TestTool {
396 id: "tool-1".to_string(),
397 name: "Tool 1".to_string(),
398 description: "First tool".to_string(),
399 });
400 let tool2 = Arc::new(TestTool {
401 id: "tool-2".to_string(),
402 name: "Tool 2".to_string(),
403 description: "Second tool".to_string(),
404 });
405
406 registry.register(tool1);
407 registry.register(tool2);
408
409 let ids = registry.available_tool_ids();
410 assert_eq!(ids.len(), 2);
411 assert!(ids.contains(&"tool-1".to_string()));
412 assert!(ids.contains(&"tool-2".to_string()));
413 }
414
415 #[test]
416 fn test_discover_builtin_tools() {
417 let mut registry = ToolRegistry::new();
418 let result = registry.discover_builtin_tools();
419 assert!(result.is_ok());
420 }
421
422 #[test]
423 fn test_load_configuration() {
424 let mut registry = ToolRegistry::new();
425 let mut config = HashMap::new();
426 config.insert("tool-1".to_string(), serde_json::json!({"enabled": true}));
427
428 let result = registry.load_configuration(config);
429 assert!(result.is_ok());
430 }
431
432 #[test]
433 fn test_registry_empty_discovery() {
434 let registry = ToolRegistry::new();
435 assert_eq!(registry.tool_count(), 0);
436 assert!(registry.all_tool_metadata().is_empty());
437 assert!(registry.available_tool_ids().is_empty());
438 }
439
440 #[test]
441 fn test_registry_with_multiple_tools() {
442 let mut registry = ToolRegistry::new();
443 let tools: Vec<Arc<dyn ToolInvoker>> = (1..=5)
444 .map(|i| {
445 Arc::new(TestTool {
446 id: format!("tool-{}", i),
447 name: format!("Tool {}", i),
448 description: format!("Test tool {}", i),
449 }) as Arc<dyn ToolInvoker>
450 })
451 .collect();
452
453 for tool in tools {
454 registry.register(tool);
455 }
456
457 assert_eq!(registry.tool_count(), 5);
458 assert_eq!(registry.all_tool_metadata().len(), 5);
459 }
460}