chore: cache campaign_assistant system prefix via cache_control - #73
Merged
Conversation
Add an ephemeral Anthropic prompt-caching (cache_control) breakpoint on the `system` field for the planning model's requests, so a multi-turn campaign_assistant conversation reuses the tools+system tokens across turns instead of paying full input price every turn. The breakpoint is injected at the wire in the existing tool-order RoundTripper (server.anthropicToolOrderTransport), which sidesteps the Genkit abstraction — we already rewrite the outgoing /v1/messages body there to sort tools. Anthropic assembles the prefix as tools -> system, so one breakpoint on the last system block caches the tool schemas and the system prompt together. Scoped to cfg.PlanningModelID only: other flows (generation/quality) whose system prompt changes per request would just pay the ~1.25x cache-write premium with no reads, so they are left uncached. cachePrefixModel == "" disables the caching while leaving the tool-order sort intact. Notes: - Distinct from the strict-tool grammar cache the tool-order stabilizer already keeps warm; this is token-level prompt caching. - Silent no-op if the cached prefix is under the model's minimum (~4096 tokens on Haiku 4.5). Verify via usage.cache_read_input_tokens. - Corrected the assembleContext comment that implied caching already applied.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
WalkthroughThe Anthropic transport now adds an ephemeral system cache breakpoint only for requests matching the configured planning model, while retaining tool-order sorting. Runtime wiring supplies that model, and campaign context comments document prompt-prefix determinism. ChangesAnthropic prompt caching
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant newGenkitRuntime
participant anthropicToolOrderTransport
participant AnthropicMessagesAPI
newGenkitRuntime->>anthropicToolOrderTransport: Configure planning model
anthropicToolOrderTransport->>anthropicToolOrderTransport: Sort tools and match model
anthropicToolOrderTransport->>AnthropicMessagesAPI: Send request with conditional ephemeral cache_control
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
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.
Summary
Adds an ephemeral Anthropic prompt-caching (
cache_control) breakpoint on thesystemfield for the campaign_assistant's planning-model requests. A multi-turnconversation now reuses the tools + system tokens across turns instead of paying
full input price on every turn (Haiku cached-read is ~0.1× input).
This is token-level prompt caching — distinct from the strict-tool grammar cache
that the existing tool-order stabilizer already keeps warm. Before this change the
campaign_assistant set no
cache_controlat all, despite a code comment implyingcaching "still applied".
How
The breakpoint is injected at the wire, inside the existing tool-order
RoundTripper(server.anthropicToolOrderTransport) that already rewrites theoutgoing
/v1/messagesbody to sort tools. This sidesteps the Genkit abstractionentirely — no dependency on whether the Genkit Anthropic plugin forwards
cache_control. Anthropic assembles the prefix astools → system, so onebreakpoint on the last
systemblock caches the tool schemas and the systemprompt together.
addAnthropicSystemCacheControlhandles both wire forms ofsystem(a bare JSONstring → promoted to a cached text block; an array →
cache_controlon the lastblock), and is a no-op when there's no system, it's empty, or a breakpoint already
exists.
Scope & safety
modelmatchescfg.PlanningModelID(the Haiku model behind campaign_assistant). Other flows(generation/quality) — whose system prompts vary per request — are left uncached
so they don't pay the ~1.25× cache-write premium for reads that never come.
AnthropicStableToolOrderflag;cachePrefixModel == ""disables the caching while keeping the tool-order sort.The cached prefix (tools + system) is borderline against Haiku 4.5's ~4096-token
minimum (estimated ~4.2–5.0K). Prompt caching is a **silent no-op below
— no error, just no cache. Two checks:
messages/count_tokenson a representativetools + systempayload and confirm it clears ~4096.usage.cache_read_input_tokens > 0(via
KindCacheReadmetering orANTHROPIC_DEBUG_HTTP). Zero means thunder the floor or the date/brief rotated the cache between turns.
Note the cached prefix includes today's date + the live brief (concatenated after the
static prompt), so it rotates daily and on brief edits — fine for the real
pattern (consecutive turns within the 5-min TTL share it).
Test plan
go build ./...✅go test ./src/server/ ./src/genkit/flows/campaign_assistant/✅transport behavior (matching model caches; other model and empty config
Follow-up (not in this PR)
A call-site variant in
run.gocould sendsystemas two blocks(
[{static, cache_control}, {dynamic}]) so the cached prefix survives across days andbrief edits — but that split can't be done reliably at the wire and depend
Genkit plugin forwarding multi-block
cache_control. Deferred.Summary by CodeRabbit
Performance
Reliability