[router][tool parser] Modify tool parser to return both normal text and tool calls (non-stream) (#10995)

This commit is contained in:
Chang Su
2025-09-27 15:10:17 -07:00
committed by GitHub
parent f6bc3f529b
commit c1c8dd1dd0
30 changed files with 1467 additions and 934 deletions

View File

@@ -24,9 +24,9 @@ async fn test_openai_models_use_json() {
for model in models {
let parser = registry.get_parser(model).unwrap();
let test_input = r#"{"name": "test", "arguments": {}}"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "test");
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].function.name, "test");
}
}
@@ -38,8 +38,8 @@ async fn test_anthropic_models_use_json() {
for model in models {
let parser = registry.get_parser(model).unwrap();
let test_input = r#"{"name": "test", "arguments": {}}"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
}
}
@@ -51,9 +51,9 @@ async fn test_mistral_models() {
for model in models {
let parser = registry.get_parser(model).unwrap();
let test_input = r#"[TOOL_CALLS] [{"name": "test", "arguments": {}}]"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "test");
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].function.name, "test");
}
}
@@ -67,9 +67,9 @@ async fn test_qwen_models() {
let test_input = r#"<tool_call>
{"name": "test", "arguments": {}}
</tool_call>"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "test");
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].function.name, "test");
}
}
@@ -80,22 +80,22 @@ async fn test_llama_model_variants() {
// Llama 4 uses pythonic
let parser = registry.get_parser("llama-4-70b").unwrap();
let test_input = r#"[get_weather(city="NYC")]"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "get_weather");
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].function.name, "get_weather");
// Llama 3.2 uses python_tag
let parser = registry.get_parser("llama-3.2-8b").unwrap();
let test_input = r#"<|python_tag|>{"name": "test", "arguments": {}}"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "test");
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].function.name, "test");
// Other Llama models use JSON
let parser = registry.get_parser("llama-2-70b").unwrap();
let test_input = r#"{"name": "test", "arguments": {}}"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
}
#[tokio::test]
@@ -105,9 +105,9 @@ async fn test_deepseek_models() {
// DeepSeek uses pythonic format (simplified, v3 would need custom parser)
let parser = registry.get_parser("deepseek-coder").unwrap();
let test_input = r#"[function(arg="value")]"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "function");
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].function.name, "function");
}
#[tokio::test]
@@ -117,9 +117,9 @@ async fn test_unknown_model_fallback() {
// Unknown models should fall back to JSON parser
let parser = registry.get_parser("unknown-model-xyz").unwrap();
let test_input = r#"{"name": "fallback", "arguments": {}}"#;
let result = parser.parse_complete(test_input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "fallback");
let (_normal_text, tools) = parser.parse_complete(test_input).await.unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0].function.name, "fallback");
}
#[tokio::test]
@@ -181,10 +181,10 @@ The weather information has been requested."#,
for (model, output, expected_name) in test_cases {
let parser = registry.get_parser(model).unwrap();
let result = parser.parse_complete(output).await.unwrap();
assert!(!result.is_empty(), "No tools parsed for model {}", model);
let (_normal_text, tools) = parser.parse_complete(output).await.unwrap();
assert!(!tools.is_empty(), "No tools parsed for model {}", model);
assert_eq!(
result[0].function.name, expected_name,
tools[0].function.name, expected_name,
"Wrong function name for model {}",
model
);