fix(vertex): serialize the body static-fields-first so prompt caching survives routing - #1927
Open
nkato wants to merge 1 commit into
Open
fix(vertex): serialize the body static-fields-first so prompt caching survives routing#1927nkato wants to merge 1 commit into
nkato wants to merge 1 commit into
Conversation
… survives routing On Google Cloud's Agent Platform (Vertex AI) a request is served by one of several independent prompt-cache stores, and which store is chosen depends on a hash of the head of the request body, measured at roughly the first 16 KB. The body serializes `messages` second, so while a conversation is still short the growing history sits inside the hashed region: each request of that conversation hashes differently, can reach a store that has not seen the prefix, and writes the prefix again instead of reading it. A six-request script that keeps a 7k-token system prefix unchanged and appends one turn at a time missed on 2, 1 and 2 of the 5 follow-up requests over three runs on 1.5.0. With this change, three runs of the same script missed on none of them. The behaviour and the fix are the same on the `us` multi-region endpoint and on `global`. Two further measurements locate the region that is hashed. Recording how many leading bytes each request shared with the previous one, requests that shared 9 KB missed and requests that shared 19 KB or more hit. Keeping the current key order but putting a 20 KB block of unchanging text at the start of the first user message also removes the misses, so what matters is which bytes are at the front of the body rather than which field they belong to. JSON object key order does not change the meaning of the request, so this only changes which bytes land at the front of it. The added tests assert the serialized key order for the sync and the async client and fail without the change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nkato
force-pushed
the
fix/vertex-prompt-cache-body-key-order
branch
from
September 12, 2026 16:25
3f43cc4 to
d5fbcdf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Google Cloud's Agent Platform (Vertex AI), prompt caching misses during the first turns of a conversation even though the cached prefix never changes.
A request is served by one of several independent cache stores, and which store is chosen depends on a hash of the head of the request body. The body serializes
messagessecond, so while a conversation is still short the growing history sits inside the hashed region. Each request of that conversation hashes differently, can reach a store that has not seen the prefix, and writes the prefix again instead of reading it.Reproduction
Requires
anthropic[vertex]and application default credentials.The system prefix is 7k tokens and never changes, so requests 1 to 5 should all read it. On
mainat 1.5.0, three runs missed on 2, 1 and 2 of those 5 requests. The first run:With this branch, three runs of the same script missed on none of them. The first run:
The behaviour is the same on the
usmulti-region endpoint and onglobal.Measurements
The hashed region is about 16 KB. Recording how many leading bytes each request shared with the previous one, requests that shared 9 KB missed and requests that shared 19 KB or more hit, on every step of two chains. Serializing
systemfirst but keeping it at 12 KB still missed, and 15 KB or larger did not.Keeping the current key order and instead putting a 20 KB block of unchanging text at the start of the first user message also removes the misses. What matters is which bytes are at the front of the body, not which field they belong to.
On one failing chain a request read back the prefix that had been written three requests earlier rather than the most recent one, so the conversation was spread over more than one cache store.
Change
_prepare_optionsreorders the assembled body soanthropic_version,systemandtoolscome first andmessagescomes last. JSON object key order does not change the meaning of the request.The added tests assert the serialized key order for the sync and the async Vertex client, and fail without the change.
This reduces cold prefixes rather than removing them, since the assignment of a request to a store can still change on the platform side.