Skip to content

feat(kimi): forward prompt_cache_key on the Kimi Coding Plan presets - #597

Merged
Wibias merged 3 commits into
lidge-jun:devfrom
olddonkey:codex/kimi-prompt-cache-affinity
Jul 28, 2026
Merged

feat(kimi): forward prompt_cache_key on the Kimi Coding Plan presets#597
Wibias merged 3 commits into
lidge-jun:devfrom
olddonkey:codex/kimi-prompt-cache-affinity

Conversation

@olddonkey

@olddonkey olddonkey commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What

Opt the canonical Kimi Coding Plan presets (kimi OAuth and kimi-code API key, both targeting https://api.kimi.com/coding/v1) into the existing openai-chat promptCacheKey forwarding flag, so the request's stable prompt_cache_key reaches Kimi's Chat Completions body.

Why

Kimi's Chat Completions docs (https://platform.kimi.com/docs/api/chat) document prompt_cache_key as required to improve cache hit rates for Kimi Code Plan: a stable session/task id, unchanged across exit and resume. Codex already sends one on every /v1/responses request (and the Claude /v1/messages inbound derives a session-scoped one), but opencodex previously dropped it for Kimi because forwarding is deny-by-default — strict OpenAI-compatible backends reject the field.

How

  • src/providers/registry.ts: promptCacheKey: true on the kimi and kimi-code entries. The pre-existing machinery does the rest: registry→config scalar backfill (src/router.ts) and conditional forwarding in the openai-chat adapter, which only forwards a key already on the internal request — the adapter never invents one, and an explicit provider-level promptCacheKey: false still opts out.
  • src/providers/derive.ts: persist the flag through providerConfigSeed / enrichProviderFromRegistry, mirroring the sibling scalars (parallelToolCalls, modelSuffixBracketStrip). Without this, key-pool 429 rotation — which rebuilds the provider from the persisted config rather than the routed one — would silently drop the key on exactly the quota-sensitive retries that need affinity most.
  • Other Kimi-model presets (moonshot open platform, Fireworks-served) are intentionally left deny-by-default; field support there is unverified.
  • Docs: providers guide (all five locales) + structure/04_transports-and-sidecars.md.

Tests

  • tests/openai-chat-hardening.test.ts: both presets forward a caller-supplied key end-to-end through routeModelbuildRequest (config omits the flag, so this pins the registry backfill, not a config tautology); an explicit promptCacheKey: false still suppresses it.
  • tests/provider-registry-parity.test.ts: both registry entries carry the flag, and providerConfigSeed/enrichment persist it (the 429-rotation guarantee).
  • bun test green on the touched suites plus the kimi-adjacent sweep (hardening, parity, key-failover, payload, reasoning-effort, adapter-usage, inbound); tsc --noEmit clean.

Live verification

The implemented route was exercised against Kimi K3 through routeModel → the openai-chat adapter, with the only A/B variable being whether the provider forwarded the same caller-supplied key.

  • 26 paired two-turn trials per arm (104 live API calls total).
  • Coverage included pooled connections and requests that explicitly closed the client connection.
  • With the key: 26/26 follow-up cache hits.
  • Without the key: 26/26 follow-up cache hits.
  • The paired cached-token difference was exactly zero in all 26 pairs.
  • In the expanded 20-pair run, median follow-up latency was 3,984.5 ms with the key versus 4,070.5 ms without it; there were no retries, 429s, or 5xx responses.
  • Across both runs: 260,102 input tokens, 3,743 output tokens, and 118,784 cached tokens.

This verifies that Kimi accepts the field and that forwarding it introduced no observable cache or latency regression in the tested workload. It deliberately does not claim a short-run cache improvement: Kimi's automatic prefix cache hit every control follow-up too. The change is retained to satisfy Kimi Code Plan's documented stable session/task-key requirement and preserve affinity in deployment conditions not reproduced by this local A/B.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added prompt-cache affinity for Kimi Coding Plan by forwarding a caller-supplied prompt_cache_key to outbound chat requests (OAuth kimi and API-key kimi-code), deny-by-default for other providers.
    • Preserves prompt_cache_key across 429-driven key-rotation retries and honors provider-level opt-out (omits the field when disabled).
  • Documentation
    • Updated provider guides (EN/JA/KO/RU/ZH-CN) and added “Kimi Coding Plan prompt-cache affinity” guidance.
  • Tests
    • Expanded routing/opt-out coverage and added regression checks for 429 retry behavior.

Kimi's Chat Completions API documents prompt_cache_key as required for
Kimi Code Plan cache hits (a stable session/task id, unchanged across
exit and resume). Opt the canonical `kimi` OAuth and `kimi-code`
API-key presets into the existing openai-chat forwarding flag so the
caller-supplied key reaches https://api.kimi.com/coding/v1.

The adapter never invents a key: it forwards what the internal request
already carries (Codex's session key on /v1/responses, or the
session-scoped key the Claude /v1/messages inbound derives), an absent
field stays absent, and an explicit provider-level
`promptCacheKey: false` still opts out. All other OpenAI-compatible
presets remain deny-by-default because strict backends reject the
OpenAI-specific field.

Also persist the flag through providerConfigSeed /
enrichProviderFromRegistry like the sibling scalars
(parallelToolCalls, modelSuffixBracketStrip): key-pool 429 rotation
rebuilds the provider from the persisted config rather than the routed
one, so without seeding the retried request would silently drop the
key on exactly the quota-sensitive turns that need affinity most.

Evidence: https://platform.kimi.com/docs/api/chat

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the enhancement New feature or request label Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Kimi OAuth and API-key presets now preserve and forward caller-supplied prompt_cache_key values through provider configuration, Chat Completions requests, and 429 transport failover. Tests and multilingual documentation describe forwarding, opt-out, and keyless behavior.

Changes

Kimi prompt-cache affinity

Layer / File(s) Summary
Provider configuration propagation
src/providers/registry.ts, src/providers/derive.ts, tests/provider-registry-parity.test.ts
The kimi and kimi-code registry entries enable promptCacheKey, while seed, enrichment, and parity checks preserve the setting.
Request routing validation
tests/openai-chat-hardening.test.ts
Tests verify forwarding of prompt_cache_key when enabled and omission when explicitly disabled.
Failover preservation
src/providers/key-failover.ts, src/server/responses/core.ts, tests/key-failover.test.ts, tests/server-key-failover-e2e.test.ts
429 transport rotation preserves routed provider metadata, advances the API key, and keeps the prompt cache key on retry requests across server retry paths.
Behavior documentation
structure/04-transports-and-sidecars.md, docs-site/src/content/docs/*/guides/providers.md
Documentation describes Kimi forwarding, keyless requests, opt-out handling, upstream rejection behavior, and deny-by-default behavior for other providers.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: lidge-jun, ingwannu, wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: forwarding prompt_cache_key for the Kimi Coding Plan presets.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c16336a27a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/providers/registry.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs-site/src/content/docs/guides/providers.md`:
- Around line 93-96: Update the canonical Kimi Coding Plan preset documentation
for the kimi and kimi-code configurations to state that opencodex forwards only
a caller-supplied stable prompt_cache_key and never generates one. Explicitly
document that if the upstream provider rejects the field, the rest of the
request and configuration remain unchanged, and keep the wording synchronized
with the CLI/API behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2563ce11-1e53-4d27-bee2-414472a8fdc0

📥 Commits

Reviewing files that changed from the base of the PR and between e545512 and c16336a.

📒 Files selected for processing (10)
  • docs-site/src/content/docs/guides/providers.md
  • docs-site/src/content/docs/ja/guides/providers.md
  • docs-site/src/content/docs/ko/guides/providers.md
  • docs-site/src/content/docs/ru/guides/providers.md
  • docs-site/src/content/docs/zh-cn/guides/providers.md
  • src/providers/derive.ts
  • src/providers/registry.ts
  • structure/04_transports-and-sidecars.md
  • tests/openai-chat-hardening.test.ts
  • tests/provider-registry-parity.test.ts

Comment thread docs-site/src/content/docs/guides/providers.md Outdated
olddonkey and others added 2 commits July 27, 2026 22:00
Generalize the promptCacheKey preservation from the previous commit: the
narrow registry backfill in rotateProviderTransportOn429 covered only that
one scalar, while every OTHER registry backfill routedProviderConfig merges
at request time was still lost when the four 429-failover sites in
src/server/responses/core.ts assigned the persisted-config snapshot to
route.provider wholesale. Concretely: kimi-code's noTemperatureModels /
modelReasoningEfforts / modelSuffixBracketStrip merges, NVIDIA NIM's
parallelToolCalls: false, and a registry-pinned baseUrl all silently
reverted on the rotated retry and later continuations in the turn.

rotateProviderTransportOn429 now takes the request's routed provider and
swaps ONLY the API key onto it before re-applying transport metadata,
mirroring the OAuth-401 replay path (which already spreads route.provider).
The registry lookup becomes unnecessary and is removed. Other failover
paths were audited and are sound: Codex multi-account retry strips runtime
fields off route.provider, and the Anthropic account pool spreads
route.provider directly.

Regression coverage: the existing Kimi unit test keeps asserting the wire
body on both attempts; a new unit test proves arbitrary registry-backfilled
fields survive rotation; a new e2e test on the kimi-code preset asserts
prompt_cache_key is present on BOTH the initial attempt and the
post-rotation retry (verified to fail against the pre-fix behavior).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Wibias

Wibias commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Review verdict: approve / merge-ready

Reviewed tip 2c9de85c vs upstream/dev. CI green. Local: touched suites 67/67 pass, tsc --noEmit clean.

Bugs

No shippable defects found.

  • Adapter still only forwards when provider.promptCacheKey && parsed.options.promptCacheKey !== undefined — never invents a key; absent stays absent.
  • Explicit promptCacheKey: false still opts out (covered in openai-chat-hardening).
  • rotateProviderTransportOn429 now takes the routed provider and swaps only apiKey — fixes the real regression where a persisted pre-upgrade/login snapshot dropped registry backfills (promptCacheKey, parallelToolCalls, pinned baseUrl, etc.) on 429 retries. All four core.ts call sites updated.
  • moonshot / other Kimi-adjacent presets remain deny-by-default (only kimi + kimi-code set promptCacheKey: true).
  • E2E asserts wire prompt_cache_key on both attempts with deliberately stale persisted config (no seed flag).

Security

No medium+ findings in this diff.

Area Result
Credential / key rotation Swaps pool key within the same provider; no cross-provider / cross-account mix.
prompt_cache_key Caller-supplied (or inbound-hashed) session affinity only; not logged as a secret in failover paths (key ids only).
Auth boundary No change to auth gates; deny-by-default elsewhere limits blast radius if a strict backend rejects the field.

Residual (non-blocking): live A/B in the PR body showed no short-run cache win vs Kimi’s automatic prefix cache — retained for documented Code Plan contract / affinity under pool rotation, which the e2e covers.

Open CodeRabbit / Codex

0 open threads.

  • Codex P2 (preserve cache key across kimi-code 429 rotation) — fixed by 4bb35c05 / generalized in 2c9de85c; thread resolved.
  • CodeRabbit docs nit (never generate key; rejection leaves config alone) — addressed in EN + locales; thread resolved/outdated.

Verdict

LGTM — safe to merge to dev. No remaining open review fixes required.

@Wibias

Wibias commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR, @olddonkey — clean Kimi Coding Plan prompt_cache_key opt-in plus the 429 rotation inherit fix. Squash-merging now.

@Wibias
Wibias merged commit 0338b78 into lidge-jun:dev Jul 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants