Skip to main content

codetether_agent/tool/
invalid.rs

1//! Invalid tool: Handle invalid or unknown tool calls gracefully
2
3use super::{Tool, ToolResult};
4use anyhow::Result;
5use async_trait::async_trait;
6use serde_json::{json, Value};
7
8/// Invalid Tool - A fallback tool that is returned when a model tries to call
9/// a tool that doesn't exist. This provides helpful error messages and suggestions.
10pub struct InvalidTool {
11    requested_tool: String,
12    available_tools: Vec<String>,
13}
14
15impl Default for InvalidTool {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl InvalidTool {
22    pub fn new() -> Self {
23        Self {
24            requested_tool: String::new(),
25            available_tools: Vec::new(),
26        }
27    }
28
29    pub fn with_context(requested_tool: String, available_tools: Vec<String>) -> Self {
30        Self {
31            requested_tool,
32            available_tools,
33        }
34    }
35}
36
37#[async_trait]
38impl Tool for InvalidTool {
39    fn id(&self) -> &str {
40        "invalid"
41    }
42
43    fn name(&self) -> &str {
44        "Invalid Tool Handler"
45    }
46
47    fn description(&self) -> &str {
48        "Handles invalid or unknown tool calls. Returns helpful error messages with suggestions for valid tools."
49    }
50
51    fn parameters(&self) -> Value {
52        json!({
53            "type": "object",
54            "properties": {
55                "requested_tool": {
56                    "type": "string",
57                    "description": "The tool that was requested but not found"
58                },
59                "args": {
60                    "type": "object",
61                    "description": "The arguments that were passed"
62                }
63            },
64            "required": ["requested_tool"]
65        })
66    }
67
68    async fn execute(&self, args: Value) -> Result<ToolResult> {
69        let requested = args["requested_tool"]
70            .as_str()
71            .unwrap_or(&self.requested_tool);
72        
73        let passed_args = if let Some(obj) = args.get("args") {
74            serde_json::to_string_pretty(obj).unwrap_or_else(|_| "{}".to_string())
75        } else {
76            "{}".to_string()
77        };
78
79        let suggestions = if !self.available_tools.is_empty() {
80            // Find similar tool names
81            let similar: Vec<&String> = self.available_tools
82                .iter()
83                .filter(|t| {
84                    t.contains(requested) || 
85                    requested.contains(t.as_str()) ||
86                    Self::levenshtein(t, requested) <= 3
87                })
88                .take(3)
89                .collect();
90            
91            if similar.is_empty() {
92                format!("Available tools: {}", self.available_tools.join(", "))
93            } else {
94                format!("Did you mean: {}?\n\nAll available tools: {}", 
95                    similar.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(", "),
96                    self.available_tools.join(", ")
97                )
98            }
99        } else {
100            "No tool list provided. Check available tools.".to_string()
101        };
102
103        let message = format!(
104            "Tool '{}' not found.\n\n\
105            Arguments received: {}\n\n\
106            {}",
107            requested, passed_args, suggestions
108        );
109
110        Ok(ToolResult::error(message))
111    }
112}
113
114impl InvalidTool {
115    /// Simple Levenshtein distance for finding similar tool names
116    fn levenshtein(a: &str, b: &str) -> usize {
117        let a: Vec<char> = a.chars().collect();
118        let b: Vec<char> = b.chars().collect();
119        let m = a.len();
120        let n = b.len();
121        
122        if m == 0 { return n; }
123        if n == 0 { return m; }
124        
125        let mut prev: Vec<usize> = (0..=n).collect();
126        let mut curr = vec![0; n + 1];
127        
128        for i in 1..=m {
129            curr[0] = i;
130            for j in 1..=n {
131                let cost = if a[i-1] == b[j-1] { 0 } else { 1 };
132                curr[j] = (prev[j] + 1)
133                    .min(curr[j-1] + 1)
134                    .min(prev[j-1] + cost);
135            }
136            std::mem::swap(&mut prev, &mut curr);
137        }
138        
139        prev[n]
140    }
141}