#395·claurst

DeepSeek V4 DSML tool-calls are not parsed — raw markup leaks into assistant content and tools never execute

Author: 7jrxt42BxFZo4iAnN4CXCreated Aug 22, 2026Updated Aug 25, 2026

Environment

  • Claurst master @595b0eb (also present in v0.1.7 release)
  • Any route serving DeepSeek V4-family models over an OpenAI-compatible API: DeepSeek-V4-Flash/Pro direct, SiliconFlow, Chutes, OpenCode Zen

Problem

DeepSeek V4 models emit tool calls as tagged DSML envelopes inside plain delta.content SSE text instead of OpenAI-style tool_calls JSON:

<|DSML|tool_calls>
<|DSML|invoke name="question">
<|DSML|parameter name="questions" string="false">[{"q": "..."}]</|DSML|parameter>
</|DSML|invoke>
</|DSML|tool_calls>

Grammar notes: fullwidth vertical bars (, U+FF5C); an ASCII <|tool_call|> variant exists; multiple <|DSML|invoke> per envelope; string="true|false" selects raw-string vs JSON parameter encoding.

Claurst has no DSML handling, so the raw envelope is rendered to the user as assistant content and the tool never executes — the agent loop breaks exactly like it did in every other agent before they added parsers.

Verified absent at 595b0eb: zero hits for dsml / <|tool_call|> across api/src/providers/*, query/src/runner/stream.rs, tools/src/lib.rs, tui/src/app.rs; zero matching issues/PRs in this repo.

Steps to reproduce

  1. Point Claurst at a DeepSeek V4 model (e.g. deepseek-v4-flash on SiliconFlow, or a V4-derived preview via OpenCode Zen)
  2. Ask anything that requires a tool call
  3. Observe: envelope text appears verbatim in the transcript; no tool runs

Reference implementations (prior art)

Suggested fix scope

  1. Tagged-envelope parser (fullwidth + ASCII forms, nested invokes, string= attribute) routed into the existing tool-call pipeline
  2. Streaming text guard: buffer/suppress partial DSML fragments split across SSE chunk boundaries so protocol text never reaches the transcript mid-stream (plain text merely containing the prefixes must still pass through)
  3. Regression fixtures pinned to captured SiliconFlow/Zen streams (cf. Hmbown/CodeWhale#3431)

Note: DSML arrives as plain delta.content, so no provider wiring changes are needed — parser + stream guard are sufficient (same scope boundary ZeroClaw drew in zeroclaw-labs/zeroclaw#9723).

Happy to test a patch against a live V4 route.