Skip to content

Strip corrupted DSML markers from replies (catch-all on fullwidth pipe) - #74

Closed
ItszFinn wants to merge 3 commits into
AmanCode22:mainfrom
ItszFinn:fix/dsml-corrupted-marker-leak
Closed

ItszFinn wants to merge 3 commits into
AmanCode22:mainfrom
ItszFinn:fix/dsml-corrupted-marker-leak

Conversation

@ItszFinn

@ItszFinn ItszFinn commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The web model occasionally degenerates and emits DSML tool-call markers with a stray character between the fullwidth bars, e.g. <|触|DSML|| calls>, or floods bare </||DSML||> closers. These corrupted variants don't match the structured DSML regexes (which expect a clean ||DSML), so they leak straight into the reply text. Seen live via a chat client (a wall of </||DSML||> and <|触|DSML|| calls> in the assistant message).

The existing FIX 1–5 handling covers clean decorated/spaced/bare DSML, but not a marker with an unexpected character wedged between the bars.

Fix

A catch-all _FULLWIDTH_PIPE_TAG_RE = re.compile(r"<[^<>]*|[^<>]*>") that strips any <...> token containing a fullwidth pipe U+FF5C — which never appears in legitimate prose. It runs only in the prose/leftover paths (_strip_stray_markers, _strip_wrapper_tags), i.e. after tool extraction, so real tool calls are unaffected.

  • ASCII | is deliberately NOT matched (markdown tables, shell pipes, code stay intact).
  • The streaming holdback in StreamToolParser.feed also keeps an unclosed < tail that already contains a U+FF5C, so a corrupted marker split across SSE chunks isn't emitted as prose before the catch-all can strip it.

Tests

Added to tests/test_dsml_leaks.py: corrupted marker in prose, in a single stream chunk, split across 3-char chunks, and an assertion that ASCII | survives. All pass against the patched code.

Update: tool-call arguments too

The corrupted markers also leak inside tool-call argument values — e.g. a path parsed as shell/core/Theme.qml</||DSML|| >, which makes the tool call fail (file not found). Added _clean_tool_arg, applied in normalize_tool_call (all tool calls route through it), so every string argument gets the same fullwidth-pipe catch-all (recurses into dicts/lists; only strings containing U+FF5C are touched; ASCII | preserved). Covered by two more tests.

Update 2: never emit a bare command as message text

Separately, the model sometimes emitted a raw shell command / script as its normal message content (shown to the user, never executed) instead of a tool call — e.g. a bare cd … && python3 - <<'EOF' … EOF. The DSML format block alone did not rule this out, so TOOL_USE_INSTRUCTIONS now carries an explicit directive: to run a shell command, Python or any code the model MUST use a tool call and must never write a command/script/code block as message text. Prompt-level, so it also helps the streaming path.


🤖 Generated with Claude Code

ItszFinn and others added 3 commits October 25, 2026 11:08
The web model occasionally degenerates and emits DSML tool-call markers with a
stray character between the fullwidth bars, e.g. "<|触|DSML|| calls>", or
floods bare "</||DSML||>" closers. These variants don't match the structured
DSML regexes (which expect clean ||DSML), so they leak into the reply text
(seen live via a chat client).

Add a catch-all `_FULLWIDTH_PIPE_TAG_RE` that strips any `<...>` token containing
a fullwidth pipe U+FF5C — which never appears in legitimate prose — in the
prose/leftover paths (`_strip_stray_markers`, `_strip_wrapper_tags`), i.e. after
tool extraction. ASCII '|' is deliberately left untouched (tables, code). The
streaming holdback in `StreamToolParser.feed` also keeps an unclosed '<' tail
that already contains a U+FF5C, so a split corrupted marker is never emitted as
prose before the catch-all can strip it.

Tests cover the prose, streaming, split-across-chunks, and ASCII-pipe-preserved
cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The catch-all cleaned leaked DSML markers from reply prose, but corrupted
markers also land INSIDE tool-call argument values — e.g. a path parsed as
"shell/core/Theme.qml</||DSML|| >" — which makes the tool call fail (file not
found). Sanitize every string argument in `normalize_tool_call` (all tool calls
route through it) with the same fullwidth-pipe catch-all. Only strings that
actually contain U+FF5C are touched (recurses into dicts/lists), so normal
arguments and ASCII '|' are untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e text

The model occasionally emitted a raw shell command or script as its normal
message content (shown to the user but never executed) instead of a tool call —
e.g. a bare "cd … && python3 - <<'EOF' … EOF" delivered straight to the chat.
The DSML format block alone did not rule this out. Add an explicit directive:
to run a shell command, Python or any code the model MUST use a tool call, and
must never write a command/script/code block as message text. Prompt-level, so
it also helps the streaming path (changes what the model emits, not parsing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AmanCode22

Copy link
Copy Markdown
Owner

@ItszFinn kindly run ruff check also always with pytest as it's failing in your pr .

@merfiDEV merfiDEV mentioned this pull request Oct 29, 2026
@AmanCode22

Copy link
Copy Markdown
Owner

Merging #91 instead

@AmanCode22 AmanCode22 closed this Nov 1, 2026
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