tiy_core/transform/
tool_calls.rs1pub fn normalize_tool_call_id(id: &str, target_provider: &crate::types::Provider) -> String {
10 match target_provider {
11 crate::types::Provider::Anthropic => {
12 let id = if id.contains('|') {
15 id.split('|').next().unwrap_or(id)
16 } else {
17 id
18 };
19
20 let sanitized: String = id
22 .chars()
23 .map(|c| {
24 if c.is_ascii_alphanumeric() || c == '_' || c == '-' {
25 c
26 } else {
27 '_'
28 }
29 })
30 .take(64)
31 .collect();
32
33 sanitized
34 }
35 crate::types::Provider::OpenAI | crate::types::Provider::Groq => {
36 if id.len() > 40 {
38 id[..40].to_string()
39 } else {
40 id.to_string()
41 }
42 }
43 _ => id.to_string(),
44 }
45}
46
47pub struct ToolCallIdMapper {
49 to_normalized: std::collections::HashMap<String, String>,
51 from_normalized: std::collections::HashMap<String, String>,
53 target_provider: crate::types::Provider,
55}
56
57impl ToolCallIdMapper {
58 pub fn new(target_provider: crate::types::Provider) -> Self {
60 Self {
61 to_normalized: std::collections::HashMap::new(),
62 from_normalized: std::collections::HashMap::new(),
63 target_provider,
64 }
65 }
66
67 pub fn normalize(&mut self, id: &str) -> String {
69 if let Some(normalized) = self.to_normalized.get(id) {
70 return normalized.clone();
71 }
72
73 let normalized = normalize_tool_call_id(id, &self.target_provider);
74
75 let mut final_normalized = normalized.clone();
77 let mut counter = 1;
78 while self.from_normalized.contains_key(&final_normalized) {
79 final_normalized = format!("{}_{}", normalized, counter);
80 counter += 1;
81 }
82
83 self.to_normalized
84 .insert(id.to_string(), final_normalized.clone());
85 self.from_normalized
86 .insert(final_normalized.clone(), id.to_string());
87
88 final_normalized
89 }
90
91 pub fn denormalize(&self, normalized: &str) -> Option<&String> {
93 self.from_normalized.get(normalized)
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_normalize_for_anthropic() {
103 let id = "call_abc123+def/ghi=";
104 let normalized = normalize_tool_call_id(id, &crate::types::Provider::Anthropic);
105 assert!(normalized.len() <= 64);
106 assert!(normalized
107 .chars()
108 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-'));
109 }
110
111 #[test]
112 fn test_normalize_pipe_separated() {
113 let id = "call_123|very_long_suffix_here";
114 let normalized = normalize_tool_call_id(id, &crate::types::Provider::Anthropic);
115 assert!(!normalized.contains('|'));
116 assert_eq!(normalized, "call_123");
117 }
118
119 #[test]
120 fn test_normalize_for_openai() {
121 let id = "a".repeat(50);
122 let normalized = normalize_tool_call_id(&id, &crate::types::Provider::OpenAI);
123 assert_eq!(normalized.len(), 40);
124 }
125}