Skip to content

fix(responses): preserve prompt cache reuse in chat bridge - #42281

Merged
ryan-crabbe-berri merged 5 commits into
BerriAI:mainfrom
sorgfresser:litellm_preserve_responses_prompt_cache_breakpoint
Oct 7, 2026
Merged

ryan-crabbe-berri merged 5 commits into
BerriAI:mainfrom
sorgfresser:litellm_preserve_responses_prompt_cache_breakpoint

Conversation

@sorgfresser

@sorgfresser sorgfresser commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Moving breakpoints stop reusing the previous tool prefix
  • Growing agent histories keep caching only the static prefix

How it solves it:

  • Preserve cache breakpoints through the Chat-to-Responses bridge
  • Default gateway-injected breakpoints to implicit lookup
  • Preserve caller-selected cache modes and TTLs

User Flow

Before: an agent with growing tool history keeps rewriting previously cached input

  1. The developer sends POST https://litellm-domain/v1/chat/completions with tools, medium reasoning, and automatic cache injection
  2. The first 2,283-token turn writes 2,280 cache tokens
  3. The 2,838-token turn reads only 1,725 cached tokens and rewrites 1,110
  4. The 3,393-token turn still reads only 1,725 cached tokens and rewrites 1,665

After: each turn reuses the previous eligible tool boundary and writes only its new suffix

  1. The developer sends the same growing tool conversation
  2. The first turn still writes 2,280 cache tokens
  3. The second turn reads 2,280 cached tokens and writes 555
  4. The third turn reads 2,835 cached tokens and writes 555

Pre-Submission checklist

  • I have added meaningful tests
  • The handful of test files covering my change pass locally
  • My PR passes all required CI/CD checks
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5

Screenshots / Proof of Fix

The same three growing tool turns were captured from the exact production image and replayed through the real upstream provider. The six bounded calls cost $0.1265075 total

Before (cdcbf2f)

  1. Captured requests used prompt_cache_options.mode: explicit
  2. Cached tokens by turn were 0, 1725, and 1725
  3. Cache writes by turn were 2280, 1110, and 1665

After (b1084f0, patch-equivalent live run at 6d84883)

  1. Captured requests used prompt_cache_options.mode: implicit while retaining explicit breakpoints
  2. Cached tokens by turn were 0, 2280, and 2835
  3. Cache writes by turn were 2280, 555, and 555
  4. The rebase changed test paths and commit identity without changing the production patch

Test Plan

  • 20 passed, 735 deselected across the focused cache and Responses tests on the rebased tip
  • All required CI checks pass on the rebased tip
  • Restoring the old explicit default makes the new cache-mode assertion fail
  • Caller-provided explicit and implicit modes, including TTL, remain unchanged

Type

Bug Fix
Test

Final Attestation

  • The tests check the right things, including caller overrides and growing tool histories

Note

Medium Risk
Changes upstream prompt-cache semantics (implicit vs explicit) and request payload shape for cached agent/tool conversations; incorrect behavior would affect cache hit rates and cost but is limited to OpenAI prompt-cache breakpoint flows with tests covering overrides.

Overview
Fixes prompt-cache reuse when chat completions are bridged to the Responses API and when LiteLLM auto-injects cache breakpoints for OpenAI GPT ≥5.6.

The chat→Responses converter now copies prompt_cache_breakpoint onto converted text, image_url, and file content blocks via with_prompt_cache_breakpoint, so injected marks are not dropped mid-bridge. On the OpenAI-dialect chat path, when the cache hook newly places breakpoints it defaults prompt_cache_options.mode to implicit instead of explicit, while per-block breakpoints stay explicit and caller-supplied prompt_cache_options (including TTL) are unchanged.

Tests assert breakpoint survival through transform_request and update expectations for the implicit default on chat/completions and Responses request bodies.

Reviewed by Cursor Bugbot for commit b1084f0. Bugbot is set up for automated code reviews on this repo. Configure here.

@CLAassistant

CLAassistant commented Sep 21, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codspeed

codspeed Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing sorgfresser:litellm_preserve_responses_prompt_cache_breakpoint (b1084f0) with main (22b36cb)

Open in CodSpeed

@codecov

codecov Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sorgfresser
sorgfresser marked this pull request as ready for review September 21, 2026 19:38
@sorgfresser
sorgfresser requested a review from a team September 21, 2026 19:38
@greptile-apps

greptile-apps Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Changes prompt cache mode from explicit to implicit in Anthropic integration.

The PR appears safe to merge; no outstanding finding or new actionable issue was established.

Summary

The PR preserves prompt-cache breakpoints when Chat content is converted to Responses input and changes the gateway-injected chat-path cache mode to implicit while retaining caller-provided options.

  • Adds conversion coverage for text, image URL, and file blocks.
  • Updates cache-mode assertions and tests caller-selected modes and TTLs.

