Skip to content

fix(ai): allow prompt cache keys for compatible proxies - #9442

Open
dannote wants to merge 6 commits into
earendil-works:mainfrom
dannote:fix/ai-proxy-prompt-cache
Open

dannote wants to merge 6 commits into
earendil-works:mainfrom
dannote:fix/ai-proxy-prompt-cache

Conversation

@dannote

@dannote dannote commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Chat Completions only sends prompt_cache_key to direct OpenAI URLs or when long retention is requested and supported. A compatible proxy therefore cannot receive pi's session key with default short retention, even if it accepts that field.

Add compat.supportsPromptCacheKey to opt in independently of long retention:

"compat": {
  "supportsPromptCacheKey": true,
  "supportsLongCacheRetention": false
}

false explicitly suppresses the key; unset preserves existing behavior. cacheRetention: "none" still omits it. This does not change retention duration or session-affinity headers.

In a live test through llm_proxy with a Codex backend, a follow-up without the key returned 0/1,931 cached input tokens; a separate pair with a stable key returned 1,792/1,931.

Related to #6654, but addresses the URL/retention gate that PR explicitly leaves unchanged, rather than adding a key-value override.

Validation: npm run check, npm run build:offline, and ./test.sh pass. Added coverage for proxy opt-in, unchanged defaults, disabled caching, independent retention, missing session IDs, Unicode key clamping, and models.json provider/model overrides. A filesystem-watcher test timed out on the first full run, then passed individually and in the full rerun.

AI-assisted implementation and PR description.

@petrroll

Copy link
Copy Markdown
Contributor

not a maintainer but:

@dannote seeing how things tend to evolve, I'd recommend switching bool for enum of smth like prompCacheKeyStyle: none | openai, so that it's evolvable for the inevitable future where someone will want to send it via smth. a bit different.

(or if you're reasonably sure this won't end up like session-id mess as it's not headers it might be fine)

@holny

holny commented Sep 11, 2026

Copy link
Copy Markdown

Traced the old vs new conditions — the refactor is behavior-preserving: direct api.openai.com still keys whenever retention isn't none, and other baseUrls still only get the key through the long-retention path unless opted in. Test matrix covers the corners nicely (no identity invented, 64-char clamp, none still wins).

One gap worth flagging: this only covers the Chat Completions shape. The Responses adapter sends prompt_cache_key unconditionally for any provider when retention isn't none (openai-responses.ts:294 — no baseUrl check there), and azure-openai-responses.ts:293 always sends it. So a Responses-compatible proxy has no escape hatch at all, and once this lands the two adapters will have opposite policies for the same compat problem. A sibling flag there, or at least a sentence in models.md noting the asymmetry, seems worth considering — fine as a follow-up.

On the bool vs enum thread: agree with @petrroll — with ?? treating unset as auto this is really auto/on/off, and cheaper to settle before it ships in models.json.

@dannote

dannote commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the idea, @petrroll! Switched the boolean to promptCacheKeyMode: "auto" | "enabled" | "disabled".

@gaoanze888 gaoanze888 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.

Reviewed exact head 490bebc99, including the Chat Completions and Responses policies plus models.json provider/model override propagation.

The tri-state behavior is coherent and backward-compatible:

  • Chat Completions auto retains the existing direct-OpenAI / supported-long-retention gate.
  • Responses auto retains its existing key-on behavior.
  • enabled allows compatible proxies to receive the session key without claiming long-retention support.
  • disabled and cacheRetention: "none" suppress the key as documented.
  • session-affinity headers remain independent, and caller headers still override generated affinity headers.

I also checked config schema acceptance, provider inheritance, per-model override precedence, missing session IDs, Unicode clamping, and the relation to #6654 (key policy vs caller-supplied key value). Azure/Codex adapters remain outside the documented scope of this flag.

Validation:

  • prompt-cache adapter tests: 30/30
  • model registry/config tests: 85/85
  • npm run check
  • full exact-head merge into current origin/main (71dca871b) applies cleanly
  • clean git diff --check

No blocker found.

This comment is AI-generated by /wr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants