fix: preserve thinking block signatures across three independent corruption paths#21860
fix: preserve thinking block signatures across three independent corruption paths#21860chan1103 wants to merge 1 commit intoanomalyco:devfrom
Conversation
|
The following comment was made by an LLM, it may be inaccurate: Based on my search, I found several related PRs addressing similar issues. Here are the most relevant ones: Directly Related (mentioned in the PR description):
Related to thinking block and reasoning content filtering:
The PR's description already acknowledges the relationship with #14393 and #12131, indicating these are complementary approaches to the same underlying issue. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Issue for this PR
Closes #13286
Related: #18078, #16748, #14393
Type of change
What does this PR do?
Extended thinking sessions fail intermittently with "thinking blocks cannot be modified." I traced the message pipeline and found three independent places where signatures get corrupted.
1.
differentModelguard strips reasoning metadata (message-v2.ts)When the current model differs from the stored message's model,
toModelMessagesdropsproviderMetadatafrom all parts — including reasoning parts that carry thinking block signatures. Common with plugin-based model routing.Fix: reasoning parts always pass their metadata through, bypassing the guard. Text/tool parts keep the guard. This is safe because provider metadata is namespaced (
anthropic: { signature }) — other providers ignore unknown keys.2.
trimEnd()mutates reasoning text (processor.ts)The
reasoning-endhandler trims trailing whitespace. The signature was computed against the original text, so any mutation invalidates it.Fix: remove the
trimEnd(). Reasoning text arrives final from the stream.3.
normalizeMessagesremoves signed empty parts (transform.ts)The Anthropic compat filter (c285304) removes parts where
text === "". This catchesredacted_thinkingblocks — empty text but valid signatures inproviderOptions. Removing them shifts positions, and Anthropic validates positionally (#16748).Fix: keep reasoning parts that have
providerOptions. Still remove truly empty ones.Each cause triggers independently under different conditions, which is why fixing just one doesn't eliminate the error. PR #14393 covers cause 1, PR #12131 covers cause 2 — neither covers cause 3, and this PR covers all three.
How did you verify your code works?
Added 4 tests across 3 files. Each test fails against unpatched code and passes with the fix:
message-v2.test.ts: reasoning metadata survives cross-model sessionsprocessor-effect.test.ts: trailing whitespace in reasoning text is preservedtransform.test.ts: signed empty reasoning parts survive filteringtransform.test.ts: unsigned empty parts still removed (existing behavior)Full suite: 1867 pass, 0 fail.
Screenshots / recordings
Not a UI change.
Checklist