sglang_v0.5.2/sglang/sgl-router/tests/tool_parser_deepseek.rs

184 lines
5.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! DeepSeek V3 Parser Integration Tests
use sglang_router_rs::tool_parser::{DeepSeekParser, ParseState, StreamResult, ToolParser};
#[tokio::test]
async fn test_deepseek_complete_parsing() {
let parser = DeepSeekParser::new();
// Test single tool call
let input = r#"Let me help you with that.
<tool▁calls▁begin><tool▁call▁begin>function<tool▁sep>get_weather
```json
{"location": "Tokyo", "units": "celsius"}
```<tool▁call▁end><tool▁calls▁end>
The weather in Tokyo is..."#;
let result = parser.parse_complete(input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "get_weather");
// Verify arguments
let args: serde_json::Value = serde_json::from_str(&result[0].function.arguments).unwrap();
assert_eq!(args["location"], "Tokyo");
assert_eq!(args["units"], "celsius");
}
#[tokio::test]
async fn test_deepseek_multiple_tools() {
let parser = DeepSeekParser::new();
let input = r#"<tool▁calls▁begin>
<tool▁call▁begin>function<tool▁sep>search
```json
{"query": "rust programming"}
```<tool▁call▁end>
<tool▁call▁begin>function<tool▁sep>translate
```json
{"text": "Hello World", "to": "ja"}
```<tool▁call▁end>
<tool▁calls▁end>"#;
let result = parser.parse_complete(input).await.unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result[0].function.name, "search");
assert_eq!(result[1].function.name, "translate");
}
#[tokio::test]
async fn test_deepseek_streaming() {
let parser = DeepSeekParser::new();
let mut state = ParseState::new();
// Simulate streaming chunks
let chunks = vec![
"<tool▁calls▁begin><tool▁call▁begin>",
"function<tool▁sep>get_weather\n",
"```json\n",
r#"{"location": "#,
r#""Beijing", "#,
r#""units": "metric"}"#,
"\n```<tool▁call▁end><tool▁calls▁end>",
];
let mut found_name = false;
let mut found_complete = false;
for chunk in chunks {
let result = parser.parse_incremental(chunk, &mut state).await.unwrap();
match result {
StreamResult::ToolName { name, .. } => {
assert_eq!(name, "get_weather");
found_name = true;
}
StreamResult::ToolComplete(tool) => {
assert_eq!(tool.function.name, "get_weather");
found_complete = true;
}
_ => {}
}
}
assert!(found_name || found_complete);
}
#[tokio::test]
async fn test_deepseek_nested_json() {
let parser = DeepSeekParser::new();
let input = r#"<tool▁calls▁begin><tool▁call▁begin>function<tool▁sep>process
```json
{
"data": {
"nested": {
"deep": [1, 2, 3]
}
}
}
```<tool▁call▁end><tool▁calls▁end>"#;
let result = parser.parse_complete(input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "process");
let args: serde_json::Value = serde_json::from_str(&result[0].function.arguments).unwrap();
assert!(args["data"]["nested"]["deep"].is_array());
}
#[test]
fn test_deepseek_format_detection() {
let parser = DeepSeekParser::new();
// Should detect DeepSeek format
assert!(parser.detect_format("<tool▁calls▁begin>"));
assert!(parser.detect_format("text with <tool▁calls▁begin> marker"));
// Should not detect other formats
assert!(!parser.detect_format("[TOOL_CALLS]"));
assert!(!parser.detect_format("<tool_call>"));
assert!(!parser.detect_format("plain text"));
}
#[tokio::test]
async fn test_deepseek_malformed_json_handling() {
let parser = DeepSeekParser::new();
// Malformed JSON should be skipped
let input = r#"<tool▁calls▁begin>
<tool▁call▁begin>function<tool▁sep>broken
```json
{invalid json}
```<tool▁call▁end>
<tool▁call▁begin>function<tool▁sep>valid
```json
{"key": "value"}
```<tool▁call▁end>
<tool▁calls▁end>"#;
let result = parser.parse_complete(input).await.unwrap();
// Only the valid tool call should be parsed
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "valid");
}
#[tokio::test]
async fn test_normal_text_extraction() {
let parser = DeepSeekParser::new();
// Python extracts text before tool calls as normal_text
let input = r#"Let me help you with that.
<tool▁calls▁begin><tool▁call▁begin>function<tool▁sep>get_weather
```json
{"location": "Tokyo"}
```<tool▁call▁end><tool▁calls▁end>"#;
let result = parser.parse_complete(input).await.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].function.name, "get_weather");
// TODO: Verify normal text extraction when parser returns it
// In Python: normal_text = "Let me help you with that."
}
#[tokio::test]
async fn test_multiple_tool_calls() {
let parser = DeepSeekParser::new();
let input = r#"<tool▁calls▁begin>
<tool▁call▁begin>function<tool▁sep>get_weather
```json
{"location": "Tokyo"}
```<tool▁call▁end>
<tool▁call▁begin>function<tool▁sep>get_weather
```json
{"location": "Paris"}
```<tool▁call▁end>
<tool▁calls▁end><end▁of▁sentence>"#;
let result = parser.parse_complete(input).await.unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result[0].function.name, "get_weather");
assert_eq!(result[1].function.name, "get_weather");
}