MoonshotAI / MoonshotAI/kimi-code

Thinking text doubled when server mirrors reasoning deltas into both `reasoning_content` and `reasoning`

Open
#3,928 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
7.5k
Forks
1.2k
Avg merge
11h 53m
Merged PRs (30d)
350

Description

Summary

When an OpenAI-compatible server mirrors each reasoning delta into both delta.reasoning_content and delta.reasoning (identical text in the same chunk), the openai provider adapter appends both copies. Result: the thinking chain is rendered word-by-word doubled in the TUI, and the doubled text is also written into the session log.

This is a client-side handling gap, not a server bug: many harness-compatible servers deliberately emit reasoning twice (once per field name) so that clients reading either field work. A client that consumes both fields silently corrupts the reasoning display for every such endpoint.

Environment

  • Kimi Code CLI 2.0.1 (Windows 11)
  • Custom model pointing to an OpenAI-compatible server on the LAN (mlx-vlm 0.7.0, Server: mlx_vlm/0.7.0 header)
[providers."local-qwen"]
type = "openai"
base_url = "http://192.168.50.243:1919"
api_key = "test"

[models."qwen3.8-flash-next-4bit"]
provider = "local-qwen"
model = "models/Qwen3.8-Flash-Next-4bit"
max_context_size = 262144
capabilities = [ "thinking", "image_in", "tool_use" ]
support_efforts = [ "low", "medium", "xhigh" ]
default_effort = "xhigh"

What it looks like

The thinking panel renders every token twice:

TheThe user user greeted greeted me me in in Chinese Chinese,, so so I I should should respond respond with with a a brief brief greeting greeting in in return return..

The final answer text (content) is unaffected — only the reasoning chain is doubled.

Evidence

Raw SSE captured directly from the server with curl — the same delta text arrives under two field names in every chunk:

data: {"choices":[{"index":0,"delta":{"role":"assistant","content":null,
  "reasoning_content":"The","reasoning":"The", "...": "..."}}]}

data: {"choices":[{"index":0,"delta":{"role":"assistant","content":null,
  "reasoning_content":" user","reasoning":" user", "...": "..."}}]}

Kimi Code then records two content.part think entries per token in wire.jsonl, one per field:

{"type":"content.part",...,"part":{"type":"think","think":" greeted","reasoningKey":"reasoning_content"}}
{"type":"content.part",...,"part":{"type":"think","think":" greeted","reasoningKey":"reasoning"}}

In one test turn: 40 parts with "reasoningKey":"reasoning_content" + 40 identical parts with "reasoningKey":"reasoning" — the accumulated reasoning string is exactly twice its natural length.

Workaround tried: reasoning_key

Setting reasoning_key = "reasoning_content" on the model entry does not prevent the doubling: after pinning it, the session log still contained both keys (40 + 40). reasoning_key appears to add a non-standard field name to auto-detection rather than restrict the adapter to one field.

Expected / suggested behavior

When a chunk carries identical text in both reasoning_content and reasoning, the text should be consumed once. Any of:

  1. Dedupe identical delta content between the two fields (e.g. prefer reasoning_content when reasoning repeats it); or
  2. Make reasoning_key a hard pin — when set, read only that field; or
  3. Treat "both fields present and equal in the same chunk" as one occurrence at the accumulate/normalize layer, before display and context storage.

Impact

  • Thinking display is unreadable and twice as long.
  • Doubled reasoning is written into session context; if thinking is kept in context (thinking.keep), this also wastes tokens.

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.

Research direction

Start by tracing the openai provider adapter's handling of reasoning_content, reasoning, and the reasoning_key option, then follow where think parts enter the accumulation or normalization layer. Reproduce the paired SSE deltas and verify that identical reasoning is emitted once in the TUI and session log while content remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.