dynamo_parsers/tool_calling/
parsers.rs

1// SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use super::config::{ToolCallConfig, ToolCallParserType};
5use super::harmony::{
6    detect_tool_call_start_harmony, find_tool_call_end_position_harmony,
7    parse_tool_calls_harmony_complete,
8};
9use super::json::{
10    detect_tool_call_start_json, find_tool_call_end_position_json, try_tool_call_parse_json,
11};
12use super::pythonic::{
13    detect_tool_call_start_pythonic, find_tool_call_end_position_pythonic,
14    try_tool_call_parse_pythonic,
15};
16use super::response::ToolCallResponse;
17use std::collections::HashMap;
18use std::sync::OnceLock;
19
20static PARSER_MAP: OnceLock<HashMap<&'static str, ToolCallConfig>> = OnceLock::new();
21
22// Always update this parsermap when adding a new parser
23pub fn get_tool_parser_map() -> &'static HashMap<&'static str, ToolCallConfig> {
24    PARSER_MAP.get_or_init(|| {
25        let mut map = HashMap::new();
26        map.insert("hermes", ToolCallConfig::hermes());
27        map.insert("nemotron_deci", ToolCallConfig::nemotron_deci());
28        map.insert("llama3_json", ToolCallConfig::llama3_json());
29        map.insert("mistral", ToolCallConfig::mistral());
30        map.insert("phi4", ToolCallConfig::phi4());
31        map.insert("pythonic", ToolCallConfig::pythonic());
32        map.insert("harmony", ToolCallConfig::harmony());
33        map.insert("deepseek_v3_1", ToolCallConfig::deepseek_v3_1());
34        map.insert("default", ToolCallConfig::default());
35        map
36    })
37}
38
39pub fn get_available_tool_parsers() -> Vec<&'static str> {
40    get_tool_parser_map().keys().copied().collect()
41}
42
43pub async fn try_tool_call_parse(
44    message: &str,
45    config: &ToolCallConfig,
46) -> anyhow::Result<(Vec<ToolCallResponse>, Option<String>)> {
47    // Use match statement (Rust's switch statement) to call the appropriate parser
48    match config.format {
49        ToolCallParserType::Json => {
50            let (results, normal_content) = try_tool_call_parse_json(message, &config.json)?;
51            Ok((results, normal_content))
52        }
53        ToolCallParserType::Harmony => {
54            let (results, normal_content) =
55                parse_tool_calls_harmony_complete(message, &config.json).await?;
56            Ok((results, normal_content))
57        }
58        ToolCallParserType::Pythonic => {
59            let (results, normal_content) = try_tool_call_parse_pythonic(message)?;
60            Ok((results, normal_content))
61        }
62        ToolCallParserType::Typescript => {
63            anyhow::bail!("Typescript parser not implemented");
64        }
65        ToolCallParserType::Xml => {
66            anyhow::bail!("Xml parser not implemented");
67        }
68    }
69}
70
71// Base Detector to call for all tool parsing
72pub async fn detect_and_parse_tool_call(
73    message: &str,
74    parser_str: Option<&str>,
75) -> anyhow::Result<(Vec<ToolCallResponse>, Option<String>)> {
76    // Get the tool parser map
77    let parser_map = get_tool_parser_map();
78
79    // Handle None or empty string by defaulting to "default"
80    let parser_key = match parser_str {
81        Some(s) if !s.is_empty() => s,
82        _ => "default", // None or empty string
83    };
84
85    match parser_map.get(parser_key) {
86        Some(config) => {
87            let (results, normal_content) = try_tool_call_parse(message, config).await?;
88            Ok((results, normal_content))
89        }
90        None => anyhow::bail!(
91            "Parser '{}' is not implemented. Available parsers: {:?}",
92            parser_key,
93            get_available_tool_parsers()
94        ),
95    }
96}
97
98pub fn detect_tool_call_start(chunk: &str, parser_str: Option<&str>) -> anyhow::Result<bool> {
99    let parser_map = get_tool_parser_map();
100    let parser_key = match parser_str {
101        Some(s) if !s.is_empty() => s,
102        _ => "default", // None or empty string
103    };
104
105    match parser_map.get(parser_key) {
106        Some(config) => match config.format {
107            ToolCallParserType::Json => Ok(detect_tool_call_start_json(chunk, &config.json)),
108            ToolCallParserType::Harmony => {
109                Ok(detect_tool_call_start_harmony(chunk, &config.json, false))
110            }
111            ToolCallParserType::Pythonic => Ok(detect_tool_call_start_pythonic(chunk)),
112            ToolCallParserType::Typescript => {
113                anyhow::bail!("Typescript parser not implemented");
114            }
115            ToolCallParserType::Xml => {
116                anyhow::bail!("Xml parser not implemented");
117            }
118        },
119        None => anyhow::bail!(
120            "Parser '{}' is not implemented. Available parsers: {:?}",
121            parser_key,
122            get_available_tool_parsers()
123        ),
124    }
125}
126
127pub fn find_tool_call_end_position(chunk: &str, parser_str: Option<&str>) -> usize {
128    let parser_map = get_tool_parser_map();
129    let parser_key = match parser_str {
130        Some(s) if !s.is_empty() => s,
131        _ => "default",
132    };
133
134    match parser_map.get(parser_key) {
135        Some(config) => match config.format {
136            ToolCallParserType::Json => {
137                // For "default", use "nemotron_deci" as the effective parser; otherwise, use the provided parser_key
138                let effective_parser = if parser_key == "default" {
139                    "nemotron_deci"
140                } else {
141                    parser_key
142                };
143                find_tool_call_end_position_json(chunk, effective_parser, &config.json)
144            }
145            ToolCallParserType::Harmony => find_tool_call_end_position_harmony(chunk, &config.json),
146            ToolCallParserType::Pythonic => find_tool_call_end_position_pythonic(chunk),
147            ToolCallParserType::Typescript => {
148                // Typescript parser not implemented
149                chunk.len()
150            }
151            ToolCallParserType::Xml => {
152                // Xml parser not implemented
153                chunk.len()
154            }
155        },
156        None => {
157            // Unknown parser, return full content length
158            chunk.len()
159        }
160    }
161}
162// Tests
163// cargo test postprocessor::tool_calling::parsers
164#[cfg(test)]
165mod tests {
166    use super::super::config::JsonParserConfig;
167    use super::*;
168
169    fn extract_name_and_args(call: ToolCallResponse) -> (String, serde_json::Value) {
170        let args: serde_json::Value = serde_json::from_str(&call.function.arguments).unwrap();
171        (call.function.name, args)
172    }
173
174    #[test]
175    fn test_get_available_tool_parsers() {
176        let parsers = get_available_tool_parsers();
177        assert!(!parsers.is_empty());
178        // Update this list when adding a new parser
179        let available_parsers = [
180            "hermes",
181            "llama3_json",
182            "harmony",
183            "nemotron_deci",
184            "mistral",
185            "phi4",
186            "default",
187            "pythonic",
188            "deepseek_v3_1",
189        ];
190        for parser in available_parsers {
191            assert!(parsers.contains(&parser));
192        }
193    }
194
195    #[tokio::test]
196    async fn parses_single_parameters_object() {
197        let input = r#"{ "name": "hello", "parameters": { "x": 1, "y": 2 } }"#;
198        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
199            .await
200            .unwrap();
201        assert_eq!(content, Some("".to_string()));
202        assert!(!result.is_empty());
203        assert_eq!(result.len(), 1);
204        let (name, args) = extract_name_and_args(result[0].clone());
205        assert_eq!(name, "hello");
206        assert_eq!(args["x"], 1);
207        assert_eq!(args["y"], 2);
208    }
209
210    #[tokio::test]
211    async fn parses_single_arguments_object() {
212        let input = r#"{ "name": "world", "arguments": { "a": "abc", "b": 42 } }"#;
213        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
214            .await
215            .unwrap();
216        assert_eq!(content, Some("".to_string()));
217        assert!(!result.is_empty());
218        assert_eq!(result.len(), 1);
219        let (name, args) = extract_name_and_args(result[0].clone());
220        assert_eq!(name, "world");
221        assert_eq!(args["a"], "abc");
222        assert_eq!(args["b"], 42);
223    }
224
225    #[tokio::test]
226    async fn parses_vec_of_parameters() {
227        let input = r#"[{ "name": "first", "parameters": { "a": 1 } }, { "name": "second", "parameters": { "b": 2 } }]"#;
228        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
229            .await
230            .unwrap();
231        assert_eq!(content, Some("".to_string()));
232        assert!(!result.is_empty());
233        assert_eq!(result.len(), 2);
234        let (name, args) = extract_name_and_args(result[0].clone());
235        assert_eq!(name, "first");
236        assert_eq!(args["a"], 1);
237        let (name, args) = extract_name_and_args(result[1].clone());
238        assert_eq!(name, "second");
239        assert_eq!(args["b"], 2);
240    }
241
242    #[tokio::test]
243    async fn parses_vec_of_arguments() {
244        let input = r#"[{ "name": "alpha", "arguments": { "a": "x" } }, { "name": "omega", "arguments": { "z": "y" } }]"#;
245        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
246            .await
247            .unwrap();
248        assert_eq!(content, Some("".to_string()));
249        assert!(!result.is_empty());
250        assert_eq!(result.len(), 2);
251        let (name, args) = extract_name_and_args(result[0].clone());
252        assert_eq!(name, "alpha");
253        assert_eq!(args["a"], "x");
254        let (name, args) = extract_name_and_args(result[1].clone());
255        assert_eq!(name, "omega");
256        assert_eq!(args["z"], "y");
257    }
258
259    #[tokio::test]
260    async fn parses_toolcall_wrapped_payload() {
261        let input =
262            r#"<TOOLCALL>[{ "name": "wrapped", "parameters": { "foo": "bar" } }]</TOOLCALL>"#;
263        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
264            .await
265            .unwrap();
266        assert_eq!(content, Some("".to_string()));
267        assert!(!result.is_empty());
268        assert_eq!(result.len(), 1);
269        let (name, args) = extract_name_and_args(result[0].clone());
270        assert_eq!(name, "wrapped");
271        assert_eq!(args["foo"], "bar");
272    }
273
274    #[tokio::test]
275    async fn parses_python_tag_prefixed_payload() {
276        let input = r#"<|python_tag|>{ "name": "pyfunc", "arguments": { "k": "v" } }"#;
277        let (result, content) = try_tool_call_parse(
278            input,
279            &ToolCallConfig {
280                format: ToolCallParserType::Json,
281                json: JsonParserConfig {
282                    tool_call_start_tokens: vec!["<|python_tag|>".to_string()],
283                    tool_call_end_tokens: vec!["".to_string()],
284                    ..Default::default()
285                },
286            },
287        )
288        .await
289        .unwrap();
290        assert_eq!(content, Some("".to_string()));
291        assert!(!result.is_empty());
292        assert_eq!(result.len(), 1);
293        let (name, args) = extract_name_and_args(result[0].clone());
294        assert_eq!(name, "pyfunc");
295        assert_eq!(args["k"], "v");
296    }
297
298    #[tokio::test]
299    async fn returns_none_on_invalid_input() {
300        let input = r#"not even json"#;
301        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
302            .await
303            .unwrap();
304        assert_eq!(content, Some("not even json".to_string()));
305        assert!(result.is_empty());
306    }
307
308    #[tokio::test]
309    async fn returns_none_on_valid_json_wrong_shape() {
310        let input = r#"{ "foo": "bar" }"#;
311        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
312            .await
313            .unwrap();
314        assert_eq!(content, Some("{ \"foo\": \"bar\" }".to_string()));
315        assert!(result.is_empty());
316    }
317
318    // Tests for real model outputs - disabled by default
319    #[tokio::test]
320    async fn test_nvidia_llama3_nemotron_super_49b_simple() {
321        let input = r#"<think>
322Okay, the user is asking for the weather in San Francisco in Fahrenheit. Let me check the tools available.
323</think>
324
325<TOOLCALL>[{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]</TOOLCALL>"#;
326        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
327            .await
328            .unwrap();
329        assert!(!result.is_empty());
330        assert_eq!(result.len(), 1);
331        assert_eq!(content, Some("<think>\nOkay, the user is asking for the weather in San Francisco in Fahrenheit. Let me check the tools available.\n</think>".to_string()));
332        let (name, args) = extract_name_and_args(result[0].clone());
333        assert_eq!(name, "get_weather");
334        assert_eq!(args["location"], "San Francisco, CA");
335        assert_eq!(args["unit"], "fahrenheit");
336    }
337
338    #[tokio::test]
339    async fn test_nvidia_llama3_nemotron_super_49b_simple_with_no_think() {
340        let input = r#"<TOOLCALL>[{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]</TOOLCALL>"#;
341        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
342            .await
343            .unwrap();
344        assert!(!result.is_empty());
345        assert_eq!(result.len(), 1);
346        assert_eq!(content, Some("".to_string()));
347        let (name, args) = extract_name_and_args(result[0].clone());
348        assert_eq!(name, "get_weather");
349        assert_eq!(args["location"], "San Francisco, CA");
350        assert_eq!(args["unit"], "fahrenheit");
351    }
352
353    #[tokio::test]
354    async fn test_nvidia_llama3_nemotron_super_49b_with_function_array() {
355        let input = r#"<think>
356Okay, the user is asking for the weather in San Francisco in Fahrenheit. Let me check the tools available.
357</think>
358
359<TOOLCALL>[{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}, {"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}]</TOOLCALL>"#;
360        let config = ToolCallConfig::nemotron_deci();
361        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
362        assert_eq!(content, Some("<think>\nOkay, the user is asking for the weather in San Francisco in Fahrenheit. Let me check the tools available.\n</think>".to_string()));
363        assert!(!result.is_empty());
364        assert_eq!(result.len(), 2);
365        let (name, args) = extract_name_and_args(result[0].clone());
366        assert_eq!(name, "get_weather");
367        assert_eq!(args["location"], "San Francisco, CA");
368        assert_eq!(args["unit"], "fahrenheit");
369        let (name, args) = extract_name_and_args(result[1].clone());
370        assert_eq!(name, "get_weather");
371        assert_eq!(args["location"], "New York, NY");
372        assert_eq!(args["unit"], "fahrenheit");
373    }
374
375    #[tokio::test]
376    async fn test_nvidia_llama3_nemotron_super_49b_with_function_array_with_new_lines() {
377        let input = r#"<think>
378Okay, the user is asking for the weather in San Francisco in Fahrenheit. Let me check the tools available.
379</think>
380
381<TOOLCALL>
382[{"name": "get_weather",
383 "arguments": {"location": "San Francisco, CA",
384  "unit": "fahrenheit"}},
385  {"name": "get_weather",
386   "arguments":
387  {"location": "New York, NY",
388  "unit": "fahrenheit"}}]
389  </TOOLCALL>
390  "#;
391        let config = ToolCallConfig::nemotron_deci();
392        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
393        assert_eq!(content, Some("<think>\nOkay, the user is asking for the weather in San Francisco in Fahrenheit. Let me check the tools available.\n</think>".to_string()));
394        assert!(!result.is_empty());
395        assert_eq!(result.len(), 2);
396        let (name, args) = extract_name_and_args(result[0].clone());
397        assert_eq!(name, "get_weather");
398        assert_eq!(args["location"], "San Francisco, CA");
399        assert_eq!(args["unit"], "fahrenheit");
400        let (name, args) = extract_name_and_args(result[1].clone());
401        assert_eq!(name, "get_weather");
402        assert_eq!(args["location"], "New York, NY");
403        assert_eq!(args["unit"], "fahrenheit");
404    }
405
406    #[tokio::test]
407    async fn test_qwen_qwq_32b_simple() {
408        let input = r#"<tool_call>
409{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
410</tool_call>"#;
411        let (result, content) = detect_and_parse_tool_call(input, Some("hermes"))
412            .await
413            .unwrap();
414        assert_eq!(content, Some("".to_string()));
415        assert!(!result.is_empty());
416        assert_eq!(result.len(), 1);
417        let (name, args) = extract_name_and_args(result[0].clone());
418        assert_eq!(name, "get_weather");
419        assert_eq!(args["location"], "San Francisco, CA");
420        assert_eq!(args["unit"], "fahrenheit");
421    }
422
423    #[tokio::test]
424    async fn test_qwen_qwq_32b_simple_with_normal_text() {
425        let input = r#"Hey How are you? <tool_call>
426{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
427</tool_call>"#;
428        let (result, content) = detect_and_parse_tool_call(input, Some("hermes"))
429            .await
430            .unwrap();
431        assert_eq!(content, Some("Hey How are you?".to_string()));
432        assert!(!result.is_empty());
433        assert_eq!(result.len(), 1);
434    }
435
436    #[tokio::test]
437    async fn test_nousresearch_hermes3_llama31_8b_simple() {
438        let input = r#"<tool_call>
439{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
440</tool_call>"#;
441        let (result, content) = detect_and_parse_tool_call(input, Some("hermes"))
442            .await
443            .unwrap();
444        assert_eq!(content, Some("".to_string()));
445        assert!(!result.is_empty());
446        assert_eq!(result.len(), 1);
447        let (name, args) = extract_name_and_args(result[0].clone());
448        assert_eq!(name, "get_weather");
449        assert_eq!(args["location"], "San Francisco, CA");
450        assert_eq!(args["unit"], "fahrenheit");
451    }
452
453    #[tokio::test]
454    async fn test_qwen_qwq_32b_multiple_tool_calls() {
455        let input = r#"<tool_call>
456{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
457</tool_call>
458<tool_call>
459{"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}
460</tool_call>
461"#;
462        let config = ToolCallConfig::hermes();
463        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
464        assert_eq!(content, Some("".to_string()));
465        assert!(!result.is_empty());
466        assert_eq!(result.len(), 2);
467        let (name, args) = extract_name_and_args(result[0].clone());
468        assert_eq!(name, "get_weather");
469        assert_eq!(args["location"], "San Francisco, CA");
470        assert_eq!(args["unit"], "fahrenheit");
471        let (name, args) = extract_name_and_args(result[1].clone());
472        assert_eq!(name, "get_weather");
473        assert_eq!(args["location"], "New York, NY");
474        assert_eq!(args["unit"], "fahrenheit");
475    }
476
477    #[tokio::test]
478    async fn test_qwen_qwq_32b_multiple_tool_calls_with_normal_text() {
479        let input = r#"Hey How are you? <tool_call>
480{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
481</tool_call>
482<tool_call>
483{"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}
484</tool_call>
485"#;
486        let config = ToolCallConfig::hermes();
487        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
488        assert_eq!(content, Some("Hey How are you?".to_string()));
489        assert!(!result.is_empty());
490        assert_eq!(result.len(), 2);
491        let (name, args) = extract_name_and_args(result[0].clone());
492        assert_eq!(name, "get_weather");
493        assert_eq!(args["location"], "San Francisco, CA");
494        assert_eq!(args["unit"], "fahrenheit");
495        let (name, args) = extract_name_and_args(result[1].clone());
496        assert_eq!(name, "get_weather");
497        assert_eq!(args["location"], "New York, NY");
498        assert_eq!(args["unit"], "fahrenheit");
499    }
500
501    #[tokio::test]
502    async fn test_qwen_qwq_32b_multiple_tool_calls_with_new_lines() {
503        let input = r#"<tool_call>
504{"name": "get_weather",
505"arguments": {"location": "San Francisco, CA",
506"unit": "fahrenheit"}}
507</tool_call>
508<tool_call>
509{"name": "get_weather", "arguments":
510{"location": "New York, NY", "unit":
511"fahrenheit"}}
512</tool_call>
513"#;
514        let config = ToolCallConfig::hermes();
515        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
516        assert_eq!(content, Some("".to_string()));
517        assert!(!result.is_empty());
518        assert_eq!(result.len(), 2);
519        let (name, args) = extract_name_and_args(result[0].clone());
520        assert_eq!(name, "get_weather");
521        assert_eq!(args["location"], "San Francisco, CA");
522        assert_eq!(args["unit"], "fahrenheit");
523        let (name, args) = extract_name_and_args(result[1].clone());
524        assert_eq!(name, "get_weather");
525        assert_eq!(args["location"], "New York, NY");
526        assert_eq!(args["unit"], "fahrenheit");
527    }
528
529    #[tokio::test]
530    #[ignore]
531    async fn test_ibm_granite_40_tiny_preview_simple() {
532        let input = r#"[{"arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}, "name": "get_weather"}]"#;
533        let config = ToolCallConfig {
534            format: ToolCallParserType::Json,
535            json: JsonParserConfig {
536                tool_call_start_tokens: vec![],
537                tool_call_end_tokens: vec![],
538                arguments_keys: vec!["arguments".to_string()],
539                ..Default::default()
540            },
541        };
542        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
543        assert_eq!(content, Some("".to_string()));
544        assert!(!result.is_empty());
545        assert_eq!(result.len(), 1);
546        let (name, args) = extract_name_and_args(result[0].clone());
547        assert_eq!(name, "get_weather");
548        assert_eq!(args["location"], "San Francisco, CA");
549        assert_eq!(args["unit"], "fahrenheit");
550    }
551
552    #[tokio::test]
553    async fn test_mistralai_mistral_7b_instruct_v03_simple() {
554        let input = r#" [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]"#;
555        let config = ToolCallConfig::mistral();
556        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
557        assert_eq!(content, Some("".to_string()));
558        assert!(!result.is_empty());
559        assert_eq!(result.len(), 1);
560        let (name, args) = extract_name_and_args(result[0].clone());
561        assert_eq!(name, "get_weather");
562        assert_eq!(args["location"], "San Francisco, CA");
563        assert_eq!(args["unit"], "fahrenheit");
564    }
565
566    #[tokio::test]
567    async fn test_mistralai_mistral_7b_instruct_v03_simple_with_normal_text() {
568        let input = r#"Hey How are you? [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]"#;
569        let config = ToolCallConfig::mistral();
570        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
571        assert_eq!(content, Some("Hey How are you?".to_string()));
572        assert!(!result.is_empty());
573        assert_eq!(result.len(), 1);
574        let (name, args) = extract_name_and_args(result[0].clone());
575        assert_eq!(name, "get_weather");
576        assert_eq!(args["location"], "San Francisco, CA");
577        assert_eq!(args["unit"], "fahrenheit");
578    }
579
580    #[tokio::test]
581    async fn test_mistralai_mistral_7b_instruct_v03_simple_with_new_lines() {
582        let input = r#"
583        [{"name": "get_weather",
584        "arguments": {"location":
585        "San Francisco, CA",
586        "unit": "fahrenheit"}}]
587        "#;
588        let config = ToolCallConfig::mistral();
589        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
590        assert_eq!(content, Some("".to_string()));
591        assert!(!result.is_empty());
592        assert_eq!(result.len(), 1);
593        let (name, args) = extract_name_and_args(result[0].clone());
594        assert_eq!(name, "get_weather");
595        assert_eq!(args["location"], "San Francisco, CA");
596        assert_eq!(args["unit"], "fahrenheit");
597    }
598
599    #[tokio::test]
600    async fn test_mistralai_mistral_7b_instruct_v03_multiple() {
601        let input = r#" [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}, {"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}]"#;
602        let config = ToolCallConfig::mistral();
603        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
604        assert_eq!(content, Some("".to_string()));
605        assert!(!result.is_empty());
606        assert_eq!(result.len(), 2);
607        let (name, args) = extract_name_and_args(result[0].clone());
608        assert_eq!(name, "get_weather");
609        assert_eq!(args["location"], "San Francisco, CA");
610        assert_eq!(args["unit"], "fahrenheit");
611        let (name, args) = extract_name_and_args(result[1].clone());
612        assert_eq!(name, "get_weather");
613        assert_eq!(args["location"], "New York, NY");
614        assert_eq!(args["unit"], "fahrenheit");
615    }
616
617    #[tokio::test]
618    async fn test_mistralai_mistral_7b_instruct_v03_multiple_with_normal_text() {
619        let input = r#"Hey How are you? [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}, {"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}]"#;
620        let config = ToolCallConfig::mistral();
621        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
622        assert_eq!(content, Some("Hey How are you?".to_string()));
623        assert!(!result.is_empty());
624        assert_eq!(result.len(), 2);
625        let (name, args) = extract_name_and_args(result[0].clone());
626        assert_eq!(name, "get_weather");
627        assert_eq!(args["location"], "San Francisco, CA");
628        assert_eq!(args["unit"], "fahrenheit");
629        let (name, args) = extract_name_and_args(result[1].clone());
630        assert_eq!(name, "get_weather");
631        assert_eq!(args["location"], "New York, NY");
632        assert_eq!(args["unit"], "fahrenheit");
633    }
634
635    #[tokio::test]
636    async fn test_mistralai_mistral_7b_instruct_v03_multiple_with_new_lines() {
637        let input = r#"
638        [{"name": "get_weather",
639        "arguments": {"location":
640        "San Francisco, CA",
641        "unit": "fahrenheit"}},
642        {"name": "get_weather", "arguments":
643        {"location": "New York, NY", "unit":
644        "fahrenheit"}}]
645        "#;
646        let config = ToolCallConfig::mistral();
647        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
648        assert_eq!(content, Some("".to_string()));
649        assert!(!result.is_empty());
650        assert_eq!(result.len(), 2);
651        let (name, args) = extract_name_and_args(result[0].clone());
652        assert_eq!(name, "get_weather");
653        assert_eq!(args["location"], "San Francisco, CA");
654        assert_eq!(args["unit"], "fahrenheit");
655        let (name, args) = extract_name_and_args(result[1].clone());
656        assert_eq!(name, "get_weather");
657        assert_eq!(args["location"], "New York, NY");
658        assert_eq!(args["unit"], "fahrenheit");
659    }
660
661    #[tokio::test]
662    async fn test_mistralai_mistral_7b_instruct_v03_single_with_start_token() {
663        let input = r#"[TOOL_CALLS] [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]"#;
664        let config = ToolCallConfig::mistral();
665        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
666        assert_eq!(content, Some("".to_string()));
667        assert!(!result.is_empty());
668        assert_eq!(result.len(), 1);
669        let (name, args) = extract_name_and_args(result[0].clone());
670        assert_eq!(name, "get_weather");
671        assert_eq!(args["location"], "San Francisco, CA");
672        assert_eq!(args["unit"], "fahrenheit");
673    }
674
675    #[tokio::test]
676    async fn test_mistralai_mistral_7b_instruct_v03_single_with_start_token_with_normal_text() {
677        let input = r#"Hey How are you? [TOOL_CALLS] [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]"#;
678        let config = ToolCallConfig::mistral();
679        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
680        assert_eq!(content, Some("Hey How are you?".to_string()));
681        assert!(!result.is_empty());
682        assert_eq!(result.len(), 1);
683        let (name, args) = extract_name_and_args(result[0].clone());
684        assert_eq!(name, "get_weather");
685        assert_eq!(args["location"], "San Francisco, CA");
686        assert_eq!(args["unit"], "fahrenheit");
687    }
688
689    #[tokio::test]
690    async fn test_mistralai_mistral_7b_instruct_v03_single_with_start_tokenwith_new_lines() {
691        let input = r#"
692        [TOOL_CALLS]
693        [{"name": "get_weather",
694        "arguments": {"location":
695        "San Francisco, CA",
696        "unit": "fahrenheit"}}]
697        "#;
698        let config = ToolCallConfig::mistral();
699        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
700        assert_eq!(content, Some("".to_string()));
701        assert!(!result.is_empty());
702        assert_eq!(result.len(), 1);
703        let (name, args) = extract_name_and_args(result[0].clone());
704        assert_eq!(name, "get_weather");
705        assert_eq!(args["location"], "San Francisco, CA");
706        assert_eq!(args["unit"], "fahrenheit");
707    }
708
709    #[tokio::test]
710    async fn test_mistralai_mistral_7b_instruct_v03_single_with_start_token_multiple() {
711        let input = r#"[TOOL_CALLS] [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}, {"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}]"#;
712        let config = ToolCallConfig::mistral();
713        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
714        assert_eq!(content, Some("".to_string()));
715        assert!(!result.is_empty());
716        assert_eq!(result.len(), 2);
717        let (name, args) = extract_name_and_args(result[0].clone());
718        assert_eq!(name, "get_weather");
719        assert_eq!(args["location"], "San Francisco, CA");
720        assert_eq!(args["unit"], "fahrenheit");
721        let (name, args) = extract_name_and_args(result[1].clone());
722        assert_eq!(name, "get_weather");
723        assert_eq!(args["location"], "New York, NY");
724        assert_eq!(args["unit"], "fahrenheit");
725    }
726
727    #[tokio::test]
728    async fn test_mistralai_mistral_7b_instruct_v03_single_with_start_token_multiple_with_normal_text()
729     {
730        let input = r#"Hey How are you? [TOOL_CALLS] [{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}, {"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}]"#;
731        let config = ToolCallConfig::mistral();
732        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
733        assert_eq!(content, Some("Hey How are you?".to_string()));
734        assert!(!result.is_empty());
735        assert_eq!(result.len(), 2);
736        let (name, args) = extract_name_and_args(result[0].clone());
737        assert_eq!(name, "get_weather");
738        assert_eq!(args["location"], "San Francisco, CA");
739        assert_eq!(args["unit"], "fahrenheit");
740        let (name, args) = extract_name_and_args(result[1].clone());
741        assert_eq!(name, "get_weather");
742        assert_eq!(args["location"], "New York, NY");
743        assert_eq!(args["unit"], "fahrenheit");
744    }
745
746    #[tokio::test]
747    async fn test_mistralai_mistral_7b_instruct_v03_single_with_start_token_multiple_with_new_lines()
748     {
749        let input = r#"
750        [TOOL_CALLS]
751        [{"name": "get_weather",
752        "arguments": {"location":
753        "San Francisco, CA",
754        "unit": "fahrenheit"}},
755        {"name": "get_weather", "arguments":
756        {"location": "New York, NY", "unit":
757        "fahrenheit"}}]
758        "#;
759        let config = ToolCallConfig::mistral();
760        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
761        assert_eq!(content, Some("".to_string()));
762        assert!(!result.is_empty());
763        assert_eq!(result.len(), 2);
764        let (name, args) = extract_name_and_args(result[0].clone());
765        assert_eq!(name, "get_weather");
766        assert_eq!(args["location"], "San Francisco, CA");
767        assert_eq!(args["unit"], "fahrenheit");
768        let (name, args) = extract_name_and_args(result[1].clone());
769        assert_eq!(name, "get_weather");
770        assert_eq!(args["location"], "New York, NY");
771        assert_eq!(args["unit"], "fahrenheit");
772    }
773
774    #[tokio::test]
775    async fn test_meta_llama_llama31_8b_instruct_simple() {
776        let input = r#"{"name": "get_weather", "parameters": {"location": "San Francisco, CA", "unit": "fahrenheit"}}"#;
777        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::mistral())
778            .await
779            .unwrap();
780        assert_eq!(content, Some("".to_string()));
781        assert!(!result.is_empty());
782        assert_eq!(result.len(), 1);
783        let (name, args) = extract_name_and_args(result[0].clone());
784        assert_eq!(name, "get_weather");
785        assert_eq!(args["location"], "San Francisco, CA");
786        assert_eq!(args["unit"], "fahrenheit");
787    }
788
789    #[tokio::test]
790    async fn test_meta_llama_llama31_8b_instruct_simple_with_normal_text() {
791        let input = r#"Hey How are you? {"name": "get_weather", "parameters": {"location": "San Francisco, CA", "unit": "fahrenheit"}}"#;
792        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::mistral())
793            .await
794            .unwrap();
795        assert_eq!(content, Some("Hey How are you?".to_string()));
796        assert!(!result.is_empty());
797        assert_eq!(result.len(), 1);
798        let (name, args) = extract_name_and_args(result[0].clone());
799        assert_eq!(name, "get_weather");
800        assert_eq!(args["location"], "San Francisco, CA");
801        assert_eq!(args["unit"], "fahrenheit");
802    }
803
804    #[tokio::test]
805    async fn test_meta_llama_llama31_8b_instruct_with_new_lines() {
806        let input = r#"
807        {"name": "get_weather",
808        "parameters": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
809        "#;
810        let (result, content) = detect_and_parse_tool_call(input, Some("llama3_json"))
811            .await
812            .unwrap();
813        assert_eq!(content, Some("".to_string()));
814        assert!(!result.is_empty());
815        assert_eq!(result.len(), 1);
816        let (name, args) = extract_name_and_args(result[0].clone());
817        assert_eq!(name, "get_weather");
818        assert_eq!(args["location"], "San Francisco, CA");
819        assert_eq!(args["unit"], "fahrenheit");
820    }
821
822    #[tokio::test]
823    async fn test_meta_llama_llama31_8b_instruct_with_python_tag() {
824        let input = r#"<|python_tag|>{ "name": "get_weather", "parameters": {"location": "San Francisco, CA", "unit": "fahrenheit" } }"#;
825        let (result, content) = detect_and_parse_tool_call(input, Some("llama3_json"))
826            .await
827            .unwrap();
828        assert_eq!(content, Some("".to_string()));
829        assert!(!result.is_empty());
830        assert_eq!(result.len(), 1);
831        let (name, args) = extract_name_and_args(result[0].clone());
832        assert_eq!(name, "get_weather");
833        assert_eq!(args["location"], "San Francisco, CA");
834        assert_eq!(args["unit"], "fahrenheit");
835    }
836
837    #[tokio::test]
838    async fn test_meta_llama_llama31_8b_instruct_with_python_tag_with_normal_text() {
839        let input = r#"Hey How are you? <|python_tag|>{ "name": "get_weather", "parameters": {"location": "San Francisco, CA", "unit": "fahrenheit" } }"#;
840        let (result, content) = detect_and_parse_tool_call(input, Some("llama3_json"))
841            .await
842            .unwrap();
843        assert_eq!(content, Some("Hey How are you?".to_string()));
844        assert!(!result.is_empty());
845        assert_eq!(result.len(), 1);
846        let (name, args) = extract_name_and_args(result[0].clone());
847        assert_eq!(name, "get_weather");
848        assert_eq!(args["location"], "San Francisco, CA");
849        assert_eq!(args["unit"], "fahrenheit");
850    }
851
852    #[tokio::test]
853    async fn test_meta_llama_llama31_8b_instruct_with_python_tag_with_new_lines() {
854        let input = r#"
855        <|python_tag|>
856        {"name": "get_weather", "parameters": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
857        "#;
858        let (result, content) = detect_and_parse_tool_call(input, Some("llama3_json"))
859            .await
860            .unwrap();
861        assert_eq!(content, Some("".to_string()));
862        assert!(!result.is_empty());
863        assert_eq!(result.len(), 1);
864        let (name, args) = extract_name_and_args(result[0].clone());
865        assert_eq!(name, "get_weather");
866        assert_eq!(args["location"], "San Francisco, CA");
867        assert_eq!(args["unit"], "fahrenheit");
868    }
869
870    #[tokio::test]
871    async fn test_meta_llama_llama31_8b_instruct_with_python_tag_multiple_with_new_lines() {
872        let input = r#"
873        <|python_tag|>
874        {"name": "get_weather", "parameters": {"location": "San Francisco, CA", "unit": "fahrenheit" }}
875        <|python_tag|>
876        {"name": "get_weather", "parameters": {"location": "New York, NY", "unit": "fahrenheit" }}
877        "#;
878        let (result, content) = detect_and_parse_tool_call(input, Some("llama3_json"))
879            .await
880            .unwrap();
881        assert_eq!(content, Some("".to_string()));
882        assert!(!result.is_empty());
883        assert_eq!(result.len(), 2);
884        let (name, args) = extract_name_and_args(result[0].clone());
885        assert_eq!(name, "get_weather");
886        assert_eq!(args["location"], "San Francisco, CA");
887        assert_eq!(args["unit"], "fahrenheit");
888        let (name, args) = extract_name_and_args(result[1].clone());
889        assert_eq!(name, "get_weather");
890        assert_eq!(args["location"], "New York, NY");
891        assert_eq!(args["unit"], "fahrenheit");
892    }
893
894    #[tokio::test]
895    async fn test_detect_and_parse_tool_call_error_handling() {
896        // Unknown parser string should return an error
897        let input = r#"{"name": "get_weather", "arguments": {"location": "San Francisco, CA"}}"#;
898        let result = detect_and_parse_tool_call(input, Some("unknown_parser")).await;
899        assert!(result.is_err());
900        let err = result.unwrap_err().to_string();
901        assert!(
902            err.contains("is not implemented"),
903            "Unexpected error message: {}",
904            err
905        );
906
907        // Known parser, but invalid input (not JSON) should return Ok(None)
908        let input = "not a json";
909        let (result, content) = detect_and_parse_tool_call(input, Some("hermes"))
910            .await
911            .unwrap();
912        assert_eq!(content, Some("not a json".to_string()));
913        assert!(result.is_empty());
914
915        // Known parser, but valid JSON with wrong shape should return Ok(None)
916        let input = r#"{"foo": "bar"}"#;
917        let (result, content) = detect_and_parse_tool_call(input, Some("hermes"))
918            .await
919            .unwrap();
920        assert_eq!(content, Some(r#"{"foo": "bar"}"#.to_string()));
921        assert!(result.is_empty());
922    }
923
924    #[tokio::test]
925    #[ignore]
926    async fn test_internlm_internlm2_5_7b_chat_simple() {
927        let input = r#"San Francisco's weather is known for its mild climate with plenty of fog, especially along the coast. Here's an overview of the weather in Fahrenheit:
928
929- **Summer (June to August)**: Average highs range from the mid-60s to low 70s Fahrenheit, with cooler mornings and evenings. Coastal areas may be cooler than inland spots.
930
931Remember, San Francisco weather can be quite unpredictable, particularly with its famous fog, which can significantly lower temperatures. Always check a local weather forecast for the most accurate and up-to-date information."#;
932        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::default())
933            .await
934            .unwrap();
935        assert_eq!(content, Some(input.to_string()));
936        assert!(result.is_empty()); // This model doesn't produce tool calls
937    }
938
939    #[tokio::test]
940    #[ignore]
941    async fn test_ai21labs_ai21_jamba_15_mini_simple() {
942        let input = r#" [
943    {"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}
944]"#;
945        let config = ToolCallConfig {
946            format: ToolCallParserType::Json,
947            json: JsonParserConfig {
948                tool_call_start_tokens: vec![],
949                tool_call_end_tokens: vec![],
950                arguments_keys: vec!["arguments".to_string()],
951                ..Default::default()
952            },
953        };
954        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
955        assert_eq!(content, Some("".to_string()));
956        assert!(!result.is_empty());
957        assert_eq!(result.len(), 1);
958        let (name, args) = extract_name_and_args(result[0].clone());
959        assert_eq!(name, "get_weather");
960        assert_eq!(args["location"], "San Francisco, CA");
961        assert_eq!(args["unit"], "fahrenheit");
962    }
963
964    #[tokio::test]
965    #[ignore]
966    async fn test_salesforce_llama_xlam_2_8b_fc_r_simple() {
967        let input = r#"[{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]"#;
968        let config = ToolCallConfig {
969            format: ToolCallParserType::Json,
970            json: JsonParserConfig {
971                tool_call_start_tokens: vec![],
972                tool_call_end_tokens: vec![],
973                arguments_keys: vec!["arguments".to_string()],
974                ..Default::default()
975            },
976        };
977        let (result, content) = try_tool_call_parse(input, &config).await.unwrap();
978        assert_eq!(content, Some("".to_string()));
979        assert!(!result.is_empty());
980        assert_eq!(result.len(), 1);
981        let (name, args) = extract_name_and_args(result[0].clone());
982        assert_eq!(name, "get_weather");
983        assert_eq!(args["location"], "San Francisco, CA");
984        assert_eq!(args["unit"], "fahrenheit");
985    }
986
987    #[tokio::test]
988    async fn test_detect_and_parse_tool_call_default_parser_nemotron_deci() {
989        let input = r#"<TOOLCALL>[{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}]</TOOLCALL>"#;
990        let (result, content) = detect_and_parse_tool_call(input, None).await.unwrap();
991        assert_eq!(content, Some("".to_string()));
992        assert!(!result.is_empty());
993        assert_eq!(result.len(), 1);
994        let (name, args) = extract_name_and_args(result[0].clone());
995        assert_eq!(name, "get_weather");
996        assert_eq!(args["location"], "San Francisco, CA");
997        assert_eq!(args["unit"], "fahrenheit");
998    }
999
1000    #[tokio::test]
1001    async fn test_detect_and_parse_tool_call_default_parser_nemotron_deci_multiple() {
1002        let input = r#"<TOOLCALL>[{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}, {"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}]</TOOLCALL>"#;
1003        let (result, content) = detect_and_parse_tool_call(input, None).await.unwrap();
1004        assert_eq!(content, Some("".to_string()));
1005        assert!(!result.is_empty());
1006        assert_eq!(result.len(), 2);
1007        let (name, args) = extract_name_and_args(result[0].clone());
1008        assert_eq!(name, "get_weather");
1009        assert_eq!(args["location"], "San Francisco, CA");
1010        assert_eq!(args["unit"], "fahrenheit");
1011        let (name, args) = extract_name_and_args(result[1].clone());
1012        assert_eq!(name, "get_weather");
1013        assert_eq!(args["location"], "New York, NY");
1014        assert_eq!(args["unit"], "fahrenheit");
1015    }
1016
1017    #[tokio::test]
1018    async fn test_detect_and_parse_tool_call_default_parser_nemotron_deci_multiple_with_normal_text()
1019     {
1020        let input = r#"Hey How are you? <TOOLCALL>[{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit"}}, {"name": "get_weather", "arguments": {"location": "New York, NY", "unit": "fahrenheit"}}]</TOOLCALL>"#;
1021        let (result, content) = detect_and_parse_tool_call(input, None).await.unwrap();
1022        assert_eq!(content, Some("Hey How are you?".to_string()));
1023        assert!(!result.is_empty());
1024        assert_eq!(result.len(), 2);
1025        let (name, args) = extract_name_and_args(result[0].clone());
1026        assert_eq!(name, "get_weather");
1027        assert_eq!(args["location"], "San Francisco, CA");
1028        assert_eq!(args["unit"], "fahrenheit");
1029        let (name, args) = extract_name_and_args(result[1].clone());
1030        assert_eq!(name, "get_weather");
1031        assert_eq!(args["location"], "New York, NY");
1032        assert_eq!(args["unit"], "fahrenheit");
1033    }
1034
1035    #[tokio::test]
1036    async fn test_detect_and_parse_tool_call_default_parser_llama3_json_with_python_tag() {
1037        let input = r#"<|python_tag|>{ "name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit" } }"#;
1038        let (result, content) = detect_and_parse_tool_call(input, None).await.unwrap();
1039        assert_eq!(content, Some("".to_string()));
1040        assert!(!result.is_empty());
1041        assert_eq!(result.len(), 1);
1042        let (name, args) = extract_name_and_args(result[0].clone());
1043        assert_eq!(name, "get_weather");
1044        assert_eq!(args["location"], "San Francisco, CA");
1045        assert_eq!(args["unit"], "fahrenheit");
1046    }
1047
1048    #[tokio::test]
1049    async fn test_detect_and_parse_tool_call_default_parser_llama3_json_with_python_tag_with_normal_text()
1050     {
1051        let input = r#"Hey How are you? <|python_tag|>{ "name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit" } }"#;
1052        let (result, content) = detect_and_parse_tool_call(input, None).await.unwrap();
1053        assert_eq!(content, Some("Hey How are you?".to_string()));
1054        assert!(!result.is_empty());
1055        assert_eq!(result.len(), 1);
1056        let (name, args) = extract_name_and_args(result[0].clone());
1057        assert_eq!(name, "get_weather");
1058        assert_eq!(args["location"], "San Francisco, CA");
1059        assert_eq!(args["unit"], "fahrenheit");
1060    }
1061
1062    #[tokio::test]
1063    async fn test_detect_and_parse_tool_call_default_parser_llama3_json_with_python_tag_with_new_lines()
1064     {
1065        let input = r#"
1066        <|python_tag|>
1067        {"name":
1068        "get_weather",
1069         "arguments":
1070          {"location": "San Francisco, CA",
1071          "unit": "fahrenheit" }}
1072        "#;
1073        let (result, content) = detect_and_parse_tool_call(input, None).await.unwrap();
1074        assert_eq!(content, Some("".to_string()));
1075        assert!(!result.is_empty());
1076        assert_eq!(result.len(), 1);
1077        let (name, args) = extract_name_and_args(result[0].clone());
1078        assert_eq!(name, "get_weather");
1079        assert_eq!(args["location"], "San Francisco, CA");
1080        assert_eq!(args["unit"], "fahrenheit");
1081    }
1082
1083    #[tokio::test]
1084    async fn test_detect_and_parse_tool_call_default_parser_llama3_json_without_python_tag_multiple_with_new_lines()
1085     {
1086        let input = r#"
1087        {"name": "get_weather", "arguments":
1088         {"location": "San Francisco, CA",
1089          "unit": "fahrenheit" }}
1090        "#;
1091        let (result, content) = detect_and_parse_tool_call(input, None).await.unwrap();
1092        assert_eq!(content, Some("".to_string()));
1093        assert!(!result.is_empty());
1094        assert_eq!(result.len(), 1);
1095        let (name, args) = extract_name_and_args(result[0].clone());
1096        assert_eq!(name, "get_weather");
1097        assert_eq!(args["location"], "San Francisco, CA");
1098        assert_eq!(args["unit"], "fahrenheit");
1099    }
1100
1101    #[tokio::test]
1102    async fn test_detect_and_parse_tool_call_default_parser_llama3_json_without_python_tag() {
1103        let input = r#"{ "name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit" } }"#;
1104        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::mistral())
1105            .await
1106            .unwrap();
1107        assert_eq!(content, Some("".to_string()));
1108        assert!(!result.is_empty());
1109        assert_eq!(result.len(), 1);
1110        let (name, args) = extract_name_and_args(result[0].clone());
1111        assert_eq!(name, "get_weather");
1112        assert_eq!(args["location"], "San Francisco, CA");
1113        assert_eq!(args["unit"], "fahrenheit");
1114    }
1115
1116    #[tokio::test]
1117    async fn test_detect_and_parse_tool_call_default_parser_llama3_json_without_python_tag_with_normal_text()
1118     {
1119        let input = r#"Hey How are you? { "name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "fahrenheit" } }"#;
1120        let (result, content) = try_tool_call_parse(input, &ToolCallConfig::mistral())
1121            .await
1122            .unwrap();
1123        assert_eq!(content, Some("Hey How are you?".to_string()));
1124        assert!(!result.is_empty());
1125        assert_eq!(result.len(), 1);
1126        let (name, args) = extract_name_and_args(result[0].clone());
1127        assert_eq!(name, "get_weather");
1128        assert_eq!(args["location"], "San Francisco, CA");
1129        assert_eq!(args["unit"], "fahrenheit");
1130    }
1131
1132    #[tokio::test]
1133    async fn test_phi4_single_function_call() {
1134        let input =
1135            r#"functools[{"name": "get_country_capital", "arguments": {"country": "Poland"}}]"#;
1136        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1137            .await
1138            .unwrap();
1139        assert_eq!(content, Some("".to_string()));
1140        assert_eq!(result.len(), 1);
1141        let (name, args) = extract_name_and_args(result[0].clone());
1142        assert_eq!(name, "get_country_capital");
1143        assert_eq!(args["country"], "Poland");
1144    }
1145
1146    #[tokio::test]
1147    async fn test_phi4_single_function_call_with_normal_text() {
1148        let input = r#"Hey How are you? functools[{"name": "get_country_capital", "arguments": {"country": "Poland"}}]"#;
1149        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1150            .await
1151            .unwrap();
1152        assert_eq!(content, Some("Hey How are you?".to_string()));
1153        assert_eq!(result.len(), 1);
1154        let (name, args) = extract_name_and_args(result[0].clone());
1155        assert_eq!(name, "get_country_capital");
1156        assert_eq!(args["country"], "Poland");
1157    }
1158
1159    #[tokio::test]
1160    async fn test_phi4_multiple_function_calls_simple_arguments() {
1161        let input = r#"functools[
1162  {"name": "get_country_capital", "arguments": {"country": "Poland"}},
1163  {"name": "get_population", "arguments": {"city": "Warsaw"}}
1164]"#;
1165        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1166            .await
1167            .unwrap();
1168        assert_eq!(content, Some("".to_string()));
1169        assert_eq!(result.len(), 2);
1170
1171        let (name1, args1) = extract_name_and_args(result[0].clone());
1172        assert_eq!(name1, "get_country_capital");
1173        assert_eq!(args1["country"], "Poland");
1174
1175        let (name2, args2) = extract_name_and_args(result[1].clone());
1176        assert_eq!(name2, "get_population");
1177        assert_eq!(args2["city"], "Warsaw");
1178    }
1179
1180    #[tokio::test]
1181    async fn test_phi4_multiple_function_calls_simple_arguments_with_normal_text() {
1182        let input = r#"Hey How are you? functools[
1183  {"name": "get_country_capital", "arguments": {"country": "Poland"}},
1184  {"name": "get_population", "arguments": {"city": "Warsaw"}}
1185]"#;
1186        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1187            .await
1188            .unwrap();
1189        assert_eq!(content, Some("Hey How are you?".to_string()));
1190        assert_eq!(result.len(), 2);
1191
1192        let (name1, args1) = extract_name_and_args(result[0].clone());
1193        assert_eq!(name1, "get_country_capital");
1194        assert_eq!(args1["country"], "Poland");
1195
1196        let (name2, args2) = extract_name_and_args(result[1].clone());
1197        assert_eq!(name2, "get_population");
1198        assert_eq!(args2["city"], "Warsaw");
1199    }
1200
1201    #[tokio::test]
1202    async fn test_phi4_single_function_call_nested_json_arguments() {
1203        let input = r#"functools[{"name": "get_weather_forecast", "arguments":
1204        {"location": {"city": "San Francisco",
1205        "state": "CA"}, "date": "2023-10-05"}}]"#;
1206        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1207            .await
1208            .unwrap();
1209        assert_eq!(content, Some("".to_string()));
1210        assert_eq!(result.len(), 1);
1211        let (name, args) = extract_name_and_args(result[0].clone());
1212        assert_eq!(name, "get_weather_forecast");
1213        assert_eq!(args["date"], "2023-10-05");
1214        assert_eq!(args["location"]["city"], "San Francisco");
1215        assert_eq!(args["location"]["state"], "CA");
1216    }
1217
1218    #[tokio::test]
1219    async fn test_phi4_single_function_call_nested_json_arguments_with_normal_text() {
1220        let input = r#"Hey How are you? functools[{"name": "get_weather_forecast", "arguments":
1221        {"location": {"city": "San Francisco",
1222        "state": "CA"}, "date": "2023-10-05"}}]"#;
1223        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1224            .await
1225            .unwrap();
1226        assert_eq!(content, Some("Hey How are you?".to_string()));
1227        assert_eq!(result.len(), 1);
1228        let (name, args) = extract_name_and_args(result[0].clone());
1229        assert_eq!(name, "get_weather_forecast");
1230        assert_eq!(args["date"], "2023-10-05");
1231        assert_eq!(args["location"]["city"], "San Francisco");
1232        assert_eq!(args["location"]["state"], "CA");
1233    }
1234
1235    #[tokio::test]
1236    async fn test_phi4_function_call_with_parameters_instead_of_arguments() {
1237        let input = r#"functools[{"name": "calculate_distance",
1238         "parameters": {"from": "New York", "to": "Los Angeles"}}]"#;
1239        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1240            .await
1241            .unwrap();
1242        assert_eq!(content, Some("".to_string()));
1243        assert_eq!(result.len(), 1);
1244        let (name, args) = extract_name_and_args(result[0].clone());
1245        assert_eq!(name, "calculate_distance");
1246        assert_eq!(args["from"], "New York");
1247        assert_eq!(args["to"], "Los Angeles");
1248    }
1249
1250    #[tokio::test]
1251    async fn test_phi4_function_call_with_parameters_instead_of_arguments_with_normal_text() {
1252        let input = r#"Hey How are you? functools[{"name": "calculate_distance",
1253         "parameters": {"from": "New York", "to": "Los Angeles"}}]"#;
1254        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1255            .await
1256            .unwrap();
1257        assert_eq!(content, Some("Hey How are you?".to_string()));
1258        assert_eq!(result.len(), 1);
1259        let (name, args) = extract_name_and_args(result[0].clone());
1260        assert_eq!(name, "calculate_distance");
1261        assert_eq!(args["from"], "New York");
1262        assert_eq!(args["to"], "Los Angeles");
1263    }
1264
1265    #[tokio::test]
1266    async fn test_phi4_token_leak_reproduction() {
1267        // Reproduce the issue where "functools" appears in content field
1268        // This might happen when there's malformed JSON or parsing issues
1269        let input = r#"functools{"name": "get_weather","arguments":{"location":"San Francisco"}}"#;
1270        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1271            .await
1272            .unwrap();
1273        // Content should be empty, not contain "functools"
1274        assert_eq!(content, Some("".to_string()));
1275        assert_eq!(result.len(), 1);
1276        let (name, args) = extract_name_and_args(result[0].clone());
1277        assert_eq!(name, "get_weather");
1278        assert_eq!(args["location"], "San Francisco");
1279    }
1280
1281    #[tokio::test]
1282    async fn test_phi4_token_leak_edge_case() {
1283        // Test the case where only the token appears without JSON
1284        // This case is less critical but shouldn't leak the full token
1285        let input = r#"functools"#;
1286        let (result, _content) = detect_and_parse_tool_call(input, Some("phi4"))
1287            .await
1288            .unwrap();
1289        // Content may contain the token if no valid JSON follows, but shouldn't crash
1290        // The important thing is that no tool calls are returned
1291        assert_eq!(result.len(), 0); // No tool calls found
1292        // Content behavior is less critical for this edge case
1293    }
1294
1295    #[tokio::test]
1296    async fn test_phi4_token_with_invalid_json() {
1297        // Test the case where token is followed by invalid JSON
1298        let input = r#"functools{invalid json}"#;
1299        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1300            .await
1301            .unwrap();
1302        // Content should be empty, not contain "functools" or leak the token
1303        assert_eq!(content, Some("".to_string()));
1304        assert_eq!(result.len(), 0); // No tool calls found due to invalid JSON
1305    }
1306
1307    #[tokio::test]
1308    async fn test_phi4_streaming_partial_tokens() {
1309        // Test that our fix handles the actual streaming scenario described by the user
1310        // Where "fun", "ct", "ools" arrive as separate chunks
1311
1312        // Test that "fun" is detected as a potential tool call start (for streaming jailing)
1313        let config = super::get_tool_parser_map().get("phi4").unwrap();
1314
1315        // Test detection of partial tokens
1316        use super::super::json::detect_tool_call_start_json;
1317        assert!(
1318            detect_tool_call_start_json("fun", &config.json),
1319            "'fun' should be detected as potential start"
1320        );
1321        assert!(
1322            detect_tool_call_start_json("f", &config.json),
1323            "'f' should be detected as potential start"
1324        );
1325        assert!(
1326            detect_tool_call_start_json("func", &config.json),
1327            "'func' should be detected as potential start"
1328        );
1329        assert!(
1330            detect_tool_call_start_json("functo", &config.json),
1331            "'functo' should be detected as potential start"
1332        );
1333
1334        // Test that unrelated text is not detected
1335        assert!(
1336            !detect_tool_call_start_json("hello", &config.json),
1337            "'hello' should not be detected"
1338        );
1339        assert!(
1340            !detect_tool_call_start_json("xyz", &config.json),
1341            "'xyz' should not be detected"
1342        );
1343    }
1344
1345    #[tokio::test]
1346    async fn test_phi4_false_positive_words() {
1347        // Test that words like "funk" or text starting with "func" but not "functools"
1348        // are correctly treated as normal content, not tool calls
1349
1350        let input = r#"funk music is great"#;
1351        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1352            .await
1353            .unwrap();
1354        // Should be treated as normal content, not tool call
1355        assert_eq!(
1356            result.len(),
1357            0,
1358            "No tool calls should be found in 'funk music is great'"
1359        );
1360        assert_eq!(
1361            content,
1362            Some("funk music is great".to_string()),
1363            "Content should contain the original text"
1364        );
1365    }
1366
1367    #[tokio::test]
1368    async fn test_phi4_partial_but_complete_words() {
1369        // Test words that start with "func" but are not "functools"
1370
1371        let input = r#"The function works well"#;
1372        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1373            .await
1374            .unwrap();
1375        assert_eq!(
1376            result.len(),
1377            0,
1378            "No tool calls should be found in 'The function works well'"
1379        );
1380        assert_eq!(content, Some("The function works well".to_string()));
1381
1382        let input = r#"functional programming"#;
1383        let (result, content) = detect_and_parse_tool_call(input, Some("phi4"))
1384            .await
1385            .unwrap();
1386        assert_eq!(
1387            result.len(),
1388            0,
1389            "No tool calls should be found in 'functional programming'"
1390        );
1391        assert_eq!(content, Some("functional programming".to_string()));
1392    }
1393
1394    #[tokio::test]
1395    async fn test_phi4_funk_variations() {
1396        // Test various "funk" related words to ensure they're not treated as tool calls
1397
1398        let test_cases = vec![
1399            "funk",
1400            "funky",
1401            "funktion", // German word for function
1402            "funked",
1403            "I love funk music",
1404            "This is funky stuff",
1405        ];
1406
1407        for test_input in test_cases {
1408            let (result, content) = detect_and_parse_tool_call(test_input, Some("phi4"))
1409                .await
1410                .unwrap();
1411            assert_eq!(
1412                result.len(),
1413                0,
1414                "No tool calls should be found in '{}'",
1415                test_input
1416            );
1417            assert_eq!(
1418                content,
1419                Some(test_input.to_string()),
1420                "Content should match input for '{}'",
1421                test_input
1422            );
1423        }
1424    }
1425
1426    #[tokio::test]
1427    async fn test_phi4_func_but_not_functools() {
1428        // Test words starting with "func" that are complete words, not partial "functools"
1429
1430        let test_cases = vec![
1431            "func()",  // Programming syntax
1432            "funcdef", // Python keyword variant
1433            "functions are useful",
1434            "functionally speaking",
1435        ];
1436
1437        for test_input in test_cases {
1438            let (result, content) = detect_and_parse_tool_call(test_input, Some("phi4"))
1439                .await
1440                .unwrap();
1441            assert_eq!(
1442                result.len(),
1443                0,
1444                "No tool calls should be found in '{}'",
1445                test_input
1446            );
1447            assert_eq!(
1448                content,
1449                Some(test_input.to_string()),
1450                "Content should match input for '{}'",
1451                test_input
1452            );
1453        }
1454    }
1455
1456    #[tokio::test]
1457    async fn test_pythonic_parser_basic_with_constants() {
1458        let input = r#"[get_weather(location="San Francisco", unit="fahrenheit"), get_weather(location="New York", unit="fahrenheit")]"#;
1459        let (result, content) = detect_and_parse_tool_call(input, Some("pythonic"))
1460            .await
1461            .unwrap();
1462        assert_eq!(content, Some("".to_string()));
1463        assert_eq!(result.len(), 2);
1464        let (name, args) = extract_name_and_args(result[0].clone());
1465        assert_eq!(name, "get_weather");
1466        assert_eq!(args["location"], "San Francisco");
1467        assert_eq!(args["unit"], "fahrenheit");
1468        let (name, args) = extract_name_and_args(result[1].clone());
1469        assert_eq!(name, "get_weather");
1470        assert_eq!(args["location"], "New York");
1471        assert_eq!(args["unit"], "fahrenheit");
1472    }
1473
1474    #[tokio::test]
1475    #[ignore]
1476    async fn test_pythonic_parser_with_constants_and_normal_text() {
1477        let input = r#"Hey How are you? [get_weather(location="San Francisco", unit="fahrenheit"), get_weather(location="New York", unit="fahrenheit")]"#;
1478        let (result, content) = detect_and_parse_tool_call(input, Some("pythonic"))
1479            .await
1480            .unwrap();
1481        assert_eq!(content, Some("Hey How are you?".to_string()));
1482        assert_eq!(result.len(), 2);
1483
1484        let (name, args) = extract_name_and_args(result[0].clone());
1485        assert_eq!(name, "get_weather");
1486        assert_eq!(args["location"], "San Francisco");
1487        assert_eq!(args["unit"], "fahrenheit");
1488        let (name, args) = extract_name_and_args(result[1].clone());
1489        assert_eq!(name, "get_weather");
1490        assert_eq!(args["location"], "New York");
1491        assert_eq!(args["unit"], "fahrenheit");
1492    }
1493
1494    #[tokio::test]
1495    async fn test_harmony_parser_basic() {
1496        let input = r#"
1497        <|channel|>analysis<|message|>Need to use function get_current_weather.<|end|><|start|>assistant<|channel|>commentary to=functions.get_current_weather <|constrain|>json<|message|>{"location":"San Francisco", "unit":"fahrenheit"}"#;
1498        let (result, content) = detect_and_parse_tool_call(input, Some("harmony"))
1499            .await
1500            .unwrap();
1501        assert_eq!(
1502            content,
1503            Some("Need to use function get_current_weather.".to_string())
1504        );
1505        assert_eq!(result.len(), 1);
1506        let (name, args) = extract_name_and_args(result[0].clone());
1507        assert_eq!(name, "get_current_weather");
1508        assert_eq!(args["location"], "San Francisco");
1509        assert_eq!(args["unit"], "fahrenheit");
1510    }
1511
1512    #[tokio::test]
1513    async fn test_deepseek_v3_1_parser_basic() {
1514        let input = r#"<|tool▁calls▁begin|><|tool▁call▁begin|>get_current_weather<|tool▁sep|>{"location": "Tokyo"}<|tool▁call▁end|><|tool▁call▁begin|>get_current_weather<|tool▁sep|>{"location": "Paris"}<|tool▁call▁end|><|tool▁calls▁end|><|end▁of▁sentence|>"#;
1515        let (result, content) = detect_and_parse_tool_call(input, Some("deepseek_v3_1"))
1516            .await
1517            .unwrap();
1518        assert_eq!(content, Some("".to_string()));
1519        assert_eq!(result.len(), 2);
1520        let (name, args) = extract_name_and_args(result[0].clone());
1521        assert_eq!(name, "get_current_weather");
1522        assert_eq!(args["location"], "Tokyo");
1523        let (name, args) = extract_name_and_args(result[1].clone());
1524        assert_eq!(name, "get_current_weather");
1525        assert_eq!(args["location"], "Paris");
1526    }
1527
1528    #[tokio::test]
1529    async fn test_hermes_parser_without_new_line() {
1530        let input = r#"<tool_call>{"name": "get_weather", "arguments": {"location": "San Francisco, CA", "unit": "celsius"}}</tool_call>"
1531        "#;
1532        let (result, content) = detect_and_parse_tool_call(input, Some("hermes"))
1533            .await
1534            .unwrap();
1535        assert_eq!(content, Some("".to_string()));
1536        assert_eq!(result.len(), 1);
1537        let (name, args) = extract_name_and_args(result[0].clone());
1538        assert_eq!(name, "get_weather");
1539        assert_eq!(args["location"], "San Francisco, CA");
1540        assert_eq!(args["unit"], "celsius");
1541    }
1542}
1543
1544// Comprehensive parallel tool calling tests based on the examples provided
1545#[cfg(test)]
1546mod parallel_tool_calling_tests {
1547    use super::*;
1548
1549    fn extract_name_and_args(call: ToolCallResponse) -> (String, serde_json::Value) {
1550        let args: serde_json::Value = serde_json::from_str(&call.function.arguments).unwrap();
1551        (call.function.name, args)
1552    }
1553
1554    /// Helper function to validate parallel tool call results for weather queries
1555    fn validate_weather_tool_calls(result: &[ToolCallResponse], expected_cities: &[(&str, &str)]) {
1556        assert_eq!(
1557            result.len(),
1558            expected_cities.len(),
1559            "Expected {} tool calls, got {}",
1560            expected_cities.len(),
1561            result.len()
1562        );
1563
1564        for (i, (expected_city, expected_state)) in expected_cities.iter().enumerate() {
1565            let (name, args) = extract_name_and_args(result[i].clone());
1566            assert_eq!(
1567                name, "get_current_weather",
1568                "Tool call {} should be get_current_weather",
1569                i
1570            );
1571            assert_eq!(
1572                args["city"], *expected_city,
1573                "Tool call {} city should be {}",
1574                i, expected_city
1575            );
1576            assert_eq!(
1577                args["state"], *expected_state,
1578                "Tool call {} state should be {}",
1579                i, expected_state
1580            );
1581            assert_eq!(
1582                args["unit"], "fahrenheit",
1583                "Tool call {} unit should be fahrenheit",
1584                i
1585            );
1586
1587            // Validate tool call ID format (should be at least 9 characters)
1588            assert!(
1589                result[i].id.len() >= 9,
1590                "Tool call {} ID should be at least 9 characters",
1591                i
1592            );
1593
1594            // Validate tool call type
1595            assert_eq!(
1596                result[i].tp,
1597                crate::tool_calling::response::ToolCallType::Function,
1598                "Tool call {} type should be 'function'",
1599                i
1600            );
1601        }
1602    }
1603
1604    // =============================================================================
1605    // 1. NEMOTRON/DECI TOOL PARSER FORMAT (JSON Array in XML tags)
1606    // =============================================================================
1607
1608    #[tokio::test]
1609    async fn test_parallel_nemotron_format_two_cities() {
1610        let input = r#" <TOOLCALL>[
1611    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1612    {"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}}
1613]</TOOLCALL>"#;
1614
1615        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1616            .await
1617            .unwrap();
1618
1619        assert_eq!(content, Some("".to_string()));
1620        validate_weather_tool_calls(&result, &[("Dallas", "TX"), ("Orlando", "FL")]);
1621    }
1622
1623    #[tokio::test]
1624    async fn test_parallel_nemotron_format_three_cities() {
1625        let input = r#"<TOOLCALL>[
1626    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1627    {"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}},
1628    {"name": "get_current_weather", "arguments": {"city": "Seattle", "state": "WA", "unit": "fahrenheit"}}
1629]</TOOLCALL>"#;
1630
1631        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1632            .await
1633            .unwrap();
1634
1635        assert_eq!(content, Some("".to_string()));
1636        validate_weather_tool_calls(
1637            &result,
1638            &[("Dallas", "TX"), ("Orlando", "FL"), ("Seattle", "WA")],
1639        );
1640    }
1641
1642    #[tokio::test]
1643    async fn test_parallel_nemotron_format_with_normal_text() {
1644        let input = r#"I'll help you get the weather for both cities. <TOOLCALL>[
1645    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1646    {"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}}
1647]</TOOLCALL>"#;
1648
1649        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1650            .await
1651            .unwrap();
1652
1653        assert_eq!(
1654            content,
1655            Some("I'll help you get the weather for both cities.".to_string())
1656        );
1657        validate_weather_tool_calls(&result, &[("Dallas", "TX"), ("Orlando", "FL")]);
1658    }
1659
1660    // =============================================================================
1661    // 2. QWEN3CODER TOOL PARSER FORMAT (XML-style tags) - Testing via hermes parser
1662    // =============================================================================
1663
1664    #[tokio::test]
1665    async fn test_parallel_qwen3coder_format_two_cities() {
1666        let _input = r#"<tool_call>
1667<function=get_current_weather>
1668<parameter=city>
1669Dallas
1670</parameter>
1671<parameter=state>
1672TX
1673</parameter>
1674<parameter=unit>
1675fahrenheit
1676</parameter>
1677</function>
1678</tool_call>
1679<tool_call>
1680<function=get_current_weather>
1681<parameter=city>
1682Orlando
1683</parameter>
1684<parameter=state>
1685FL
1686</parameter>
1687<parameter=unit>
1688fahrenheit
1689</parameter>
1690</function>
1691</tool_call>"#;
1692
1693        // Note: This format would need a specialized parser, but for now we test with hermes
1694        // which handles multiple <tool_call> tags
1695        let input_hermes_format = r#"<tool_call>{"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}}</tool_call>
1696<tool_call>{"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}}</tool_call>"#;
1697
1698        let (result, content) = detect_and_parse_tool_call(input_hermes_format, Some("hermes"))
1699            .await
1700            .unwrap();
1701
1702        assert_eq!(content, Some("".to_string()));
1703        validate_weather_tool_calls(&result, &[("Dallas", "TX"), ("Orlando", "FL")]);
1704    }
1705
1706    // =============================================================================
1707    // 3. xLAM TOOL PARSER FORMAT (Pure JSON Array) - Testing via mistral parser
1708    // =============================================================================
1709
1710    #[tokio::test]
1711    async fn test_parallel_xlam_format_pure_json() {
1712        let input = r#"[{"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}}, {"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}}]"#;
1713
1714        let (result, content) = detect_and_parse_tool_call(input, Some("mistral"))
1715            .await
1716            .unwrap();
1717
1718        assert_eq!(content, Some("".to_string()));
1719        validate_weather_tool_calls(&result, &[("Dallas", "TX"), ("Orlando", "FL")]);
1720    }
1721
1722    #[tokio::test]
1723    async fn test_parallel_xlam_format_with_whitespace() {
1724        let input = r#"[
1725    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1726    {"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}}
1727]"#;
1728
1729        let (result, content) = detect_and_parse_tool_call(input, Some("mistral"))
1730            .await
1731            .unwrap();
1732
1733        assert_eq!(content, Some("".to_string()));
1734        validate_weather_tool_calls(&result, &[("Dallas", "TX"), ("Orlando", "FL")]);
1735    }
1736
1737    // =============================================================================
1738    // 4. MINIMAX TOOL PARSER FORMAT (Multi-line JSON in XML tags)
1739    // =============================================================================
1740
1741    #[tokio::test]
1742    async fn test_parallel_minimax_format() {
1743        let _input = r#"<tool_calls>
1744{"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}}
1745{"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}}
1746</tool_calls>"#;
1747
1748        // This would need a specialized parser, but we can test with a modified hermes approach
1749        // For now, test with nemotron_deci which handles similar XML wrapping
1750        let input_nemotron_format = r#"<TOOLCALL>[
1751{"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1752{"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}}
1753]</TOOLCALL>"#;
1754
1755        let (result, content) =
1756            detect_and_parse_tool_call(input_nemotron_format, Some("nemotron_deci"))
1757                .await
1758                .unwrap();
1759
1760        assert_eq!(content, Some("".to_string()));
1761        validate_weather_tool_calls(&result, &[("Dallas", "TX"), ("Orlando", "FL")]);
1762    }
1763
1764    // =============================================================================
1765    // 5. HARMONY TOOL PARSER FORMAT (Multiple Tool Calls with Harmony Encoding)
1766    // =============================================================================
1767
1768    #[tokio::test]
1769    async fn test_parallel_harmony_format_multiple_tools() {
1770        // Test with harmony parser for multiple tool calls
1771        let input = r#"<|channel|>commentary to=functions.get_current_weather <|constrain|>json<|message|>{"city": "Dallas", "state": "TX", "unit": "fahrenheit"}<|call|><|start|>assistant<|channel|>commentary to=functions.get_current_weather <|constrain|>json<|message|>{"city": "Orlando", "state": "FL", "unit": "fahrenheit"}<|call|>"#;
1772
1773        let (result, _content) = detect_and_parse_tool_call(input, Some("harmony"))
1774            .await
1775            .unwrap();
1776
1777        // Harmony parser might handle this differently, so we check for at least one tool call
1778        assert!(!result.is_empty(), "Should parse at least one tool call");
1779
1780        // Validate first tool call
1781        let (name, args) = extract_name_and_args(result[0].clone());
1782        assert_eq!(name, "get_current_weather");
1783        assert!(args.get("city").is_some() || args.get("location").is_some());
1784    }
1785
1786    // =============================================================================
1787    // 6. MIXED TOOL TYPES PARALLEL CALLING
1788    // =============================================================================
1789
1790    #[tokio::test]
1791    async fn test_parallel_mixed_tool_types() {
1792        let input = r#"<TOOLCALL>[
1793    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1794    {"name": "web_search", "arguments": {"query": "Orlando Florida attractions", "max_results": 5}}
1795]</TOOLCALL>"#;
1796
1797        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1798            .await
1799            .unwrap();
1800
1801        assert_eq!(content, Some("".to_string()));
1802        assert_eq!(result.len(), 2);
1803
1804        // Validate first tool call (weather)
1805        let (name1, args1) = extract_name_and_args(result[0].clone());
1806        assert_eq!(name1, "get_current_weather");
1807        assert_eq!(args1["city"], "Dallas");
1808        assert_eq!(args1["state"], "TX");
1809        assert_eq!(args1["unit"], "fahrenheit");
1810
1811        // Validate second tool call (web search)
1812        let (name2, args2) = extract_name_and_args(result[1].clone());
1813        assert_eq!(name2, "web_search");
1814        assert_eq!(args2["query"], "Orlando Florida attractions");
1815        assert_eq!(args2["max_results"], 5);
1816    }
1817
1818    // =============================================================================
1819    // 7. EDGE CASES AND ERROR HANDLING
1820    // =============================================================================
1821
1822    #[tokio::test]
1823    async fn test_parallel_malformed_second_call() {
1824        let input = r#"<TOOLCALL>[
1825    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1826    {"name": "get_current_weather", "arguments": {"city": "Orlando", "invalid_field": 123}}
1827]</TOOLCALL>"#;
1828
1829        let (result, _content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1830            .await
1831            .unwrap();
1832
1833        // Should still parse the valid first call
1834        assert!(
1835            !result.is_empty(),
1836            "Should parse at least the valid tool call"
1837        );
1838
1839        let (name, args) = extract_name_and_args(result[0].clone());
1840        assert_eq!(name, "get_current_weather");
1841        assert_eq!(args["city"], "Dallas");
1842    }
1843
1844    #[tokio::test]
1845    async fn test_parallel_empty_array() {
1846        let input = r#"<TOOLCALL>[]</TOOLCALL>"#;
1847
1848        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1849            .await
1850            .unwrap();
1851
1852        assert_eq!(
1853            result.len(),
1854            0,
1855            "Empty array should result in no tool calls"
1856        );
1857        assert_eq!(content, Some("".to_string()));
1858    }
1859
1860    #[tokio::test]
1861    async fn test_parallel_single_call_in_array() {
1862        let input = r#"<TOOLCALL>[
1863    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}}
1864]</TOOLCALL>"#;
1865
1866        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1867            .await
1868            .unwrap();
1869
1870        assert_eq!(content, Some("".to_string()));
1871        assert_eq!(result.len(), 1);
1872        validate_weather_tool_calls(&result, &[("Dallas", "TX")]);
1873    }
1874
1875    // =============================================================================
1876    // 8. LARGE SCALE PARALLEL CALLS
1877    // =============================================================================
1878
1879    #[tokio::test]
1880    async fn test_parallel_five_cities() {
1881        let input = r#"<TOOLCALL>[
1882    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
1883    {"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}},
1884    {"name": "get_current_weather", "arguments": {"city": "Seattle", "state": "WA", "unit": "fahrenheit"}},
1885    {"name": "get_current_weather", "arguments": {"city": "Denver", "state": "CO", "unit": "fahrenheit"}},
1886    {"name": "get_current_weather", "arguments": {"city": "Miami", "state": "FL", "unit": "fahrenheit"}}
1887]</TOOLCALL>"#;
1888
1889        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1890            .await
1891            .unwrap();
1892
1893        assert_eq!(content, Some("".to_string()));
1894        validate_weather_tool_calls(
1895            &result,
1896            &[
1897                ("Dallas", "TX"),
1898                ("Orlando", "FL"),
1899                ("Seattle", "WA"),
1900                ("Denver", "CO"),
1901                ("Miami", "FL"),
1902            ],
1903        );
1904    }
1905
1906    // =============================================================================
1907    // 9. COMPLEX ARGUMENTS PARALLEL CALLS
1908    // =============================================================================
1909
1910    #[tokio::test]
1911    async fn test_parallel_complex_arguments() {
1912        let input = r#"<TOOLCALL>[
1913    {
1914        "name": "get_weather_forecast",
1915        "arguments": {
1916            "location": {"city": "Dallas", "state": "TX", "country": "USA"},
1917            "days": 7,
1918            "units": "fahrenheit",
1919            "include_hourly": true,
1920            "alerts": ["severe_weather", "temperature_extreme"]
1921        }
1922    },
1923    {
1924        "name": "get_air_quality",
1925        "arguments": {
1926            "coordinates": {"lat": 32.7767, "lon": -96.7970},
1927            "metrics": ["pm2.5", "pm10", "ozone", "no2"],
1928            "radius_km": 50
1929        }
1930    }
1931]</TOOLCALL>"#;
1932
1933        let (result, content) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
1934            .await
1935            .unwrap();
1936
1937        assert_eq!(content, Some("".to_string()));
1938        assert_eq!(result.len(), 2);
1939
1940        // Validate first tool call (weather forecast)
1941        let (name1, args1) = extract_name_and_args(result[0].clone());
1942        assert_eq!(name1, "get_weather_forecast");
1943        assert_eq!(args1["location"]["city"], "Dallas");
1944        assert_eq!(args1["days"], 7);
1945        assert_eq!(args1["include_hourly"], true);
1946
1947        // Validate second tool call (air quality)
1948        let (name2, args2) = extract_name_and_args(result[1].clone());
1949        assert_eq!(name2, "get_air_quality");
1950        assert_eq!(args2["coordinates"]["lat"], 32.7767);
1951        assert_eq!(args2["radius_km"], 50);
1952    }
1953
1954    // =============================================================================
1955    // 10. VALIDATION HELPERS AND UTILITIES
1956    // =============================================================================
1957
1958    /// Helper function to validate tool call IDs are unique and properly formatted
1959    fn validate_tool_call_ids(result: &[ToolCallResponse]) {
1960        let mut ids = std::collections::HashSet::new();
1961        for (i, tool_call) in result.iter().enumerate() {
1962            assert!(
1963                tool_call.id.len() >= 9,
1964                "Tool call {} ID '{}' should be at least 9 characters",
1965                i,
1966                tool_call.id
1967            );
1968
1969            assert!(
1970                ids.insert(&tool_call.id),
1971                "Tool call {} ID '{}' is not unique",
1972                i,
1973                tool_call.id
1974            );
1975        }
1976    }
1977
1978    /// Helper function to validate tool call structure and OpenAI compatibility
1979    fn validate_openai_compatibility(result: &[ToolCallResponse]) {
1980        for (i, tool_call) in result.iter().enumerate() {
1981            // Validate type is "function"
1982            assert_eq!(
1983                tool_call.tp,
1984                crate::tool_calling::response::ToolCallType::Function,
1985                "Tool call {} type should be 'function', got '{:?}'",
1986                i,
1987                tool_call.tp
1988            );
1989
1990            // Validate function name is not empty
1991            assert!(
1992                !tool_call.function.name.is_empty(),
1993                "Tool call {} function name should not be empty",
1994                i
1995            );
1996
1997            // Validate arguments are valid JSON
1998            let _: serde_json::Value = serde_json::from_str(&tool_call.function.arguments)
1999                .unwrap_or_else(|_| panic!("Tool call {} arguments should be valid JSON", i));
2000        }
2001    }
2002
2003    #[tokio::test]
2004    async fn test_parallel_tool_call_id_uniqueness() {
2005        let input = r#"<TOOLCALL>[
2006    {"name": "get_current_weather", "arguments": {"city": "Dallas", "state": "TX", "unit": "fahrenheit"}},
2007    {"name": "get_current_weather", "arguments": {"city": "Orlando", "state": "FL", "unit": "fahrenheit"}},
2008    {"name": "web_search", "arguments": {"query": "weather forecast", "max_results": 3}}
2009]</TOOLCALL>"#;
2010
2011        let (result, _) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
2012            .await
2013            .unwrap();
2014
2015        assert_eq!(result.len(), 3);
2016        validate_tool_call_ids(&result);
2017        validate_openai_compatibility(&result);
2018    }
2019
2020    #[tokio::test]
2021    async fn test_parallel_openai_compatibility_validation() {
2022        let input = r#"[TOOL_CALLS][
2023    {"name": "function_one", "arguments": {"param1": "value1", "param2": 42}},
2024    {"name": "function_two", "arguments": {"param3": true, "param4": [1, 2, 3]}},
2025    {"name": "function_three", "arguments": {"param5": {"nested": "object"}}}
2026][/TOOL_CALLS]"#;
2027
2028        let (result, _) = detect_and_parse_tool_call(input, Some("mistral"))
2029            .await
2030            .unwrap();
2031
2032        assert_eq!(result.len(), 3);
2033        validate_openai_compatibility(&result);
2034
2035        // Verify all functions have different names
2036        let names: std::collections::HashSet<_> =
2037            result.iter().map(|tc| &tc.function.name).collect();
2038        assert_eq!(names.len(), 3, "All function names should be unique");
2039    }
2040
2041    // =============================================================================
2042    // 11. PERFORMANCE AND STRESS TESTS
2043    // =============================================================================
2044
2045    #[tokio::test]
2046    async fn test_parallel_performance_many_small_calls() {
2047        let mut tool_calls = Vec::new();
2048        for i in 0..20 {
2049            tool_calls.push(format!(
2050                r#"{{"name": "get_data_{}", "arguments": {{"id": {}, "type": "test"}}}}"#,
2051                i, i
2052            ));
2053        }
2054
2055        let input = format!("<TOOLCALL>[{}]</TOOLCALL>", tool_calls.join(","));
2056
2057        let start = std::time::Instant::now();
2058        let (result, _) = detect_and_parse_tool_call(&input, Some("nemotron_deci"))
2059            .await
2060            .unwrap();
2061        let duration = start.elapsed();
2062
2063        assert_eq!(result.len(), 20);
2064        assert!(
2065            duration < std::time::Duration::from_millis(100),
2066            "Parsing 20 tool calls should take less than 100ms, took {:?}",
2067            duration
2068        );
2069
2070        validate_tool_call_ids(&result);
2071        validate_openai_compatibility(&result);
2072    }
2073
2074    #[tokio::test]
2075    async fn test_parallel_large_arguments() {
2076        let large_data = "x".repeat(1000); // 1KB of data
2077        let input = format!(
2078            r#"<TOOLCALL>[
2079    {{"name": "process_large_data", "arguments": {{"data": "{}", "size": 1000}}}},
2080    {{"name": "backup_data", "arguments": {{"backup_data": "{}", "timestamp": "2024-01-01T00:00:00Z"}}}}
2081]</TOOLCALL>"#,
2082            large_data, large_data
2083        );
2084
2085        let (result, _) = detect_and_parse_tool_call(&input, Some("nemotron_deci"))
2086            .await
2087            .unwrap();
2088
2089        assert_eq!(result.len(), 2);
2090
2091        // Validate large arguments are preserved
2092        for tool_call in &result {
2093            let args: serde_json::Value =
2094                serde_json::from_str(&tool_call.function.arguments).unwrap();
2095            if tool_call.function.name == "process_large_data" {
2096                assert_eq!(args["data"].as_str().unwrap().len(), 1000);
2097                assert_eq!(args["size"], 1000);
2098            }
2099        }
2100    }
2101
2102    // =============================================================================
2103    // 12. ADDITIONAL EDGE CASES AND ERROR SCENARIOS
2104    // =============================================================================
2105
2106    #[tokio::test]
2107    async fn test_parallel_unicode_and_special_characters() {
2108        let input = r#"<TOOLCALL>[
2109    {"name": "translate_text", "arguments": {"text": "Hello 世界! 🌍", "from": "en", "to": "zh"}},
2110    {"name": "analyze_emoji", "arguments": {"emoji": "🚀💫⭐", "context": "space exploration"}},
2111    {"name": "process_unicode", "arguments": {"data": "café naïve résumé", "encoding": "utf-8"}}
2112]</TOOLCALL>"#;
2113
2114        let (result, _) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
2115            .await
2116            .unwrap();
2117
2118        assert_eq!(result.len(), 3);
2119
2120        // Validate Unicode characters are preserved
2121        let (name1, args1) = extract_name_and_args(result[0].clone());
2122        assert_eq!(name1, "translate_text");
2123        assert_eq!(args1["text"], "Hello 世界! 🌍");
2124
2125        let (name2, args2) = extract_name_and_args(result[1].clone());
2126        assert_eq!(name2, "analyze_emoji");
2127        assert_eq!(args2["emoji"], "🚀💫⭐");
2128
2129        let (name3, args3) = extract_name_and_args(result[2].clone());
2130        assert_eq!(name3, "process_unicode");
2131        assert_eq!(args3["data"], "café naïve résumé");
2132    }
2133
2134    #[tokio::test]
2135    async fn test_parallel_json_escaping_and_quotes() {
2136        // Test that complex JSON with escaping doesn't crash the parser
2137        // We don't validate the exact escaped content, just that parsing succeeds
2138        let input = r#"<TOOLCALL>[
2139    {"name": "process_json", "arguments": {"json_string": "{\"key\": \"value with \\\"quotes\\\"\"}", "format": "strict"}},
2140    {"name": "handle_paths", "arguments": {"windows_path": "C:\\Users\\Test\\Documents\\file.txt", "unix_path": "/home/user/file.txt"}},
2141    {"name": "regex_pattern", "arguments": {"pattern": "\\d{3}-\\d{3}-\\d{4}", "test_string": "Phone: 123-456-7890"}}
2142]</TOOLCALL>"#;
2143
2144        let (result, _) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
2145            .await
2146            .unwrap();
2147
2148        // Just verify parsing succeeds and we get the expected number of tool calls
2149        assert_eq!(result.len(), 3);
2150
2151        // Verify function names are correct
2152        let (name1, _args1) = extract_name_and_args(result[0].clone());
2153        assert_eq!(name1, "process_json");
2154
2155        let (name2, _args2) = extract_name_and_args(result[1].clone());
2156        assert_eq!(name2, "handle_paths");
2157
2158        let (name3, _args3) = extract_name_and_args(result[2].clone());
2159        assert_eq!(name3, "regex_pattern");
2160    }
2161
2162    #[tokio::test]
2163    async fn test_parallel_mixed_argument_types() {
2164        let input = r#"<TOOLCALL>[
2165    {"name": "type_test", "arguments": {"string": "text", "number": 42, "float": 2.718281828459045, "boolean": true, "null_value": null}},
2166    {"name": "array_test", "arguments": {"empty_array": [], "string_array": ["a", "b", "c"], "mixed_array": [1, "two", true, null]}},
2167    {"name": "object_test", "arguments": {"empty_object": {}, "nested": {"level1": {"level2": {"value": "deep"}}}}}
2168]</TOOLCALL>"#;
2169
2170        let (result, _) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
2171            .await
2172            .unwrap();
2173
2174        assert_eq!(result.len(), 3);
2175
2176        // Validate different argument types are preserved
2177        let (name1, args1) = extract_name_and_args(result[0].clone());
2178        assert_eq!(name1, "type_test");
2179        assert_eq!(args1["string"], "text");
2180        assert_eq!(args1["number"], 42);
2181        assert_eq!(args1["float"], std::f64::consts::E);
2182        assert_eq!(args1["boolean"], true);
2183        assert!(args1["null_value"].is_null());
2184
2185        let (name2, args2) = extract_name_and_args(result[1].clone());
2186        assert_eq!(name2, "array_test");
2187        assert!(args2["empty_array"].is_array());
2188        assert_eq!(args2["empty_array"].as_array().unwrap().len(), 0);
2189        assert_eq!(args2["string_array"].as_array().unwrap().len(), 3);
2190        assert_eq!(args2["mixed_array"].as_array().unwrap().len(), 4);
2191
2192        let (name3, args3) = extract_name_and_args(result[2].clone());
2193        assert_eq!(name3, "object_test");
2194        assert!(args3["empty_object"].is_object());
2195        assert_eq!(args3["nested"]["level1"]["level2"]["value"], "deep");
2196    }
2197
2198    #[tokio::test]
2199    async fn test_parallel_whitespace_variations() {
2200        // Test with various whitespace patterns
2201        let input = r#"<TOOLCALL>[
2202    {
2203        "name": "spaced_function",
2204        "arguments": {
2205            "param1": "value1",
2206            "param2": "value2"
2207        }
2208    },
2209    {"name":"compact_function","arguments":{"param":"value"}},
2210    {
2211      "name"  :  "weird_spacing",
2212      "arguments"  :  {
2213        "key"  :  "value"
2214      }
2215    }
2216]</TOOLCALL>"#;
2217
2218        let (result, _) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
2219            .await
2220            .unwrap();
2221
2222        assert_eq!(result.len(), 3);
2223        validate_openai_compatibility(&result);
2224
2225        // All should parse correctly despite different whitespace
2226        let names: Vec<_> = result.iter().map(|tc| &tc.function.name).collect();
2227        assert!(names.contains(&&"spaced_function".to_string()));
2228        assert!(names.contains(&&"compact_function".to_string()));
2229        assert!(names.contains(&&"weird_spacing".to_string()));
2230    }
2231
2232    #[tokio::test]
2233    async fn test_parallel_cross_parser_compatibility() {
2234        // Test the same parallel tool calls across different parsers
2235        let base_calls = r#"[
2236    {"name": "get_weather", "arguments": {"city": "Dallas", "unit": "fahrenheit"}},
2237    {"name": "get_weather", "arguments": {"city": "Orlando", "unit": "fahrenheit"}}
2238]"#;
2239
2240        // Test with different parser formats
2241        let test_cases = vec![
2242            (
2243                format!("<TOOLCALL>{}</TOOLCALL>", base_calls),
2244                "nemotron_deci",
2245            ),
2246            (
2247                format!("[TOOL_CALLS]{}[/TOOL_CALLS]", base_calls),
2248                "mistral",
2249            ),
2250            (base_calls.to_string(), "mistral"), // Raw JSON
2251        ];
2252
2253        for (input, parser) in test_cases {
2254            let (result, _) = detect_and_parse_tool_call(&input, Some(parser))
2255                .await
2256                .unwrap_or_else(|e| panic!("Failed to parse with {}: {}", parser, e));
2257            assert_eq!(
2258                result.len(),
2259                2,
2260                "Parser {} should produce 2 tool calls",
2261                parser
2262            );
2263
2264            for tool_call in &result {
2265                assert_eq!(tool_call.function.name, "get_weather");
2266                let args: serde_json::Value =
2267                    serde_json::from_str(&tool_call.function.arguments).unwrap();
2268                assert!(args["city"].is_string());
2269                assert_eq!(args["unit"], "fahrenheit");
2270            }
2271        }
2272    }
2273
2274    #[tokio::test]
2275    async fn test_parallel_boundary_conditions() {
2276        // Test with exactly 1 tool call in array (boundary between single and parallel)
2277        let input_single = r#"<TOOLCALL>[
2278    {"name": "single_call", "arguments": {"test": true}}
2279]</TOOLCALL>"#;
2280
2281        let (result, _) = detect_and_parse_tool_call(input_single, Some("nemotron_deci"))
2282            .await
2283            .unwrap();
2284
2285        assert_eq!(result.len(), 1);
2286        assert_eq!(result[0].function.name, "single_call");
2287
2288        // Test with maximum reasonable number of parallel calls
2289        let mut many_calls = Vec::new();
2290        for i in 0..50 {
2291            many_calls.push(format!(
2292                r#"{{"name": "call_{}", "arguments": {{"index": {}}}}}"#,
2293                i, i
2294            ));
2295        }
2296
2297        let input_many = format!("<TOOLCALL>[{}]</TOOLCALL>", many_calls.join(","));
2298
2299        let (result, _) = detect_and_parse_tool_call(&input_many, Some("nemotron_deci"))
2300            .await
2301            .unwrap();
2302
2303        assert_eq!(result.len(), 50);
2304        validate_tool_call_ids(&result);
2305
2306        // Verify all calls are present and correctly indexed
2307        for (i, tool_call) in result.iter().enumerate() {
2308            assert_eq!(tool_call.function.name, format!("call_{}", i));
2309            let args: serde_json::Value =
2310                serde_json::from_str(&tool_call.function.arguments).unwrap();
2311            assert_eq!(args["index"], i);
2312        }
2313    }
2314
2315    #[tokio::test]
2316    async fn test_parallel_malformed_recovery() {
2317        // Test parser's ability to recover from malformed entries
2318        let input = r#"<TOOLCALL>[
2319    {"name": "good_call_1", "arguments": {"param": "value1"}},
2320    {"malformed": "missing_name_and_arguments"},
2321    {"name": "good_call_2", "arguments": {"param": "value2"}},
2322    {"name": "missing_args"},
2323    {"name": "good_call_3", "arguments": {"param": "value3"}},
2324    "completely_invalid_json",
2325    {"name": "good_call_4", "arguments": {"param": "value4"}}
2326]</TOOLCALL>"#;
2327
2328        let (result, _) = detect_and_parse_tool_call(input, Some("nemotron_deci"))
2329            .await
2330            .unwrap();
2331
2332        // Should recover and parse the valid entries
2333        assert!(
2334            !result.is_empty(),
2335            "Should parse at least some valid tool calls"
2336        );
2337
2338        // Count valid tool calls that were successfully parsed
2339        let valid_calls: Vec<_> = result
2340            .iter()
2341            .filter(|tc| tc.function.name.starts_with("good_call"))
2342            .collect();
2343
2344        assert!(
2345            valid_calls.len() >= 2,
2346            "Should parse at least 2 valid tool calls"
2347        );
2348
2349        // Verify the valid ones are correct
2350        for tool_call in valid_calls {
2351            assert!(tool_call.function.name.starts_with("good_call"));
2352            let args: serde_json::Value =
2353                serde_json::from_str(&tool_call.function.arguments).unwrap();
2354            assert!(args["param"].is_string());
2355        }
2356    }
2357}
2358
2359#[cfg(test)]
2360// Just e2e tests to test the flow. Detailed tests are covered in the individual parsers
2361mod detect_parser_tests {
2362    use super::*;
2363
2364    #[test]
2365    fn test_e2e_detect_tool_call_start_harmony() {
2366        let text = r#"<|start|>assistant<|channel|>commentary to=functions.get_current_weather <|constrain|>json"#;
2367        let result = detect_tool_call_start(text, Some("harmony")).unwrap();
2368        assert!(result);
2369    }
2370
2371    #[test]
2372    fn test_e2e_detect_tool_call_start_hermes() {
2373        let text = r#"{"name": "get_current_weather", "parameters": {"location": "Tokyo"}}"#;
2374        let result = detect_tool_call_start(text, Some("hermes")).unwrap();
2375        assert!(result);
2376    }
2377
2378    #[test]
2379    fn test_e2e_detect_tool_call_start_pythonic() {
2380        let text = r#"foo(a=1, b=2), bar(x=3)]"#;
2381        let result = detect_tool_call_start(text, Some("pythonic")).unwrap();
2382        assert!(!result);
2383    }
2384
2385    #[test]
2386    fn test_e2e_detect_tool_call_start_nemotron_deci() {
2387        let text = r#"<TOOLCALL>[{"name": "get_current_weather", "parameters": {"location": "Tokyo"}}]</TOOLCALL>"#;
2388        let result = detect_tool_call_start(text, Some("nemotron_deci")).unwrap();
2389        assert!(result);
2390    }
2391
2392    #[test]
2393    fn test_e2e_detect_tool_call_start_phi4() {
2394        let text =
2395            r#"functools{"name": "get_current_weather", "parameters": {"location": "Tokyo"}}"#;
2396        let result = detect_tool_call_start(text, Some("phi4")).unwrap();
2397        assert!(result);
2398    }
2399
2400    #[test]
2401    fn test_e2e_detect_tool_call_start_llama3_json() {
2402        let text = r#"<|python_tag|>{ "name": "get_current_weather", "parameters": {"location": "Tokyo"}}"#;
2403        let result = detect_tool_call_start(text, Some("llama3_json")).unwrap();
2404        assert!(result);
2405    }
2406
2407    #[test]
2408    fn test_e2e_detect_tool_call_start_mistral() {
2409        let text =
2410            r#"[TOOL_CALLS]{"name": "get_current_weather", "parameters": {"location": "Tokyo"}}"#;
2411        let result = detect_tool_call_start(text, Some("mistral")).unwrap();
2412        assert!(result);
2413    }
2414
2415    #[test]
2416    fn test_e2e_detect_incomplete_tool_call_start_deepseek_v3_1() {
2417        let text =
2418            r#"<|tool▁call▁begin|>get_current_weather{"location": "Tokyo"}<|tool▁call▁end|>"#;
2419        let result = detect_tool_call_start(text, Some("deepseek_v3_1")).unwrap();
2420        assert!(!result);
2421    }
2422
2423    #[test]
2424    fn test_e2e_detect_tool_call_start_deepseek_v3_1() {
2425        let text = r#"<|tool▁calls▁begin|><|tool▁call▁begin|>get_current_weather{"location": "Tokyo"}<|tool▁call▁end|>"#;
2426        let result = detect_tool_call_start(text, Some("deepseek_v3_1")).unwrap();
2427        assert!(result);
2428    }
2429}