Totoro 4028b63647
fix(models): keep Claude prompt-cache markers out of thread messages (#5892)
* fix(models): keep Claude prompt-cache markers out of thread messages

ClaudeChatModel._apply_prompt_caching set cache_control on payload blocks in
place. langchain-anthropic forwards Claude-native blocks (image or document
blocks with a source, search results) by reference, so the marker landed in
the thread's own message and was checkpointed with it. Each later request
carried those stale markers on top of its own four, and from the third turn
of a thread with such blocks the API rejected every request (400, "A maximum
of 4 blocks with cache_control may be provided").

Breakpoints are now placed on copies, and markers already in the request are
dropped first, so threads whose checkpoints already carry them recover
without a migration. The OAuth path's _strip_cache_control popped markers
from the same shared objects; it now copies too, and prompt caching reuses it.

* fix(models): drop stored Claude cache markers with caching off too

The strip only ran inside _apply_prompt_caching, so with
enable_prompt_caching: false a thread whose checkpoint already carried five
or more markers kept failing: exactly the switch an operator would flip in
response to these 400s. _get_request_payload now strips on the non-caching
path as well, so recovery no longer depends on the flag.

The stored-checkpoint recovery is now pinned through _get_request_payload
with real messages, for caching on and off. The docstring and the AGENTS.md
note now say which objects are really shared (blocks, list-form system
blocks, a reused binding's tool dicts), that the message-dict and list
copies are defensive, and that _apply_prompt_caching must strip first.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-09-27 08:29:56 +08:00
..