Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
dynamo
Commits
e41fa1a0
Unverified
Commit
e41fa1a0
authored
Oct 16, 2025
by
Elyas Mehtabuddin
Committed by
GitHub
Oct 16, 2025
Browse files
fix: deepseek tool parsing fixed (#3557)
parent
f4cd71f3
Changes
3
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
327 additions
and
108 deletions
+327
-108
lib/llm/tests/parallel_tool_call_integration.rs
lib/llm/tests/parallel_tool_call_integration.rs
+96
-0
lib/parsers/src/tool_calling/config.rs
lib/parsers/src/tool_calling/config.rs
+10
-1
lib/parsers/src/tool_calling/json/deepseek_parser.rs
lib/parsers/src/tool_calling/json/deepseek_parser.rs
+221
-107
No files found.
lib/llm/tests/parallel_tool_call_integration.rs
View file @
e41fa1a0
...
...
@@ -381,3 +381,99 @@ async fn test_empty_tool_calls() {
);
assert_eq!
(
remaining_content
.unwrap
(),
content_without_tools
);
}
#[tokio::test]
async
fn
test_deepseek_v3_1_tool_call_parsing
()
{
let
response_content
=
r#"I'll help you understand this codebase. Let me start by exploring the structure and key
files to provide you with a comprehensive
explanation.<|tool▁calls▁begin|><|tool▁call▁begin|>TodoWrite<|tool▁sep|>{"todos":
[{"content": "Explore the root directory structure", "status": "in_progress", "activeForm":
"Exploring the root directory structure"}, {"content": "Examine package.json and
configuration files", "status": "pending", "activeForm": "Examining package.json and
configuration files"}, {"content": "Analyze source code structure and key modules",
"status": "pending", "activeForm": "Analyzing source code structure and key modules"},
{"content": "Identify main entry points and architectural patterns", "status": "pending",
"activeForm": "Identifying main entry points and architectural patterns"}, {"content":
"Summarize the codebase purpose and functionality", "status": "pending", "activeForm":
"Summarizing the codebase purpose and
functionality"}]}<|tool▁call▁end|><|tool▁calls▁end|>"#
;
// Debug: Print the content
println!
(
"Response content: {}"
,
response_content
);
println!
(
"Contains tool_calls_begin: {}"
,
response_content
.contains
(
"<|tool▁calls▁begin|>"
)
);
println!
(
"Contains tool_call_begin: {}"
,
response_content
.contains
(
"<|tool▁call▁begin|>"
)
);
// Parse the tool calls using the deepseek_v3_1 parser
let
(
tool_calls
,
remaining_content
)
=
detect_and_parse_tool_call
(
response_content
,
Some
(
"deepseek_v3_1"
))
.await
.expect
(
"Should successfully parse deepseek_v3_1 tool calls"
);
println!
(
"Number of tool calls parsed: {}"
,
tool_calls
.len
());
if
let
Some
(
ref
content
)
=
remaining_content
{
println!
(
"Remaining content: {}"
,
content
);
}
// Validate we got exactly 1 tool call
assert_eq!
(
tool_calls
.len
(),
1
,
"Should parse exactly 1 tool call"
);
// Validate remaining content (should be the explanatory text before the tool call)
assert
!
(
remaining_content
.is_some
());
let
remaining
=
remaining_content
.unwrap
();
assert
!
(
remaining
.contains
(
"I'll help you understand this codebase"
));
assert
!
(
remaining
.contains
(
"comprehensive"
));
// Validate the tool call
let
tool_call
=
&
tool_calls
[
0
];
assert_eq!
(
tool_call
.function.name
,
"TodoWrite"
);
// Validate OpenAI compatibility
assert
!
(
!
tool_call
.id
.is_empty
(),
"Tool call should have an ID"
);
assert_eq!
(
tool_call
.tp
,
ToolCallType
::
Function
);
// Parse and validate the arguments
let
args
:
serde_json
::
Value
=
serde_json
::
from_str
(
&
tool_call
.function.arguments
)
.expect
(
"Arguments should be valid JSON"
);
let
args_obj
=
args
.as_object
()
.expect
(
"Arguments should be an object"
);
// Check that todos array exists and has 5 items
assert
!
(
args_obj
.contains_key
(
"todos"
),
"Should have 'todos' key"
);
let
todos
=
args_obj
.get
(
"todos"
)
.unwrap
()
.as_array
()
.expect
(
"todos should be an array"
);
assert_eq!
(
todos
.len
(),
5
,
"Should have exactly 5 todo items"
);
// Validate first todo item
let
first_todo
=
&
todos
[
0
];
assert_eq!
(
first_todo
.get
(
"content"
)
.unwrap
()
.as_str
()
.unwrap
(),
"Explore the root directory structure"
);
assert_eq!
(
first_todo
.get
(
"status"
)
.unwrap
()
.as_str
()
.unwrap
(),
"in_progress"
);
assert_eq!
(
first_todo
.get
(
"activeForm"
)
.unwrap
()
.as_str
()
.unwrap
(),
"Exploring the root directory structure"
);
// Validate last todo item
let
last_todo
=
&
todos
[
4
];
assert_eq!
(
last_todo
.get
(
"content"
)
.unwrap
()
.as_str
()
.unwrap
(),
"Summarize the codebase purpose and functionality"
);
assert_eq!
(
last_todo
.get
(
"status"
)
.unwrap
()
.as_str
()
.unwrap
(),
"pending"
);
}
lib/parsers/src/tool_calling/config.rs
View file @
e41fa1a0
...
...
@@ -23,6 +23,10 @@ pub struct JsonParserConfig {
pub
tool_call_start_tokens
:
Vec
<
String
>
,
/// End token for individual tool calls (e.g., "</TOOLCALL>")
pub
tool_call_end_tokens
:
Vec
<
String
>
,
/// Separator tokens between function name and arguments
/// (e.g., "<|tool▁sep|>" for DeepSeek v3.1)
/// Used by some models to separate function name from arguments
pub
tool_call_separator_tokens
:
Vec
<
String
>
,
/// The key for the function name in the tool call
/// i.e. `{"name": "function", "arguments": {...}}` it would be
/// "name"
...
...
@@ -42,6 +46,7 @@ impl Default for JsonParserConfig {
Self
{
tool_call_start_tokens
:
vec!
[
"<TOOLCALL>"
.to_string
(),
"<|python_tag|>"
.to_string
()],
tool_call_end_tokens
:
vec!
[
"</TOOLCALL>"
.to_string
(),
""
.to_string
()],
tool_call_separator_tokens
:
vec!
[],
function_name_keys
:
vec!
[
"name"
.to_string
()],
arguments_keys
:
vec!
[
"arguments"
.to_string
(),
"parameters"
.to_string
()],
parser_type
:
JsonParserType
::
Basic
,
...
...
@@ -155,7 +160,11 @@ impl ToolCallConfig {
"<|tool▁calls▁begin|>"
.to_string
(),
"<|tool▁call▁begin|>"
.to_string
(),
],
tool_call_end_tokens
:
vec!
[
"<|tool▁calls▁end|>"
.to_string
()],
tool_call_end_tokens
:
vec!
[
"<|tool▁calls▁end|>"
.to_string
(),
"<|tool▁call▁end|>"
.to_string
(),
],
tool_call_separator_tokens
:
vec!
[
"<|tool▁sep|>"
.to_string
()],
parser_type
:
JsonParserType
::
DeepseekV31
,
..
Default
::
default
()
},
...
...
lib/parsers/src/tool_calling/json/deepseek_parser.rs
View file @
e41fa1a0
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment