use super::ToolDefinition;
pub(super) fn fuzzy_match_tool_name(model_name: &str, tools: &[ToolDefinition]) -> Option<String> {
if tools.is_empty() || model_name.is_empty() {
return None;
}
let lower = model_name.to_lowercase();
let norm_model = normalize_tool_name(model_name);
if !norm_model.is_empty() {
let exact_norm: Vec<&str> = tools
.iter()
.filter(|t| normalize_tool_name(&t.function.name) == norm_model)
.map(|t| t.function.name.as_str())
.collect();
if exact_norm.len() == 1 {
return Some(exact_norm[0].to_string());
}
}
let matches: Vec<&str> = tools
.iter()
.filter(|t| t.function.name.to_lowercase().contains(&lower))
.map(|t| t.function.name.as_str())
.collect();
if matches.len() == 1 {
return Some(matches[0].to_string());
}
let matches: Vec<&str> = tools
.iter()
.filter(|t| lower.contains(&t.function.name.to_lowercase()))
.map(|t| t.function.name.as_str())
.collect();
if matches.len() == 1 {
return Some(matches[0].to_string());
}
if tools.len() == 1 {
return Some(tools[0].function.name.clone());
}
None
}
fn normalize_tool_name(s: &str) -> String {
let lower = s.trim().to_lowercase();
let mut out = String::with_capacity(lower.len());
let mut prev_sep = false;
for c in lower.chars() {
if c == '_' || c == '-' {
if !prev_sep {
out.push('_');
}
prev_sep = true;
} else {
out.push(c);
prev_sep = false;
}
}
out.trim_matches('_').to_string()
}