Skip to content

fix(kimi): omit explicit empty assistant history on native Responses - #6931

Closed
yuanyuanlove wants to merge 2 commits into
lidge-jun:devfrom
yuanyuanlove:bugfix-20261011-kimi-empty-assistant
Closed

yuanyuanlove wants to merge 2 commits into
lidge-jun:devfrom
yuanyuanlove:bugfix-20261011-kimi-empty-assistant

Conversation

@yuanyuanlove

@yuanyuanlove yuanyuanlove commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Fix the native Kimi Code Responses request compiler for explicit content-empty assistant history. The observed rejection was Invalid request: the message at position 585 with role 'assistant' must not be empty on a native Responses → Responses request, so a Chat-only fix would miss the affected wire.
  • Only final outgoing input for the exact api.kimi.com/coding/v1/responses host/path omits assistant messages whose content is explicitly "", [], or a list of empty text parts. Do not trim whitespace, invent answer text, rewrite the client's saved history, add retries, or change other Responses destinations. Keep refusal/multimodal/unknown content, nonempty annotations and extension fields, tools and reasoning carriers. Empty messages with same-input stored-item references are retained to avoid creating dangling references.
  • Close the corresponding translated Chat guard's empty-string gap without dropping serialized reasoning or tool calls; nonempty text and whitespace remain unchanged. Tool-call/result ordering and raw request immutability have regression coverage.
  • Separate from native reasoning/cipher replay and Sol hosted-search metadata projection. No provider default, protocol preference, model routing, credential, dependency, diagnostic, deployment or retry changes are included.
  • The source item at position 585 was not captured; regressions use synthetic empty-string/empty-array/empty-text cases and an offline strict upstream model, not a published user conversation. This PR fixes those explicit empty shapes; it does not assert that the original long live conversation or every Kimi compatibility problem has been recovered.

Verification

  • Fetched and merged upstream dev commit 9b6f4b2632492b3df637dccc009d6a01ed47c85d without conflicts (no rebase). Incoming upstream commits did not change these runtime sites. The PR diff is nine files; no unrelated runtime overlay is included.

  • Test-first development of the original empty shapes: 7 pass / 7 fail before implementation, then 18 pass / 0 fail. The added referenced-item regression independently failed before its guard: 19 pass / 1 fail; after the fix all 20 synthetic behavior cases pass, including explicit empty shapes, the synthetic 585th-message rejection, references, multimodal/refusal/unknown content, whitespace, native destination exclusions, tool/reasoning carriers and immutable caller input.

  • Final merged tree, pinned Bun 1.4.0: bun test --isolate --parallel=4 --timeout=15000 --changed=origin/dev ./tests/responses ./tests/adapters/openai: 4,133 pass / 1 skip / 0 fail, 165 files and 226,456 assertions. This is bounded import-connected adapter/Responses coverage, not the full repository suite. Test timeout is a runner setting, not a production timeout change. An earlier development batch at a 5-second test budget had timeout/handoff failures; its failed file passed independently on both candidate and unchanged baseline. Those old failures are recorded, not asserted to be baseline source defects; both this final batch and the preceding merged batch at a 15-second budget completed without failures.

  • bun test --isolate tests/adapters/openai/openai-empty-assistant-history.test.ts tests/test-layout.test.ts tests/test-layout-tooling.test.ts tests/ci-workflows/file-size-ratchet.test.ts tests/ci-workflows/structure-ssot.test.ts: 99 pass / 0 fail, 826 assertions. Subsequently only the new test's index placement moved next to existing OpenAI entries to avoid shared hunks with the separate Sol PR; the three index/layout/ratchet files passed again (28 pass / 0 fail, 643 assertions). Runtime and behavior tests were unchanged by that placement edit.

  • Final bun x --no-install tsc --noEmit, bun run structure:check, bun run privacy:scan: passed. English/Chinese docs build passed with existing compatible dependencies and isolated output/cache: 569 pages and 79,288 internal links checked; no install or lockfile change.

  • git diff --check origin/dev and git diff --cached --check origin/dev: passed for the proposed PR. As in upstream itself, comparing the merge to the old HEAD reports an unrelated existing blank line at EOF in tests/providers/github-copilot/github-copilot-auto-runtime.test.ts; that file is unchanged relative to the base and not cleaned up in this PR.

  • Independent read-only review raised the reference-preservation case, now fixed and covered; no implementation blocker remained. The runtime boundary is reviewed on the merged tree, not assumed from a pre-merge test result.

  • Full repository suite, live Kimi long-history acceptance and cross-platform validation remain outstanding. The PR is submitted as draft, not as a full-CI/review-readiness attestation. No real request body, ciphertext, token or private capture is committed.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (No credential/auth/workflow/dependency changes; synthetic fixtures only.)

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • Required local validation passed; commands, results, and any full-suite exception are documented.

  • I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

