pydantic-deep supports conversation checkpointing — saving snapshots of conversation state at key points so the agent (or user) can rewind to any checkpoint or fork from a checkpoint into a new session.
from pydantic_deep import create_deep_agent, InMemoryCheckpointStore
store = InMemoryCheckpointStore()
agent = create_deep_agent(
include_checkpoints=True,
checkpoint_store=store,
checkpoint_frequency="every_tool", # auto-save after each tool call
)The agent now has three tools:
| Tool | Description |
|---|---|
save_checkpoint |
Label the most recent auto-checkpoint with a custom name |
list_checkpoints |
Show all saved checkpoints with labels and metadata |
rewind_to |
Rewind conversation to a checkpoint (raises RewindRequested) |
| Parameter | Type | Default | Description |
|---|---|---|---|
include_checkpoints |
bool |
False |
Enable checkpointing |
checkpoint_frequency |
str |
"every_tool" |
When to auto-save: "every_tool", "every_turn", or "manual_only" |
max_checkpoints |
int |
20 |
Maximum checkpoints to keep (oldest pruned first) |
checkpoint_store |
CheckpointStore |
InMemoryCheckpointStore() |
Storage backend for checkpoints |
=== "every_tool" Saves a checkpoint after each tool call. Provides the finest granularity for rewinding.
```python
agent = create_deep_agent(
include_checkpoints=True,
checkpoint_frequency="every_tool",
)
```
=== "every_turn" Saves a checkpoint before each model request (i.e. at the start of each turn).
```python
agent = create_deep_agent(
include_checkpoints=True,
checkpoint_frequency="every_turn",
)
```
=== "manual_only"
No auto-checkpoints. The agent must explicitly call save_checkpoint.
```python
agent = create_deep_agent(
include_checkpoints=True,
checkpoint_frequency="manual_only",
)
```
Default store. Checkpoints are kept in a Python dict — fast, but lost when the process exits.
from pydantic_deep import InMemoryCheckpointStore
store = InMemoryCheckpointStore()Persists checkpoints as JSON files on disk. Each checkpoint is a separate file in the given directory.
from pydantic_deep import FileCheckpointStore
store = FileCheckpointStore("/path/to/checkpoints")Messages are serialized using pydantic-ai's ModelMessagesTypeAdapter, so all message types (text, tool calls, tool results) are preserved.
Implement the [CheckpointStore][pydantic_deep.toolsets.checkpointing.CheckpointStore] protocol:
from pydantic_deep.toolsets.checkpointing import Checkpoint, CheckpointStore
class RedisCheckpointStore:
async def save(self, checkpoint: Checkpoint) -> None: ...
async def get(self, checkpoint_id: str) -> Checkpoint | None: ...
async def get_by_label(self, label: str) -> Checkpoint | None: ...
async def list_all(self) -> list[Checkpoint]: ...
async def remove(self, checkpoint_id: str) -> bool: ...
async def remove_oldest(self) -> bool: ...
async def count(self) -> int: ...
async def clear(self) -> None: ...When the agent calls rewind_to, a [RewindRequested][pydantic_deep.toolsets.checkpointing.RewindRequested] exception propagates out of agent.run(). Your application loop catches it and restores the conversation:
from pydantic_deep import create_deep_agent, DeepAgentDeps, RewindRequested
from pydantic_ai_backends import StateBackend
agent = create_deep_agent(include_checkpoints=True)
deps = DeepAgentDeps(backend=StateBackend())
message_history = []
while True:
user_input = input("> ")
try:
result = await agent.run(
user_input,
deps=deps,
message_history=message_history,
)
message_history = result.all_messages()
print(result.output)
except RewindRequested as e:
print(f"Rewinding to '{e.label}'...")
message_history = e.messagesFork creates a new session from a checkpoint — the original session stays unchanged.
from pydantic_deep import fork_from_checkpoint, InMemoryCheckpointStore
store = InMemoryCheckpointStore()
# ... after some conversation with checkpoints saved ...
# Fork from a specific checkpoint
messages = await fork_from_checkpoint(store, checkpoint_id="abc123")
# Start a new session with the forked history
new_result = await agent.run(
"Try a different approach",
deps=new_deps,
message_history=messages,
)For multi-user applications, set checkpoint_store on deps at runtime:
from pydantic_deep import DeepAgentDeps, FileCheckpointStore
# Each user gets their own checkpoint store
deps = DeepAgentDeps(
backend=backend,
checkpoint_store=FileCheckpointStore(f"/checkpoints/{user_id}"),
)The middleware resolves the store from deps.checkpoint_store first, falling back to the store passed at agent creation time.
!!! warning "Multi-User Applications"
Always create a fresh CheckpointStore per user session. A shared
InMemoryCheckpointStore means all users see each other's checkpoints.
See Multi-User Guide.
| Component | Description |
|---|---|
[Checkpoint][pydantic_deep.toolsets.checkpointing.Checkpoint] |
Immutable snapshot: id, label, turn, messages, metadata |
[CheckpointStore][pydantic_deep.toolsets.checkpointing.CheckpointStore] |
Protocol for storage backends |
[InMemoryCheckpointStore][pydantic_deep.toolsets.checkpointing.InMemoryCheckpointStore] |
Default in-memory store |
[FileCheckpointStore][pydantic_deep.toolsets.checkpointing.FileCheckpointStore] |
Persistent JSON file store |
[CheckpointMiddleware][pydantic_deep.toolsets.checkpointing.CheckpointMiddleware] |
Auto-save middleware |
[CheckpointToolset][pydantic_deep.toolsets.checkpointing.CheckpointToolset] |
Agent tools (save, list, rewind) |
[RewindRequested][pydantic_deep.toolsets.checkpointing.RewindRequested] |
Exception for app-level rewind |
[fork_from_checkpoint][pydantic_deep.toolsets.checkpointing.fork_from_checkpoint] |
Utility for session forking |
- Teams — Multi-agent collaboration with shared state
- Hooks — Claude Code-style lifecycle hooks
- Human-in-the-Loop — Approval workflows