Skip to content

server: enforce required DeepSeek tools in DSML markup - #1684

Merged
ashhart merged 2 commits into
ashhart:mainfrom
jayleaton:dsv41-up/dsml-tools
Oct 31, 2026
Merged

ashhart merged 2 commits into
ashhart:mainfrom
jayleaton:dsv41-up/dsml-tools

Conversation

@jayleaton

@jayleaton jayleaton commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

tool_choice: "required" and named tool calls compile a structural-tag grammar from the template's call envelope. DeepSeek-V4.x writes calls in DSML (<|DSML|invoke …>), and the grammar had no DSML style, so these requests could not be constrained on DeepSeek. This adds a deepseek style. It recognizes the native template's call envelope and emits structural tags with each offered tool's argument schema. Named-tool filtering and the parallel-call policy are kept. The grammar envelope is parsed in whole replies and in streamed prefixes.

Issue: none open upstream. Related: #1396 (stream and whole-reply agreement, including DSML whitespace), and #1665 (DeepSeek-V4.1 grammar masks on CUDA), which does not touch the tool-call grammar this PR changes. This PR does not change the stream filter that #1396 covers. Stacked on: nothing (based on main d2ad5a7f2).

Receipt

  • Environment, with the TensorFold commit, MLX or PyTorch version, machine and GPU, checkpoint and revision: head e9f668512bb2 on main d2ad5a7f2; Zig 0.17.0; host checks on a Linux aarch64 DGX Spark (GB10, CUDA 13.0). Real-pack boot on two GB10s, TP=2 over RoCE, checkpoint jayleaton/DeepSeek-V4.1-Flash-1017-EXL3-2.8bpw revision 859396591c34, TF_EXPERIMENTAL=deepseek.
  • Exactness: n/a; other styles are unchanged, and DeepSeek required calls go through the same grammar path as the other styles.
  • Decode speed before and after: n/a.
  • Prompt speed before and after: n/a.
  • Host tests (tool_grammar_deepseek_test.zig): the actual template, schema alternatives, named calls, typed arguments, and every streaming byte boundary of the envelope.
  • Tests run, with pass, fail and skip counts:
    zig fmt --check build.zig zig                      exit 0
    git diff --check d2ad5a7f2..HEAD                    exit 0
    python3 tools/zig/lean_check.py                     base 424 problems, head 424, new 0 (lean_new.py)
    zig build test-cuda-host --summary all              exit 0, 273/273 steps, 4081/4090 tests passed, 9 skipped (with test-server-cpu)
    zig build test-server-deepseek-chat-host test-tool-visibility-http-host test-golden  exit 0, 11/11 steps, 45/45 tests passed
    
  • Not run, and why: a device run with xgrammar on the real pack.
  • Overlap: atc overlap reports one-line additive conflicts with minimax_m3: a CUDA backend for MiniMax-M3.1-Flash at TP over GB10s, from the release's own formats #1388, minimax_m3: serve MiniMax-M3.1-Flash (428B-A23B MoE, images) natively on Metal #1235 and minimax_m3 (CUDA): draft several streams' DSpark blocks in one pass #1426 (MiniMax-M3), which each add another Style case and leads entry in tool_grammar.zig and reply_text.zig. Whichever lands second keeps both entries.

Checklist

  • Every token still goes through the lane rounds. No serial path, and nothing that needs drafts off.
  • Drafted output equals "draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (Not measured for this change alone.)
  • No precision traded for speed. If the bits change, the description says which sums change and why.
  • A new or changed GPU kernel is registered with the kernel contract (docs/kernel-contract.md); each backend's SUMMARY line is in the receipt. (No kernel changes.)
  • Prompt processing is no slower than the last release. (Server grammar only.)
  • The description says which platforms I ran, Metal M1 to M5 and CUDA, and which I could not.
  • New tests fail before the change, pass after it, and skip cleanly without their dependency. (No reverted-fix control was run for this PR.)
  • Comments and docstrings are one line. No measurements or history in the source.
  • No personal data, machine names, internal hosts or local paths. No AI attribution lines.

@jayleaton

Copy link
Copy Markdown
Contributor Author

Rebased onto main d2ad5a7f2 to resolve two conflicts with main's MiniMax-M3 tool calls. In reply_text.zig, main's Openers table and MiniMax block are kept, and the DSML function_calls opener is added after the existing openers, so the indices MiniMax and LFM2 use (calls[2]) are unchanged. In tool_grammar.zig, Style now has both minimax and deepseek; call() already handled both. The host gates were re-run on the new head and pass; the description has the new numbers.

jayleaton and others added 2 commits October 31, 2026 18:47
Recognize the native template call envelope and emit structural tags with each offered argument schema. Preserve named-tool filtering and parallel-call policy, and parse the grammar envelope through both whole replies and streamed prefixes.

Host tests cover the actual template, schema alternatives, named calls, typed arguments and every streaming byte boundary. Formatting, zero new lean identities and the complete CPU gate set pass against the pinned upstream baseline.

(cherry picked from commit 556665b)
@ashhart
ashhart force-pushed the dsv41-up/dsml-tools branch from e9f6685 to 0817c9e Compare October 31, 2026 18:47
@ashhart
ashhart merged commit 87726b0 into ashhart:main Oct 31, 2026
9 checks passed
@ashhart

ashhart commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Thank you @jayleaton. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes.

@ashhart

ashhart commented Nov 1, 2026

Copy link
Copy Markdown
Owner

Thanks @jayleaton! Required and named tool calls held to DeepSeek's DSML markup are in 1.2.1: https://github.com/ashhart/TensorFold/releases/tag/v1.2.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants