Repository navigation
fix: Keep Per-Conversation Context and the Summary at a Fixed System Position - #613
Open
Shabonasar wants to merge 1 commit into
Open
Shabonasar wants to merge 1 commit into
Shabonasar wants to merge 1 commit into
Conversation
…Position Adds conversation_instructions: context that is stable within a conversation. It stays in the system message right after the static instructions and is never moved: its own content block with a cache breakpoint for Anthropic and OpenRouter, a block followed by a cache point for Bedrock, appended text for other providers. additional_instructions keeps its per-turn behaviour. A host that passes conversation_instructions gets the compaction summary, both when created inside a run and when restored as initialSummary, as one stable user message right after the system message, with a breakpoint of its own while the request has one left. Token accounting counts it as instruction overhead. Both paths render the same carrier text, so the requests after a compaction form one continuous prefix. Hosts that do not pass the field keep the earlier placement and accounting. Adds a prompt cache harness (src/specs/prompt-cache) that runs declarative scenarios through Run and each provider class, captures the serialized request before the network, and asserts the cache invariants per provider.
This was referenced Oct 15, 2026
This branch has not been deployed
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
With Anthropic or OpenRouter prompt caching,
AgentContextmoves the dynamic system part into a user message before the newest human message, so context that is fixed for a whole conversation (start time, memory, file lists) shifts on every turn and the previous exchange is re-written to the cache each time. A summary created by a compaction inside a run was also sent as a user message while the next run rendered the same summary insystem, so every compaction cost a second prefix rebuild. This PR addsAgentInputs.conversation_instructions, kept in the system message right afterinstructionsand never moved, and one fixed placement for the summary. Hosts that do not pass the field get the same requests as before.The fixed summary placement applies to hosts that pass
conversation_instructions; LibreChat passes it for the providers where the SDK caches the conversation block (Anthropic, OpenRouter, Bedrock withpromptCache). Other providers, and hosts without the field, keep the earlier placement, so a summary there can still render in two places and cost a second rebuild after a compaction.Related to LibreChat-AI/LibreChat#17102. Used by the LibreChat PR "provider output items and conversation block" behind
endpoints.agents.promptCache.stableContext. Base: main.How it works
Sstatic instructions,Tper-conversation context,Dper-turn context,bpcache breakpoint.promptCache: a separate system content block with its own breakpoint. Bedrock withpromptCache: a block followed by a cache point. Other providers: appended to the system text at the same position.initialSummary) is one stable user message right after the system message, with a breakpoint while the request has one left (Anthropic allows four). Token accounting counts it as instruction overhead.setSummaryloses itsprecedesMessagesoption.additional_instructionskeeps its per-turn behaviour (moved under prompt caching).Measurements
Only the requests that carry
conversation_instructionschange; G1 and G3 above come from the LibreChat stack that passes it for Anthropic. OpenAI chat does not use the field, so its G1 gain comes from the host keeping the system prompt stable, and its G3 share is lower because the summary still renders in two places there.Run: group G1-G3 measured on Qwen3.8-27B (vLLM behind NVIDIA Dynamo, through LiteLLM), OpenAI chat and Anthropic Messages, dev / lever off / lever on; the same self-hosted model answers both formats, the real OpenAI and Anthropic APIs were not measured; full method in #17102.
Note: the backend caches in 1600-token blocks, so a request reuses cache only in whole blocks and the first turns of a short conversation (prompt under two blocks) cannot hit; shares are therefore lower on short chats than on long ones.
Testing
AgentContext.test.ts: system blocks and breakpoints per provider, Bedrock cache points, plain-text order, token accounting, the summary position, a summary set in a run rendering exactly like the same summary restored by the next run, and the unchanged behaviour without the field.src/specs/prompt-cache,docs/prompt-cache-harness.md): each turn is built through a realRunand the real chat model class and the serialized request is captured before the network. Providers: OpenAI, OpenAI Responses, an OpenAI-compatible endpoint, Azure, DeepSeek, xAI, Moonshot, Anthropic, OpenRouter and Bedrock with and without caching, Google. Invariants: the conversation block stays insystem, onlyadditional_instructionsmoves, request N+1 starts with request N up to the per-turn part (including after a compaction inside a turn), a prefix shared across conversations, at most four breakpoints, strict alternation where required. Offline, in the defaultnpx jest.promptCacheis covered by the harness only; it was not run against a real Bedrock endpoint.Type of change
Checklist