docs/capabilities/compaction.md
As a conversation grows, its message history can approach the model's context window. Compaction keeps it in check by shrinking older messages — trimming, clearing, or summarizing them — while preserving recent context and tool-call integrity. Pydantic AI supports this at several levels, from provider-native APIs to model-agnostic history editing.
Some providers expose a built-in compaction API that runs on their side. Pydantic AI wraps these as capabilities:
| Provider | Capability | Details |
|---|---|---|
| OpenAI Responses API | [OpenAICompaction][pydantic_ai.models.openai.OpenAICompaction] | OpenAI compaction |
| Anthropic | [AnthropicCompaction][pydantic_ai.models.anthropic.AnthropicCompaction] | Anthropic compaction |
Each uses the corresponding provider API, so it's only available on that provider.
Pydantic AI treats a compaction part as a visibility boundary: the model starts anew from that point for derived tool state. Tool discoveries and on-demand capability loads before the boundary reset, so their tools are hidden again until searched for or loaded after the boundary. Searchable tools remain in the corpus and all registered tools remain callable if the model emits a valid call, even when their earlier schema or reveal evidence is no longer visible to the model. Capability and toolset authors should apply the same rule to their own derived state: compute anything the model needs to have seen — announcements, disclosures, catalogs — from [post_compaction_window][pydantic_ai.messages.post_compaction_window] rather than remembering it in instance attributes, so it self-heals when compaction replaces the history that carried it.
[CompactionPart][pydantic_ai.messages.CompactionPart]s round-trip through the UI adapters, whose protocols have the client transmit the full conversation history on each request, so compacted conversations keep working with such frontends. A client-submitted compaction item is honored — the conversation stays compacted — but it is never trusted to stand in for the agent's system prompt: that still reaches the model on every request, as described under Loading untrusted history.
If a run also receives its own server-side history — the server-side persistence pattern, where stored messages are passed as message_history and client messages only supply the latest turn — client-submitted compaction items are ignored instead. A compaction item marks a boundary before which nothing is sent to the model, so honoring one from the client would let it hide the server's own stored history from the model and substitute its summary for that context. Client-submitted compaction items are only honored when the client-transmitted messages are the entire conversation.
Even then, a client can replay any compaction item the server's provider account has ever produced — opaque encrypted state on OpenAI, a plaintext summary on Anthropic. That is equivalent in kind to fabricating plain-text history, which client-transmitted history always permits (see Trust boundary for client-supplied history), with one difference: the server cannot inspect what an opaque item contains. If that matters for your deployment, keep the history server-side: persist the full message list keyed by conversation, send the client only display data, and pass the stored messages as message_history on each run. Don't trim the stored history around compaction boundaries yourself — each model adapter already omits what its own provider's compaction replaces, while models from other providers, which ignore a foreign compaction item, still get the full earlier history they need.
To compact on any model, edit the message history yourself with a history processor wrapped as a [ProcessHistory][pydantic_ai.capabilities.ProcessHistory] capability — this works with every provider. Common patterns:
Pydantic AI Harness packages a menu of ready-made, model-agnostic compaction strategies: mostly zero-LLM history editing — sliding-window trimming, clearing old tool results, deduplicating repeated file reads, clamping oversized message parts — plus LLM summarization for when that's not enough, and a TieredCompaction orchestrator (the recommended default) that escalates from cheap to expensive strategies only as far as needed to fit the target.