Conversation
PR #106074 — perf(prompt-caching): copy only mutable cache-plan rowsCopy-on-write planning: list-copy the transcript, then deepcopy only rows that will carry markers (
No blocking issues. |
Xipong
force-pushed
the
perf/agent-loop-20260908
branch
from
September 9, 2026 17:15
a74fe3e to
4781364
Compare
|
Thanks for this. It is salvaged into #120406 as part of the perf sweep, with your authorship kept on the commit (cherry-picked with a Closing this in favour of #120406, which is armed to rebase-merge. If that PR doesn't land, I'll reopen this one. |
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.
Closes #106061
Redundant full-history copy
build_prompt_cache_plan()began by deep-copying the complete provider message history, even though the planner mutates only a small set of rows: stale-marker carriers, list-valued content that needs canonical cleanup, the system row selected for decoration, and completed transaction endpoints selected for cache markers.On long tool-heavy turns, that unconditional copy duplicated nested history structures before the request could be sent.
Copy-on-write contract
The planner now shallow-copies the outer request list and detaches only rows whose provider-visible representation may change:
cache_controlmetadata;Unmodified rows remain shared only with the caller's request-local structural copy. Canonical history is still isolated at the actual request-assembly boundary. Modified rows are detached before mutation, and the tools array keeps its existing independent cleanup path.
Exact behavior preservation
Unmarked list-valued content still requires cleanup: the legacy helper canonicalizes supported plain-text part shapes even when no stale marker is present. An intermediate candidate skipped that case and was rejected before publication.
The final regression compares plain strings with equivalent one-part user and two-part system content across native/non-native and direct-tool-cache layouts. Provider-visible messages, marker counts, tools, and caller input remain equal to the previous planner.
This PR does not change cache placement policy, TTLs, transcript retention, provider routing, or the four-marker budget.
Measured impact
Real planner, 32 synthetic tool schemas, median of seven calls on Linux/WSL2 with CPython 3.11.15:
The direct-native-tool layout showed the same scaling improvement: 41.481 ms → 3.948 ms at 12,001 rows.
Verification
git diff --checkpassed. Local Ruff was unavailable in the shared virtual environment; repository CI remains responsible for that unchanged lint lane.Boundaries and related work
Unmodified result rows may alias the caller's request-local rows; callers continue to rely on the existing structural-copy boundary before planning. No canonical-history write-through, cache, invalidation owner, schema, or dependency is introduced. The separate tool-pairing optimization in #105660 is not duplicated.