Skip to content

fix(kimi): preserve reasoning_content in Anthropic adapter for /coding endpoint (salvage #13897)#14018

Merged
kshitijk4poor merged 3 commits into
mainfrom
salvage/kimi-reasoning-content
Apr 22, 2026
Merged

fix(kimi): preserve reasoning_content in Anthropic adapter for /coding endpoint (salvage #13897)#14018
kshitijk4poor merged 3 commits into
mainfrom
salvage/kimi-reasoning-content

Conversation

@kshitijk4poor

@kshitijk4poor kshitijk4poor commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator

Salvages #13897 by @HiddenPuppy onto current main, with critical follow-up fixes.

Problem

Kimi's /coding endpoint speaks Anthropic Messages protocol but enables thinking server-side. When replaying conversation history, it requires every assistant tool-call message to carry a thinking block — even when we don't send thinking.enabled in the request kwargs (PR #13826 correctly suppressed that). Without this, sessions break after tool calls with:

HTTP 400: thinking is enabled but reasoning_content is missing in assistant tool call message at index N

PR #13975 fixed the chat_completions path in run_agent.py (_copy_reasoning_content_for_api), but the anthropic_messages path (convert_messages_to_anthropic in anthropic_adapter.py) was still missing the reasoning_content → thinking block conversion.

Issue confirmed real: Brand-new gateway sessions (e.g. 20260422_011800_eea181) fail at message index 2 — the very first replay after an initial tool call. Not stale-session artifacts.

Cherry-picked from #13897

  • convert_messages_to_anthropic(): When an assistant message has reasoning_content, convert it to a {"type": "thinking", "thinking": ...} block in the Anthropic message format.

Follow-up fixes (found during self-review)

  1. Thinking block stripped by signature management (CRITICAL): The thinking block synthesised from reasoning_content was immediately stripped by the third-party signature-stripping code — Kimi is classified as _is_third_party_anthropic_endpoint. Added a Kimi-specific carve-out: preserve unsigned thinking blocks (which we synthesised from reasoning_content) while stripping Anthropic-signed blocks Kimi can't validate.

  2. Empty-string reasoning_content silently dropped: The truthiness check (if reasoning_content and ...) evaluates to False for "". But _copy_reasoning_content_for_api() deliberately injects "" as a tier-3 fallback for Kimi tool-call messages with no reasoning. Changed to isinstance(reasoning_content, str) so empty-string reasoning still produces a thinking block.

  3. Wrong block ordering: Thinking block was appended AFTER tool_use blocks. Anthropic protocol requires thinking → text → tool_use ordering. Changed to blocks.insert(0, ...).

  4. Duplicate thinking content on native Anthropic (CRITICAL): The reasoning_content → thinking block insertion ran for ALL endpoints, not just Kimi. On native Anthropic where reasoning_details already contributes signed thinking blocks, this created a second unsigned thinking block with the same text. The unsigned block would be downgraded to a spurious text block on the last assistant message — inflating context and potentially confusing the model. Added a guard: only insert the reasoning_content thinking block when no thinking blocks already exist from reasoning_details.

Scope verification

  • _is_kimi_coding_endpoint() is confirmed a strict subset of _is_third_party_anthropic_endpoint() — the if/elif ordering is correct
  • Non-Kimi third-party endpoints (MiniMax, etc.): reasoning_content thinking block gets inserted then stripped → zero behavior change
  • Native Anthropic with reasoning_details: duplicate guard prevents insertion → zero behavior change
  • Native Anthropic without reasoning_details (edge case — provider switch mid-session): unsigned thinking block inserted → downgraded to text on last assistant. Acceptable.

Files changed

  • agent/anthropic_adapter.py — reasoning_content → thinking block conversion + Kimi carve-out in signature stripping + duplicate guard
  • scripts/release.py — AUTHOR_MAP entries for @HiddenPuppy

Validation

  • 8/8 existing kimi thinking tests pass
  • 8/8 kimi build_api_kwargs tests pass
  • 23/23 total reasoning-related tests pass
  • 8 E2E validations: non-empty, empty-string, signed stripping, unsigned survival, non-Kimi third-party, no-reasoning_content, native Anthropic no-duplication, native Anthropic no-details edge case

Closes #13848. Closes #13897.

Jerome added 2 commits April 22, 2026 18:35
…call messages for Kimi /coding

Fixes #13848

Kimi's /coding endpoint speaks the Anthropic Messages protocol but has its
own thinking semantics: when thinking is enabled, Kimi validates message
history and requires every prior assistant tool-call message to carry
OpenAI-style reasoning_content.

The Anthropic path never populated that field, and
convert_messages_to_anthropic strips all Anthropic thinking blocks on
third-party endpoints — so the request failed with HTTP 400:
  "thinking is enabled but reasoning_content is missing in assistant
tool call message at index N"

Now, when an assistant message contains tool_calls and a
reasoning_content string, we append a {"type": "thinking", ...} block
to the Anthropic content so Kimi can validate the history.  This only
affects assistant messages with tool_calls + reasoning_content; plain
text assistant messages are unchanged.
Map tsuijinglei@gmail.com → hiddenpuppy.
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent loop, run_agent.py, prompt builder provider/kimi Kimi / Moonshot labels Apr 22, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #13897 (original PR being salvaged) and #13975 (chat_completions path fix). This covers the anthropic_messages path.

@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #13897 and #13975.

… block ordering

Follow-up to the cherry-picked PR #13897 fix. Three issues found:

1. CRITICAL: The thinking block synthesised from reasoning_content was
   immediately stripped by the third-party signature management code
   (Kimi is classified as _is_third_party_anthropic_endpoint). Added a
   Kimi-specific carve-out that preserves unsigned thinking blocks while
   still stripping Anthropic-signed blocks Kimi can't validate.

2. Empty-string reasoning_content was silently dropped because the
   truthiness check ('if reasoning_content and ...') evaluates to False
   for ''. Changed to 'isinstance(reasoning_content, str)' so the
   tier-3 fallback from _copy_reasoning_content_for_api (which injects
   '' for Kimi tool-call messages with no reasoning) actually produces
   a thinking block.

3. The thinking block was appended AFTER tool_use blocks. Anthropic
   protocol requires thinking -> text -> tool_use ordering. Changed to
   blocks.insert(0, ...) to prepend.
@kshitijk4poor kshitijk4poor force-pushed the salvage/kimi-reasoning-content branch from 319ddc9 to 123287d Compare April 22, 2026 15:05
@kshitijk4poor kshitijk4poor merged commit 04e039f into main Apr 22, 2026
11 of 12 checks passed
@kshitijk4poor kshitijk4poor deleted the salvage/kimi-reasoning-content branch April 22, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P1 High — major feature broken, no workaround provider/kimi Kimi / Moonshot type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 400 error for kimi-for-coding

2 participants