anomalyco / anomalyco/opencode
[Bug] Reasoning content mixed into `content` field and pushed word-by-word, causing `Thought` block spam in TUI
@simonklee is already working on this.
Since Aug 6, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Environment
- opencode version: 1.18.x (Windows 11, PowerShell terminal)
- Model:
deepseek-v4-flash(accessed via an OpenAI-compatible interface) - Access method: third-party relay/proxy service (OpenAI-compatible protocol)
Problem description
When using a relay/proxy, the model's reasoning content is not placed in the standard reasoning_content field. Instead, it is mixed directly into the content field and pushed with extremely fine granularity (1-4 characters per chunk), word by word.
opencode renders the beginning of the content stream (the reasoning text before the actual answer) as a Thought block. Because the granularity is so fine, this produces a spam of one Thought line per chunk, causing the terminal to scroll uncontrollably.
Steps to reproduce
- Configure an OpenAI-compatible relay provider and connect a reasoning-capable model (e.g.
deepseek-v4-flash) - Send a complex question that triggers deep reasoning
- Observe the TUI: during the reasoning phase, a large number of single-line
Thoughtindicators scroll by rapidly, spamming the screen
SSE evidence (raw captured data)
1. reasoning_content is empty in every chunk
Out of 2081 streamed chunks captured from the same request, 0 chunks carried a non-empty reasoning_content field:
data: {"id":"xxx-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"","function_call":null,"refusal":"","tool_calls":[]},"finish_reason":""}],"usage":null}
2. Reasoning text is pushed word-by-word inside content
2079 non-empty content chunks, with the reasoning text cut into 1-4 character fragments:
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"这个","reasoning_content":"","..." }},"finish_reason":""}]}
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"问题","reasoning_content":"","..." }},"finish_reason":""}]}
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"涉及","reasoning_content":"","..." }},"finish_reason":""}]}
data: {"id":"xxx-...","choices":[{"index":0,"delta":{"content":"分布式","reasoning_content":"","..." }},"finish_reason":""}]}
...
3. Chunk length statistics
- Total chunks: 2081
- Chunks with non-empty
reasoning_content: 0 - Non-empty
contentchunks: 2079 - Total
contentcharacters: 4438 - Average chunk length: 2.1 characters
- 93% of chunks are ≤ 4 characters long
4. Structure of the concatenated content
After concatenating all 2079 chunks, the content reads as: a reasoning preamble ("I will build the solution via ...") followed by the full answer. There is no protocol-level separator between the reasoning section and the actual answer (no reasoning_content, no thinking field), so opencode cannot distinguish between them.
Expected behavior
Reasoning content should be delivered via a standard field (e.g. OpenAI's reasoning_content / Anthropic's thinking), or there should be a recognizable boundary between the reasoning text and the answer inside the content stream. opencode should not misclassify the leading text of an ordinary content stream as a reasoning block and render it per chunk.
Impact
- Terminal spam, making the conversation unreadable
- The
/thinkingtoggle cannot hide these lines (it can only hide proper reasoning blocks; this content arrives via thecontentfield, so hiding has no effect) - No way to keep the model's reasoning ability while removing the visual noise
Notes
The root cause is likely the relay service's non-compliant passthrough of the upstream reasoning stream. At the same time, opencode's recognition/rendering strategy for reasoning text inside the content stream could be more robust (e.g. coalescing rendering when detecting a burst of consecutive high-frequency short chunks). Responsibility attribution is up to the opencode maintainers.
Attachments
- Full SSE capture file:
wb_stream2.txt(681KB, 2081 chunks) - Analysis script:
analyze_stream.py(paths contain no service-provider information)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.