Skip to content

fix(mimo): MiMo 2.5 thinking loop fix + Go gateway workaround (#36) - #82

Merged
ltmoerdani merged 4 commits into
mainfrom
fix/mimo-thinking-budget
Jul 23, 2026
Merged

fix(mimo): MiMo 2.5 thinking loop fix + Go gateway workaround (#36)#82
ltmoerdani merged 4 commits into
mainfrom
fix/mimo-thinking-budget

Conversation

@ltmoerdani

Copy link
Copy Markdown
Owner

Summary

Fix MiMo 2.5 infinite thinking loop and Go gateway reasoning_content leak that caused response text to disappear into the thinking panel.

Root cause

Two distinct problems:

  1. Go gateway bug #37635 — ALL streaming response text is placed in reasoning_content instead of content. When MiMo thinking is OFF, the answer text gets swallowed by the thinking panel.

  2. Model-level reasoning loop — MiMo 2.5 can enter a self-referential chain-of-thought loop, generating identical reasoning fragments indefinitely. DEFAULT_STREAM_IDLE_TIMEOUT_MS (2 min) doesn't fire because the stream stays active.

Fix (3 layers)

Layer File What
budget_tokens src/thinking.ts Cap reasoning tokens per effort: low=8K, medium=16K, high=32K
Go gateway workaround src/streaming.ts When Go gateway + NO reasoning_effort in body → reasoning_content emitted as visible text. When reasoning_effort IS present → CoT stays in thinking panel
Suffix-repetition detection src/streaming.ts Same 40-char suffix on 6+ consecutive chunks → suppress + emit warning

Regression fixes

  • Removed contentAfterReasoning guard (was false-positiving on all reasoning models)
  • Removed shouldSuppressTextEmit guard (was false-positiving on all reasoning models)
  • Reverted extractStreamParts delta block to main behavior

Debug logging

[go-gw] model=mimo-v2.5 hasReasoningEffort=false treatReasoningAsContent=true
[mimo] reasoning loop: suffix repeated 6x. Suppressing thinking parts.

Upstream

  • #37635 — gateway bug, still open
  • #35209 — extended thinking on simple prompts
  • #36354 — MiMo/DeepSeek tool-call errors

Files changed

File Change
src/thinking.ts budget_tokens per MiMo effort level
src/retry.ts HTTP 400 handler for budget_tokens rejection
src/streaming.ts treatReasoningAsContent conditional, shouldSuppressThinkingEmit(), suffix-repetition detection
docs/issues/36-20260723-mimo-thinking-infinite-loop.md Full issue documentation
CHANGELOG.md All entries consolidated into [0.4.2]
docs/devlog.md Session handoff updated

MiMo 2.5 / 2.5 Pro can enter an infinite reasoning loop when
reasoning_effort is set without a token budget cap. The model keeps
generating thinking tokens indefinitely, ignoring idle timeout (2 min)
since the stream stays active — user must wait up to 10 min total timeout.

Fix:
- src/thinking.ts: add budget_tokens per effort level (low=8K, medium=16K,
  high=32K) alongside reasoning_effort in the MiMo payload
- src/retry.ts: add handler for HTTP 400 'budget_tokens' rejection so the
  extension gracefully falls back to reasoning_effort-only if the gateway
  doesn't support the field
- docs/issues/36-20260723-mimo-thinking-infinite-loop.md: issue documentation

Closes #36
Moves the MiMo budget_tokens + Go gateway workaround entries from
[Unreleased] to the active [0.4.2] release section. Updates devlog
with full session handoff, issue #36 analysis, and web research
findings (upstream #37635, #35209, #36354). Updates issue doc with
deep-dive root cause linking the Go gateway bug.

Generated VSIX: opencode-copilot-chat-0.4.2.vsix (116 files, 1.07 MB)
Installed locally: ltmoerdani.opencode-copilot-chat@0.4.2
MiMo model-level reasoning loop still occurs even with budget_tokens
cap and treatReasoningAsContent workaround — model generates identical
fragments indefinitely until budget runs out.

Add two guards in OpenAiResponseExtractor:
- Char budget: suppress if >2000 reasoning chars emitted as visible
  text without a content field appearing
- Suffix repetition: suppress if same 40-char suffix repeats across
  6+ consecutive reasoning chunks

When triggered, a visible warning is emitted: '[MiMo seems stuck in a
reasoning loop — output suppressed]' and further reasoning chunks are
silently dropped until the stream ends (via budget_tokens).

Rebuilt + reinstalled VSIX 0.4.2.
…#36)

Root cause was two distinct problems:
1. Go gateway bug #37635 — ALL streaming output goes into
   reasoning_content instead of content. When MiMo thinking is OFF,
   the answer text gets swallowed by the thinking panel.
2. Model-level reasoning loop — MiMo 2.5 stuck in self-referential
   CoT, generating same fragments indefinitely.

Three-layer fix applied:
- budget_tokens cap per effort level (8K/16K/32K) in thinking.ts
- treatReasoningAsContent workaround: ONLY activates when Go gateway
  AND reasoning_effort is NOT in body (MiMo thinking OFF). When
  thinking IS on, reasoning_content is genuine CoT → thinking panel.
- Suffix-repetition detection in handleReasoning(): same 40-char
  suffix 6+ times → suppress thinking parts + emit warning.

Key regression fixes in this commit:
- Removed contentAfterReasoning guard (DeepSeek/GLM/Kimi legitimately
  use reasoning_content then content — this is normal, not degradation)
- Removed shouldSuppressTextEmit (false-positived on all reasoning
  models, not just MiMo)
- Reverted extractStreamParts delta block to main behavior

Upstream issue #37635 still open (assigned MrMushrooooom).
Workaround can be removed when gateway bug is fixed server-side.
@ltmoerdani
ltmoerdani merged commit 9d48853 into main Jul 23, 2026
2 checks passed
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.

1 participant