@coderabbitai

coderabbitai Bot commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Oct 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ Required local validation passed; commands, results, and any full-suite exception are documented.
  • ⬜ I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This PR stays in draft until every box above is ticked.

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source review of b49d4f6b5824a63998f1b82ee4cadb17c2a9f79d against trusted dev 9b6f4b2632492b3df637dccc009d6a01ed47c85d (nine files).

The Responses filter is narrow: exact Kimi Coding host/path, explicit empty message content, an allowlist of harmless message/text metadata, and same-input reference preservation. Unknown fields/parts, nonempty annotations, whitespace, refusals and independent reasoning/tool carriers are not treated as empty. It returns a detached input only when needed and leaves caller history unchanged. In translated Chat the check occurs before flushing pending calls; it drops an empty joined answer only when neither tool calls nor serialized reasoning remain. That matters for the tool-result pairing and reasoning-only cases.

I read the complete 20-case fixture and the production/doc/layout changes. No concrete source-level blocker found in this scope. The position-585 case is correctly labeled synthetic: it demonstrates the stated rejection shape with a strict offline fixture, not reproduction of the reporter's original conversation or a real Kimi response.

Independent execution is not completed. I prepared an exact-head ubuntu-owned worktree and a fresh private-home, CPU75% / memoryHigh1280MiB / max1536MiB / swap0 / tasks64 / finite-deadline unit. Its admission guard waited 60 seconds for foreign Cargo/rustc work and exited 75 before Bun started. This is neither a failing regression nor a passing test receipt; I did not overlap the foreign build or raise limits. The 20-case and broader receipts in the description remain yours, not mine.

Please retain Draft until readiness/required review and current-head CI complete. Cross-platform 38113761756, Catalog 38113761789 and React Doctor 38113761780 are action_required; target and hygiene success alone are not runtime validation. No live Kimi/provider/native-client call, scan, workflow authorization, sponsorship or contributor-branch modification was performed. This review is COMMENTED, not APPROVED.

@lidge-jun

Copy link
Copy Markdown
Owner

Carried into #6947 with credit to you (Co-authored-by trailers in the commit and description). The Responses filter for api.kimi.com/coding/v1/responses is unchanged from this PR. The Chat guard is narrower: independent review found that dropping every empty answer on all openai-chat providers would put two user turns side by side on strict-alternation templates such as Mistral's. #6947 drops an empty turn only while tool calls await their results, which keeps your call/result pairing fix, and keeps a standalone blank answer as content: "". The maintainer will close this PR once #6947 merges. Thank you for the precise fixtures.

lidge-jun added a commit that referenced this pull request Oct 11, 2026
…(carry #6931) (#6947)

Kimi Code's native Responses endpoint rejects a content-empty assistant
message ("the message at position N with role 'assistant' must not be
empty"). For the exact api.kimi.com/coding/v1/responses destination, the
outgoing input now drops assistant messages whose content is "" or only
empty text parts, keeping any message referenced by a same-input
item_reference and any message with unknown fields.

On the translated Chat wire, an empty assistant turn between a tool call
and its result no longer splits the pair: it used to close the pending
call with a synthetic "no tool result was recorded" message and turn the
real result into an orphan. A standalone empty answer keeps its slot so
chat templates with strict role alternation still accept the history.

Co-authored-by: panyuanyuan <panyuanyuan@hetao101.com>
Co-authored-by: yuanyuanlove <yuanyuanlove@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Carried into #6947 and merged to dev as c3a4cdf, with Co-authored-by credit. The Chat-side guard is narrower than in this PR: dropping every empty assistant turn would break strict user/assistant alternation, so a standalone empty answer keeps its slot. Thank you.

@lidge-jun lidge-jun closed this Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants