Collapse reasoning - #32148
Closed
BEEugene wants to merge 4 commits into
Closed
Conversation
…ee-shaking
Bun's bundler drops the side-effect-only `import "opentui-spinner/solid"`
when compiling the TUI. Confirmed by `extend({spinner` being absent from
the bundled binary while the `dots` (cli-spinners default) string was
present. Result: `[Reconciler] Unknown component type: spinner` at
runtime for any session that renders a Spinner.
Switch to a named import of SpinnerRenderable and an explicit
`extend({spinner})` call. Both bindings are used, so the bundler cannot
drop the registration.
The plugin loader already filters out failed plugins, so the report.error callback's `fail()` calls were producing noise without blocking anything. Convert all four stages (install, compatibility, entry, default) to `warn()` so the TUI surfaces the issue but doesn't make the process unstartable on a transient plugin error (e.g. a network blip on `git ls-remote`).
…es from text Some models (notably MiniMax-M3 with extended thinking, but also DeepSeek-R1, GLM-Z1, and other OpenAI-compatible reasoning providers) fragment a single line of reasoning across many short ReasoningPart chunks, sometimes interleaved with TextPart echoes of the same content. The TUI rendered this as dozens of "Thought: Xms" boxes plus a duplicate text paragraph in the assistant message. Root cause: certain providers stream the model's `reasoning_content` field as discrete reasoning events AND also echo the same text into the regular `content` field for back-compat. opencode's `SessionProcessor.handleEvent` correctly persists both as separate parts (`processor.ts:371-425`), but the TUI then renders them as duplicates. Dedupe at the TUI layer is the right place because not every provider does this, and the echoed text often contains the final response appended after the echo (we have to keep that tail). - Aggregate ALL reasoning parts into one block at the top of the message (not just consecutive ones; the model can interleave non-reasoning parts in between). - Strip `<think>`/`</mm:think>` tags individually from text parts. The opening/closing tags often end up split across the reasoning/text boundary, so a paired regex misses them. - Dedup on a normalized fingerprint (lowercase, whitespace + Unicode punctuation stripped) and use substring `includes` (not prefix/suffix only) so 4-part alternating streams A B A' B' are caught. - For text parts that begin with the merged reasoning, strip the echo prefix and keep only the tail (the actual response) using a character-level diff that skips whitespace/punctuation on both sides. A debug logging hook (`DEBUG_DEDUP_LOG = false` by default) writes the part sequence, fingerprints, and merge decisions to displayparts.log when enabled, kept for future regressions. Closes anomalyco#31999
Contributor
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
Contributor
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
Author
|
Split into 3 smaller focused PRs per CONTRIBUTING.md ("Keep pull requests small and focused"). See #NEW_SPINNER_URL, #NEW_PLUGIN_URL, #NEW_REASONING_URL. |
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix TUI rendering of fragmented reasoning from extended-thinking models
(notably MiniMax-M3, but the same pattern hits DeepSeek-R1, GLM-Z1, and
any OpenAI-compatible provider that streams both
reasoning_contentandechoes it in
content).Closes #31999
Root cause
Certain reasoning-capable providers (DeepSeek-style, MiniMax-M3,
GLM-Z1) stream the model's
reasoning_contentas discretereasoning-start/reasoning-delta/reasoning-endevents(
packages/opencode/src/session/processor.ts:371-425) AND also echothe same text into the regular
contentfield for back-compat. opencodecorrectly persists both as separate parts, but the TUI rendered them
as dozens of "Thought:" boxes plus a duplicate text paragraph in the
same message.
Dedupe at the TUI layer is the right place because:
(we have to preserve that tail, not just drop the text)
Changes (4 commits on
collapse-reasoning)chore: gitignore local build/log artifactsfix(tui): explicitly register spinner intrinsic to survive bundler tree-shaking— Bun.build drops side-effect-only
import "opentui-spinner/solid",was causing
[Reconciler] Unknown component type: spinnerat runtimefix(plugin): warn instead of fail on tui plugin loader errors— loader already filters failed plugins downstream;
fail()was just noisefix(tui): collapse fragmented reasoning parts and strip thinking echoes from text— main fix for Too many thought messages with MiniMax-M3 #31999, see commit body for full algorithm
Test plan
devbefore fix (verified locally)(the "text has new content not in reasoning" branch keeps them as-is)
bun turbo typecheck(29 packages) passed on local