Skip to content

[Bugfix] Parse DSML tool calls when the model omits the tool_calls wrapper - #55954

Merged
khluu merged 3 commits into
vllm-project:mainfrom
sfeng33:fix/dsv4-missing-tool-calls-wrapper
Sep 9, 2026
Merged

khluu merged 3 commits into
vllm-project:mainfrom
sfeng33:fix/dsv4-missing-tool-calls-wrapper

Conversation

@sfeng33

@sfeng33 sfeng33 commented Sep 8, 2026 •

Copy link
Copy Markdown
Member

Purpose

Fixes #48931.

At long context DeepSeek-V4 intermittently omits the <|DSML|tool_calls> opener while still emitting a well-formed <|DSML|invoke ...> block. Detection anchored solely on the opener, so the whole block leaked to the client as assistant text and no tool call was produced.

Behaviour change: trailing text after a well-formed tool block was previously returned as content on both V4 and V3.2, and is now dropped.

Test Plan

pytest tests/parser/engine/test_deepseek_v4.py \
       tests/parser/engine/test_deepseek_v32.py \
       tests/tool_parsers/test_deepseekv4_tool_parser.py \
       tests/tool_parsers/test_deepseekv32_tool_parser.py \
       tests/parser/engine/test_parser_engine.py

Signed-off-by: sfeng33 <4florafeng@gmail.com>
@sfeng33
sfeng33 marked this pull request as ready for review September 8, 2026 18:25

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@mergify mergify Bot added deepseek Related to DeepSeek models tool-calling labels Sep 8, 2026
@mergify mergify Bot added the bug Something isn't working label Sep 8, 2026
Signed-off-by: sfeng33 <4florafeng@gmail.com>
Signed-off-by: sfeng33 <4florafeng@gmail.com>
@sfeng33

sfeng33 commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

/ci run

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #87770 for commit 9839565ace49.

@yewentao256 yewentao256 left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work! Will this work for case like ...<|DSML|invoke name="terminal">not a real call?

@sfeng33

sfeng33 commented Sep 8, 2026 •

Copy link
Copy Markdown
Member Author

Thanks for the work! Will this work for case like ...<|DSML|invoke name="terminal">not a real call?

When thinking is on, this part would be inside reasoning block and would not parsed as a tool call. If thinking is off, it'd be parsed as tool call. Not sure I understand what 'not a real call' is doing here, but any content after the tool call end marker should get dropped, this is to match the official dsv4 implementation.

@yewentao256

Copy link
Copy Markdown
Member

I meant incomplete invoke, not content after the end marker. This input has neither </|DSML|invoke> nor </|DSML|tool_calls>:

Documentation: <|DSML|invoke name="terminal">not a real call

@sfeng33

sfeng33 commented Sep 8, 2026 •

Copy link
Copy Markdown
Member Author

I meant incomplete invoke, not content after the end marker. This input has neither </|DSML|invoke> nor </|DSML|tool_calls>:

Documentation: <|DSML|invoke name="terminal">not a real call

I see, this would be parsed as 'terminal' being the tool call, as {} as its arg. 'Documentation: ' is the content.

@yewentao256 yewentao256 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okk, if we can accept without </|DSML|invoke> the tool call can still be used

@sfeng33
sfeng33 enabled auto-merge (squash) September 8, 2026 22:27
@github-actions github-actions Bot added the ready ONLY add when PR is ready to merge/full CI is needed label Sep 8, 2026
@khluu
khluu disabled auto-merge September 9, 2026 00:38
@khluu
khluu merged commit d98c8c0 into vllm-project:main Sep 9, 2026
63 of 68 checks passed
@sfeng33
sfeng33 deleted the fix/dsv4-missing-tool-calls-wrapper branch September 9, 2026 00:38
ItsRoy69 pushed a commit to ItsRoy69/vllm that referenced this pull request Sep 10, 2026
…apper (vllm-project#55954)

Signed-off-by: sfeng33 <4florafeng@gmail.com>
Signed-off-by: Jyotirmoy Roy <jyotirmoyroy649@gmail.com>
nandan2003 added a commit to nandan2003/vllm that referenced this pull request Sep 16, 2026
…m-project#56482)

PR vllm-project#55954 introduced recovery for tool calls when the model omits
the opening <|DSML|tool_calls> wrapper tag by adding an eager
transition (ParserState.CONTENT, "INVOKE_PREFIX") -> TOOL_NAME
emitting TOOL_CALL_START.

This eager transition caused several regressions:
1. Truncation / Ghost Calls: If model output ends before arguments
   or invoke closing tag, an empty-argument tool call ({}) was emitted.
2. Narration / Text Hijacking: Mentioning <|DSML|invoke name="..."> in
   prose or documentation triggered unintended tool calls and swallowed text.
3. Undeclared Tool Execution: Tool names not declared in the request
   (or requests with tools=None / tool_choice='none') were recovered as
   real tool calls.

Fix:
- Make (ParserState.CONTENT, "INVOKE_PREFIX") a provisional transition.
- Buffer semantic events and raw tokens in StreamingParserEngine during
  the recovery hold.
- Verify tool name against declared request tools when exiting TOOL_NAME.
- Only commit the tool call when (ParserState.TOOL_ARGS, "INVOKE_END")
  is reached with commit_provisional_tool_call=True.
- On any validation failure, truncation at finish(), or unexpected terminal,
  abort the recovery hold and flush the buffered raw tokens as plain content.

Fixes vllm-project#56482.

Signed-off-by: Nandan Vallamdasu <nandan.vallamdasu@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working deepseek Related to DeepSeek models ready ONLY add when PR is ready to merge/full CI is needed tool-calling

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug] DeepSeek-V4 tool-call parser leaks raw DSML into content when the model omits the <|DSML|tool_calls> START token (long context)

4 participants