Summary
When DeepSeek V4 produces thinking tokens but no visible text (a "reasoning-only" turn), Gateway's reasoning-only retry with "visible-answer continuation" fails to trigger. Instead, the turn hits llm-idle-timeout and falls through to a blind retry that doesn't carry the "continue from partial turn" instruction, which is ineffective for recovering visible output.
Root Cause
The detection chain relies on isSignedThinkingBlock(), which requires specific signature fields on the thinking block:
function isSignedThinkingBlock(block) {
if (!isThinkingBlock(block)) return false;
return block.type === "redacted_thinking"
|| block.signature != null
|| block.thinkingSignature != null
|| block.thought_signature != null;
}
assessLastAssistantMessage() then uses isSignedThinkingBlock to classify the turn:
hasSignedThinking && !hasNonThinkingContent → "incomplete-text" ✅ triggers retry
hasUnsignedThinking → "incomplete-thinking" ❌ not caught by isReasoningOnlyAssistantTurn
function isReasoningOnlyAssistantTurn(message) {
return assessLastAssistantMessage(message) === "incomplete-text";
}
If DeepSeek V4 APIs return thinking blocks without signature, thinkingSignature, or thought_signature fields, the block is classified as "unsigned thinking". The turn gets "incomplete-thinking" — which is not matched by isReasoningOnlyAssistantTurn, so resolveReasoningOnlyRetryInstruction returns null and no continuation retry is attempted.
Instead, the run hits the default llm-idle-timeout (120s) and retries via sameModelIdleTimeoutRetry, which retries the same model but without the REASONING_ONLY_RETRY_INSTRUCTION ("Continue from that partial turn and produce the visible answer now...") — so the model restarts from scratch and may produce only thinking tokens again.
Affected Code
selection-BmjEdnnA.js — isSignedThinkingBlock(), assessLastAssistantMessage(), isReasoningOnlyAssistantTurn(), resolveReasoningOnlyRetryInstruction()
pi-embedded-CJ87lW5R.js — reasoning-only retry loop
Suggested Fix
Either:
-
Broaden isReasoningOnlyAssistantTurn to also match "incomplete-thinking" when the model is known to produce thinking blocks without signatures (e.g., DeepSeek V4 OpenAI-compatible API).
-
Or treat unsigned thinking blocks as signed for provider/model combinations where signatures are not expected — so assessLastAssistantMessage returns "incomplete-text" instead of "incomplete-thinking".
Related
Not the same as #75040 (which is about extra_body field collision WARN noise).
Summary
When DeepSeek V4 produces thinking tokens but no visible text (a "reasoning-only" turn), Gateway's reasoning-only retry with "visible-answer continuation" fails to trigger. Instead, the turn hits
llm-idle-timeoutand falls through to a blind retry that doesn't carry the "continue from partial turn" instruction, which is ineffective for recovering visible output.Root Cause
The detection chain relies on
isSignedThinkingBlock(), which requires specific signature fields on the thinking block:assessLastAssistantMessage()then usesisSignedThinkingBlockto classify the turn:hasSignedThinking && !hasNonThinkingContent→"incomplete-text"✅ triggers retryhasUnsignedThinking→"incomplete-thinking"❌ not caught byisReasoningOnlyAssistantTurnIf DeepSeek V4 APIs return thinking blocks without
signature,thinkingSignature, orthought_signaturefields, the block is classified as "unsigned thinking". The turn gets"incomplete-thinking"— which is not matched byisReasoningOnlyAssistantTurn, soresolveReasoningOnlyRetryInstructionreturnsnulland no continuation retry is attempted.Instead, the run hits the default
llm-idle-timeout(120s) and retries viasameModelIdleTimeoutRetry, which retries the same model but without the REASONING_ONLY_RETRY_INSTRUCTION ("Continue from that partial turn and produce the visible answer now...") — so the model restarts from scratch and may produce only thinking tokens again.Affected Code
selection-BmjEdnnA.js—isSignedThinkingBlock(),assessLastAssistantMessage(),isReasoningOnlyAssistantTurn(),resolveReasoningOnlyRetryInstruction()pi-embedded-CJ87lW5R.js— reasoning-only retry loopSuggested Fix
Either:
Broaden
isReasoningOnlyAssistantTurnto also match"incomplete-thinking"when the model is known to produce thinking blocks without signatures (e.g., DeepSeek V4 OpenAI-compatible API).Or treat unsigned thinking blocks as signed for provider/model combinations where signatures are not expected — so
assessLastAssistantMessagereturns"incomplete-text"instead of"incomplete-thinking".Related
Not the same as #75040 (which is about
extra_bodyfield collision WARN noise).