1use crate::agent_tool::Tool;
4use crate::tool::ToolDef;
5use indexmap::IndexMap;
6
7struct ProxyTool {
10 def: ToolDef,
11}
12
13impl ProxyTool {
14 fn from_def(def: ToolDef) -> Self {
15 Self { def }
16 }
17}
18
19#[async_trait::async_trait]
20impl Tool for ProxyTool {
21 fn name(&self) -> &str {
22 &self.def.name
23 }
24 fn description(&self) -> &str {
25 &self.def.description
26 }
27 fn parameters_schema(&self) -> serde_json::Value {
28 self.def.parameters.clone()
29 }
30 async fn execute(
31 &self,
32 _: serde_json::Value,
33 _: &mut crate::context::AgentContext,
34 ) -> Result<crate::agent_tool::ToolOutput, crate::agent_tool::ToolError> {
35 Err(crate::agent_tool::ToolError::Execution(
36 "ProxyTool cannot execute — use the original registry".into(),
37 ))
38 }
39}
40
41#[derive(Debug, Clone)]
43pub enum ResolveError {
44 Deferred(String),
46 NotFound(String),
48}
49
50impl std::fmt::Display for ResolveError {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 ResolveError::Deferred(name) => write!(
54 f,
55 "Tool '{}' is deferred. Call tool_search to load its schema first.",
56 name
57 ),
58 ResolveError::NotFound(name) => write!(f, "Tool '{}' not found.", name),
59 }
60 }
61}
62
63impl std::error::Error for ResolveError {}
64
65pub struct ToolRegistry {
71 tools: IndexMap<String, Box<dyn Tool>>,
72 deferred: IndexMap<String, Box<dyn Tool>>,
75}
76
77impl ToolRegistry {
78 pub fn new() -> Self {
79 Self {
80 tools: IndexMap::new(),
81 deferred: IndexMap::new(),
82 }
83 }
84
85 pub fn register(mut self, tool: impl Tool + 'static) -> Self {
87 self.tools.insert(tool.name().to_string(), Box::new(tool));
88 self
89 }
90
91 pub fn add(&mut self, tool: impl Tool + 'static) {
93 self.tools.insert(tool.name().to_string(), Box::new(tool));
94 }
95
96 pub fn register_deferred(mut self, tool: impl Tool + 'static) -> Self {
100 self.deferred
101 .insert(tool.name().to_string(), Box::new(tool));
102 self
103 }
104
105 pub fn add_deferred(&mut self, tool: impl Tool + 'static) {
107 self.deferred
108 .insert(tool.name().to_string(), Box::new(tool));
109 }
110
111 pub fn promote_deferred(&mut self, name: &str) -> bool {
114 let lower = name.to_lowercase();
115 let key = self
116 .deferred
117 .keys()
118 .find(|k| k.to_lowercase() == lower)
119 .cloned();
120 if let Some(key) = key
121 && let Some(tool) = self.deferred.swap_remove(&key)
122 {
123 self.tools.insert(key, tool);
124 return true;
125 }
126 false
127 }
128
129 pub fn deferred_names(&self) -> Vec<&str> {
131 self.deferred.keys().map(|s| s.as_str()).collect()
132 }
133
134 pub fn is_deferred(&self, name: &str) -> bool {
136 let lower = name.to_lowercase();
137 self.deferred.keys().any(|k| k.to_lowercase() == lower)
138 }
139
140 pub fn get(&self, name: &str) -> Option<&dyn Tool> {
142 let lower = name.to_lowercase();
143 self.tools
144 .iter()
145 .find(|(k, _)| k.to_lowercase() == lower)
146 .map(|(_, v)| v.as_ref())
147 }
148
149 pub fn list(&self) -> Vec<&dyn Tool> {
151 self.tools.values().map(|t| t.as_ref()).collect()
152 }
153
154 pub fn system_tools(&self) -> Vec<&dyn Tool> {
156 self.tools
157 .values()
158 .filter(|t| t.is_system())
159 .map(|t| t.as_ref())
160 .collect()
161 }
162
163 pub fn to_defs(&self) -> Vec<ToolDef> {
166 let mut defs: Vec<ToolDef> = self.tools.values().map(|t| t.to_def()).collect();
167 for tool in self.deferred.values() {
169 defs.push(ToolDef {
170 name: tool.name().to_string(),
171 description: tool.description().to_string(),
172 parameters: serde_json::json!({"type": "object", "properties": {}}),
173 });
174 }
175 defs
176 }
177
178 pub fn resolve(&self, name: &str) -> Result<&dyn Tool, ResolveError> {
181 if let Some(t) = self.get(name) {
183 return Ok(t);
184 }
185 if self.is_deferred(name) {
187 return Err(ResolveError::Deferred(name.to_string()));
188 }
189 let lower = name.to_lowercase();
191 let mut best: Option<(&str, f64)> = None;
192 for key in self.tools.keys() {
193 let score = strsim::normalized_levenshtein(&lower, &key.to_lowercase());
194 if score > 0.6 && (best.is_none() || score > best.unwrap().1) {
195 best = Some((key.as_str(), score));
196 }
197 }
198 match best.and_then(|(k, _)| self.tools.get(k).map(|t| t.as_ref())) {
199 Some(t) => Ok(t),
200 None => Err(ResolveError::NotFound(name.to_string())),
201 }
202 }
203
204 pub fn len(&self) -> usize {
206 self.tools.len()
207 }
208
209 pub fn is_empty(&self) -> bool {
210 self.tools.is_empty()
211 }
212
213 pub fn filter(&self, names: &[String]) -> ToolRegistry {
216 self.clone_filtered(names)
222 }
223
224 fn clone_filtered(&self, names: &[String]) -> ToolRegistry {
226 let mut new_tools = IndexMap::new();
227 for name in names {
228 let lower = name.to_lowercase();
229 for (k, v) in &self.tools {
230 if k.to_lowercase() == lower {
231 new_tools.insert(k.clone(), ProxyTool::from_def(v.to_def()));
235 }
236 }
237 }
238 let mut reg = ToolRegistry::new();
239 for (_, tool) in new_tools {
240 reg.tools.insert(tool.def.name.clone(), Box::new(tool));
241 }
242 reg
243 }
244}
245
246impl Default for ToolRegistry {
247 fn default() -> Self {
248 Self::new()
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use crate::agent_tool::{ToolError, ToolOutput};
256 use crate::context::AgentContext;
257 use serde_json::Value;
258
259 struct MockTool {
260 tool_name: String,
261 desc: String,
262 system: bool,
263 }
264
265 impl MockTool {
266 fn new(name: &str, desc: &str) -> Self {
267 Self {
268 tool_name: name.into(),
269 desc: desc.into(),
270 system: false,
271 }
272 }
273 fn system(name: &str, desc: &str) -> Self {
274 Self {
275 tool_name: name.into(),
276 desc: desc.into(),
277 system: true,
278 }
279 }
280 }
281
282 #[async_trait::async_trait]
283 impl Tool for MockTool {
284 fn name(&self) -> &str {
285 &self.tool_name
286 }
287 fn description(&self) -> &str {
288 &self.desc
289 }
290 fn is_system(&self) -> bool {
291 self.system
292 }
293 fn parameters_schema(&self) -> Value {
294 serde_json::json!({"type": "object"})
295 }
296 async fn execute(&self, _: Value, _: &mut AgentContext) -> Result<ToolOutput, ToolError> {
297 Ok(ToolOutput::text("ok"))
298 }
299 }
300
301 #[test]
302 fn registry_builder() {
303 let reg = ToolRegistry::new()
304 .register(MockTool::new("read_file", "Read a file"))
305 .register(MockTool::new("write_file", "Write a file"));
306 assert_eq!(reg.len(), 2);
307 }
308
309 #[test]
310 fn registry_get_case_insensitive() {
311 let reg = ToolRegistry::new().register(MockTool::new("ReadFile", "Read"));
312 assert!(reg.get("readfile").is_some());
313 assert!(reg.get("READFILE").is_some());
314 assert!(reg.get("ReadFile").is_some());
315 }
316
317 #[test]
318 fn registry_list_preserves_order() {
319 let reg = ToolRegistry::new()
320 .register(MockTool::new("alpha", "a"))
321 .register(MockTool::new("beta", "b"))
322 .register(MockTool::new("gamma", "c"));
323 let names: Vec<_> = reg.list().iter().map(|t| t.name()).collect();
324 assert_eq!(names, vec!["alpha", "beta", "gamma"]);
325 }
326
327 #[test]
328 fn registry_system_tools() {
329 let reg = ToolRegistry::new()
330 .register(MockTool::new("read_file", "Read"))
331 .register(MockTool::system("finish", "Finish task"));
332 let sys = reg.system_tools();
333 assert_eq!(sys.len(), 1);
334 assert_eq!(sys[0].name(), "finish");
335 }
336
337 #[test]
338 fn registry_to_defs() {
339 let reg = ToolRegistry::new().register(MockTool::new("bash", "Run command"));
340 let defs = reg.to_defs();
341 assert_eq!(defs.len(), 1);
342 assert_eq!(defs[0].name, "bash");
343 }
344
345 #[test]
346 fn registry_fuzzy_resolve() {
347 let reg = ToolRegistry::new()
348 .register(MockTool::new("read_file", "Read"))
349 .register(MockTool::new("write_file", "Write"));
350 assert_eq!(reg.resolve("read_file").unwrap().name(), "read_file");
352 assert_eq!(reg.resolve("reed_file").unwrap().name(), "read_file");
354 assert!(reg.resolve("xyz").is_err());
356 }
357
358 #[test]
359 fn registry_add_mutable() {
360 let mut reg = ToolRegistry::new();
361 reg.add(MockTool::new("tool_a", "A"));
362 reg.add(MockTool::new("tool_b", "B"));
363 assert_eq!(reg.len(), 2);
364 }
365
366 #[test]
367 fn register_deferred_adds_to_deferred_map() {
368 let reg = ToolRegistry::new()
369 .register(MockTool::new("active", "Active tool"))
370 .register_deferred(MockTool::new("lazy", "Lazy tool"));
371 assert_eq!(reg.len(), 1); assert!(reg.is_deferred("lazy"));
373 assert!(!reg.is_deferred("active"));
374 assert_eq!(reg.deferred_names(), vec!["lazy"]);
375 }
376
377 #[test]
378 fn to_defs_returns_deferred_with_empty_params() {
379 let reg = ToolRegistry::new()
380 .register(MockTool::new("active", "Active tool"))
381 .register_deferred(MockTool::new("lazy", "Lazy tool"));
382 let defs = reg.to_defs();
383 assert_eq!(defs.len(), 2);
384 let active_def = defs.iter().find(|d| d.name == "active").unwrap();
386 assert!(active_def.parameters["type"] == "object");
387 let lazy_def = defs.iter().find(|d| d.name == "lazy").unwrap();
389 assert_eq!(lazy_def.description, "Lazy tool");
390 assert_eq!(lazy_def.parameters["properties"], serde_json::json!({}));
391 }
392
393 #[test]
394 fn promote_deferred_moves_to_active() {
395 let mut reg = ToolRegistry::new().register_deferred(MockTool::new("lazy", "Lazy tool"));
396 assert_eq!(reg.len(), 0);
397 assert!(reg.is_deferred("lazy"));
398
399 let promoted = reg.promote_deferred("lazy");
400 assert!(promoted);
401 assert_eq!(reg.len(), 1);
402 assert!(!reg.is_deferred("lazy"));
403 assert!(reg.get("lazy").is_some());
404 }
405
406 #[test]
407 fn promote_deferred_not_found() {
408 let mut reg = ToolRegistry::new();
409 assert!(!reg.promote_deferred("ghost"));
410 }
411
412 #[test]
413 fn resolve_deferred_returns_error() {
414 let reg = ToolRegistry::new()
415 .register(MockTool::new("active", "Active"))
416 .register_deferred(MockTool::new("lazy", "Lazy"));
417 assert!(reg.resolve("active").is_ok());
419 let result = reg.resolve("lazy");
421 assert!(result.is_err());
422 let err = match result {
423 Err(e) => e,
424 Ok(_) => panic!("expected Deferred error"),
425 };
426 assert!(matches!(err, ResolveError::Deferred(_)));
427 assert!(err.to_string().contains("tool_search"));
428 }
429
430 #[test]
431 fn resolve_deferred_after_promote() {
432 let mut reg = ToolRegistry::new().register_deferred(MockTool::new("lazy", "Lazy"));
433 assert!(reg.resolve("lazy").is_err());
434 reg.promote_deferred("lazy");
435 assert!(reg.resolve("lazy").is_ok());
436 }
437}