codetether_agent/tool/
invalid.rs1use super::{Tool, ToolResult};
4use anyhow::Result;
5use async_trait::async_trait;
6use serde_json::{Value, json};
7
8pub 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 let similar: Vec<&String> = self
82 .available_tools
83 .iter()
84 .filter(|t| {
85 t.contains(requested)
86 || requested.contains(t.as_str())
87 || Self::levenshtein(t, requested) <= 3
88 })
89 .take(3)
90 .collect();
91
92 if similar.is_empty() {
93 format!("Available tools: {}", self.available_tools.join(", "))
94 } else {
95 format!(
96 "Did you mean: {}?\n\nAll available tools: {}",
97 similar
98 .iter()
99 .map(|s| s.as_str())
100 .collect::<Vec<_>>()
101 .join(", "),
102 self.available_tools.join(", ")
103 )
104 }
105 } else {
106 "No tool list provided. Check available tools.".to_string()
107 };
108
109 let message = format!(
110 "Tool '{}' not found.\n\n\
111 Arguments received: {}\n\n\
112 {}",
113 requested, passed_args, suggestions
114 );
115
116 Ok(ToolResult::error(message))
117 }
118}
119
120impl InvalidTool {
121 fn levenshtein(a: &str, b: &str) -> usize {
123 let a: Vec<char> = a.chars().collect();
124 let b: Vec<char> = b.chars().collect();
125 let m = a.len();
126 let n = b.len();
127
128 if m == 0 {
129 return n;
130 }
131 if n == 0 {
132 return m;
133 }
134
135 let mut prev: Vec<usize> = (0..=n).collect();
136 let mut curr = vec![0; n + 1];
137
138 for i in 1..=m {
139 curr[0] = i;
140 for j in 1..=n {
141 let cost = if a[i - 1] == b[j - 1] { 0 } else { 1 };
142 curr[j] = (prev[j] + 1).min(curr[j - 1] + 1).min(prev[j - 1] + cost);
143 }
144 std::mem::swap(&mut prev, &mut curr);
145 }
146
147 prev[n]
148 }
149}