anomalyco / anomalyco/opencode

[Bug] Reasoning content mixed into `content` field and pushed word-by-word, causing `Thought` block spam in TUI

Open
#40,865 2 comments 2 reactions 1 assignee View on GitHub

@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

  1. Configure an OpenAI-compatible relay provider and connect a reasoning-capable model (e.g. deepseek-v4-flash)
  2. Send a complex question that triggers deep reasoning
  3. Observe the TUI: during the reasoning phase, a large number of single-line Thought indicators 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 content chunks: 2079
  • Total content characters: 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 /thinking toggle cannot hide these lines (it can only hide proper reasoning blocks; this content arrives via the content field, 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.