Reviews (3) · Last reviewed commit: "test(cache): expect implicit chat lookup"

@sorgfresser sorgfresser changed the title fix(responses): preserve prompt cache breakpoints in chat bridge fix(responses): preserve prompt cache reuse in chat bridge Sep 25, 2026
@sorgfresser

Copy link
Copy Markdown
Contributor Author

@greptileai Please review the updated cache-mode fix and confirm multimodal breakpoint preservation remains complete across the Chat-to-Responses bridge.

@sorgfresser
sorgfresser force-pushed the litellm_preserve_responses_prompt_cache_breakpoint branch 2 times, most recently from 20520b3 to f9764fa Compare September 25, 2026 14:10
@sorgfresser
sorgfresser force-pushed the litellm_preserve_responses_prompt_cache_breakpoint branch from 6d84883 to b1084f0 Compare September 27, 2026 16:33
@yucheng-berri

Copy link
Copy Markdown
Contributor

@greptileai review latest head

@yucheng-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b1084f0. Configure here.

@ryan-crabbe-berri ryan-crabbe-berri left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. Verified live against OpenAI that implicit mode keeps the explicit marks and reads back the previous turn's prefix. Docs follow-up incoming for making the implicitl default change and then also documenting what we do by default in the other scenarios here

@ryan-crabbe-berri
ryan-crabbe-berri merged commit 48c88ba into BerriAI:main Oct 7, 2026
101 checks passed
mateo-berri added a commit that referenced this pull request Oct 7, 2026
…ady routes the hook

#42281 landed the text, image_url and file branch carry and the implicit default
on main, and the Responses routing lookup this branch changed has no observable
effect there, so the PR shrinks to the unknown-block fallback branch and its
regression test
mateo-berri added a commit that referenced this pull request Oct 8, 2026
…es (#40032)

* fix(responses): keep cache breakpoints on requests that reach the Responses API

The chat-completions bridge rebuilt every content block without the
prompt_cache_breakpoint marker the cache-control hook had just placed on it, so
the request went out with prompt_cache_options set to explicit mode and nothing
actually marked. Explicit mode caches only what is marked, so those deployments
lost the implicit caching they were getting before the injection point was added

The native surface had a second miss. The bridging check looked the provider
config up with the still-prefixed model while the config keys on the bare one,
so every bedrock_mantle model read as having no native Responses support, and
role-targeted injection points were deferred to a chat-completions pass that
never runs for them

* fix(responses): keep prompt cache breakpoint on stringified content blocks

* test: cover the responses/ routing prefix in the native cache point case

* fix(responses): keep only the fallback-branch marker carry, main already routes the hook

#42281 landed the text, image_url and file branch carry and the implicit default
on main, and the Responses routing lookup this branch changed has no observable
effect there, so the PR shrinks to the unknown-block fallback branch and its
regression test

* fix(responses): drop a malformed prompt_cache_breakpoint under drop_params in the chat-to-Responses bridge

* fix(responses): accept the 30m prompt_cache_breakpoint ttl under drop_params

OpenAI's Responses API takes a ttl of 30m on an explicit breakpoint marker, so the drop_params validation keeps it instead of stripping it as an unknown key. Trims the bridge test docstrings to the dated vendor citation.

* test(integration): audit cells for prompt_cache_breakpoint through the chat-to-Responses bridge

Adds the /audit cells for the bridge's prompt_cache_breakpoint carry: valid and malformed markers on every
carrying block kind and on tool and assistant content, the drop_params on and off contract, the hook-injected
system marker, the Anthropic SDK path through the Responses adapter, upstream errors, idempotent spend rows,
and the chaos cells (mixed burst, upstream outage, slow streams, worker SIGKILL and a proxy restart mid burst),
all against a scripted Responses endpoint with the request it received read back by response id

* test(integration): give every scripted responses reply its own id

The prompt-cache-breakpoint audit's scripted upstream minted the response id
from the request's marker, so three identical marked requests shared one id.
The spend-log writer skips rows whose request_id already landed, and the
idempotent-logging cell saw one row for three calls on every leg. The upstream
now mints a unique id per reply like a real provider, and the cells match a
response to its request by the marker inside that id.
poodle64 added a commit to poodle64/litellm that referenced this pull request Oct 10, 2026
…breakpoint an input item

Since this branch was cut, main carries prompt_cache_breakpoint markers through the
bridge on input items' content blocks (BerriAI#42281, BerriAI#44119, BerriAI#45500), and its tests expect a
leading system message whose block carries one to stay an input item so the marker
survives. Folding that message into `instructions` dropped the marker and broke eight
of those tests.

The fold now skips a leading system message any of whose blocks carries a
prompt_cache_breakpoint, alongside the non-text case it already skipped. A block with
only cache_control, the shape BerriAI#42171 reports, still folds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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