feat(providers): stable MemBox prompt-cache boundary for Anthropic - #25
Conversation
Place Anthropic cache_control on the latest host current_user_request message via a MemBox wire prefix, and skip last-message caching when that stable breakpoint is present.
sheperdh
left a comment
There was a problem hiding this comment.
Review summary
The stable MemBox cache-boundary approach is structurally correct and should prevent volatile MemoryBox context from invalidating the reusable Anthropic prompt prefix. I did not find a response-correctness regression, but I recommend addressing one cache-performance regression and the validation gaps below before merging.
What looks correct
- MemBox marks only the native Anthropic
current_user_requestpath. - The wire marker is stripped before the request is sent to Anthropic.
cache_controlis placed on the latest marked request block, before the volatile MemoryBox context suffix.- The breakpoint survives Anthropic's adjacent-user-message merging.
- Both
chatandstream_chatuse the new behavior. - The non-Anthropic projection remains unchanged.
This matches Anthropic's guidance to place an explicit breakpoint on the last block whose prefix remains identical between requests.
Performance concern: last-message caching is disabled too broadly
apply_conversation_cache_control returns as soon as it finds the stable MemBox boundary. This disables the existing last-message cache for every marked request.
That matters during a long tool loop: the growing text / tool_result tail can no longer be cached incrementally, so each provider call may reprocess more input tokens and incur additional latency and cost.
Simply retaining the old last-message breakpoint is not sufficient because the MemBox OAuth path can already use four explicit breakpoints:
- OAuth identity system block
- Full system prompt
- Last tool definition
- Current-user stable boundary
Anthropic currently allows at most four explicit breakpoints. Adding the old tail breakpoint unconditionally could therefore produce five.
Suggested allocation
When a full system block follows the fixed OAuth identity block, the identity block's separate breakpoint is largely redundant: the full-system breakpoint already includes that prefix. I suggest freeing that slot and using this layout:
- Last tool definition
- Full system prompt, including the OAuth identity prefix
- MemBox current-user stable boundary
- Latest cacheable
textortool_resulttail
If fewer components are present, allocate only the breakpoints that are needed. If no slot is available, prioritize the stable MemBox boundary over the tail cache.
Please add a test that asserts both the intended breakpoint placement and a maximum of four serialized breakpoints.
Validation gaps
The new unit test verifies content-block placement, but it does not demonstrate an actual cache read. The PR's live-cache test-plan item is also still unchecked.
Recommended coverage:
- Two real follow-up requests that assert
cache_creation_input_tokensandcache_read_input_tokens. - A multi-step tool loop showing that the stable prefix and incremental tail can both be reused.
- A conversation adding more than Anthropic's 20-block backward-lookback window between turns.
- No-marker behavior retaining the existing last-message cache.
- Serialized breakpoint count never exceeding four.
Local verification
cargo test -p zeroclaw-providers cache --lib: 47 passedcargo test -p zeroclaw-providers --lib: 1235 passed- MemBox prompt-prefix and Anthropic/non-Anthropic marker tests: 3 passed
cargo fmt --all -- --check: failed on the three modified files
The PR checks currently visible are path labeling and PR-title validation; the full Rust Quality Gate is configured for PRs targeting master, so it did not run for this memorybox-targeted PR.
Recommendation
The core fix is sound, but I recommend updating the breakpoint-allocation policy, adding live/long-tool-loop evidence, and applying cargo fmt before merge.
Free the redundant OAuth identity breakpoint when a full system block follows, still cache incremental tool-loop tails after a MemBox boundary, and enforce Anthropic's four-breakpoint cap while preferring the stable boundary over the tail.
|
Thanks for the review β addressed in 43c5fc8: Breakpoint allocation
Tests
Live cache evidence (follow-up / MemBox-side): real |
sheperdh
left a comment
There was a problem hiding this comment.
Review summary
Reviewed current head 43c5fc8. The updated implementation resolves the prior cache-performance concern without changing provider-visible prompt content or non-Anthropic behavior. I found no remaining code blocker.
β Resolved β tool-loop tail caching
The MemBox stable boundary no longer disables incremental last-message caching globally. A separate text or tool_result tail can receive its own breakpoint, while volatile MemoryBox context that shares the boundary message remains uncached.
β Resolved β four-breakpoint allocation
When a full system prompt follows the OAuth identity prefix, the identity block no longer consumes a redundant breakpoint. The normal tools + system + MemBox boundary + incremental-tail layout therefore fits Anthropic's four-breakpoint limit, and the final request-building step enforces that limit for both chat and streaming paths while dropping lower-priority tail breakpoints first.
β Resolved β regression and formatting coverage
The added tests cover the marked boundary, the unmarked fallback path, OAuth slot allocation, tool-loop tail caching, and the combined four-slot request. Formatting and strict provider Clippy checks now pass.
π‘ Warning β live cache metrics remain follow-up evidence
This review did not run credentialed Anthropic traffic, so provider-reported cache_creation_input_tokens / cache_read_input_tokens and a live >20-content-block tool loop remain unverified. This is non-blocking because the stable breakpoint is serialized directly at the unchanged boundary on every request, the incremental tail has independent structural coverage, and the live gap is now documented explicitly in the PR description.
π’ What looks good β behavior remains scoped
The transport marker is stripped before provider delivery, only the latest marked request becomes the stable breakpoint, volatile context stays outside it, both Anthropic request paths share the policy, and unmarked/non-Anthropic flows remain unchanged.
Validation
cargo fmt --all -- --check: passedcargo clippy -p zeroclaw-providers --all-targets -- -D warnings: passedcargo test -p zeroclaw-providers cache --lib: 49 passedcargo test -p zeroclaw-providers --lib: 1239 passed- Current GitHub checks: passed; the
memoryboxbase does not trigger the full Rust Quality Gate
Decision
Approved.
Summary
memoryboxMEMBOX_PROMPT_CACHE_BOUNDARY_PREFIX) so the host can identify the stablecurrent_user_requestboundary during Anthropic request conversion.cache_controlon that boundary before volatile MemoryBox context, preserving the stable prefix through adjacent-user merging and follow-up turns.memorybox-targeted PR.Testing (required)
How you can test (when useful)
current_user_requestcontent followed by volatile MemoryBox context and at least one tool round.cache_control; volatile MemoryBox context remains uncached; tool-loop tails receive an incremental breakpoint when a slot is available; the request contains no more than four explicit breakpoints; follow-ups report cache creation/read tokens when the prompt meets Anthropic's model-specific minimum.memorybox(before): Cache placement on the changing tail invalidates the reusable request prefix; the first PR revision also suppressed incremental tool-loop tail caching whenever a MemBox boundary existed.How I tested
memorybox-targeted PR does not receive the repository's full Rust Quality Gate, so focused and full provider checks were run locally.cache_creation_input_tokens/cache_read_input_tokensand a live >20-content-block tool loop remain follow-up validation. The stable explicit boundary and breakpoint allocation are covered structurally by unit tests.chatandstream_chatrequest construction, OAuth/system/tool/message breakpoint accounting, latest-boundary selection, adjacent-user merging, marker stripping, volatile-sibling exclusion, no-marker behavior, and the four-breakpoint priority policy. Live Anthropic cache usage was not verified.Security & Privacy Impact (required)
Compatibility (required)
Rollback