Checkpointers¶
State persistence between agent runs. S3-compatible object storage (S3 / MinIO / R2 via boto3) is a production backend, alongside Redis, PostgreSQL, MySQL, and OpenSearch.
For long-term memory (durable KV store, semantic recall), see
Memory. This page covers the per-run state snapshot
contract used by AgentConfig.checkpointer.
Contract¶
BaseCheckpointer ¶
Bases: ABC
Abstract base class for checkpointer implementations.
Checkpointers handle saving and loading agent state, enabling features like: - Conversation persistence - Session recovery - Branching conversations - State inspection and debugging - Full-text search (backend-dependent) - Metadata queries (backend-dependent)
All methods are async to support various backends (file, database, network storage, etc.).
Use the capabilities property to check which features are available
before calling extended methods.
Example
if checkpointer.capabilities.search: ... results = await checkpointer.search("error handling") if checkpointer.capabilities.branching: ... await checkpointer.copy_thread("main", "experiment")
capabilities
property
¶
Return the capabilities of this checkpointer.
Override in subclasses to advertise supported features.
save
abstractmethod
async
¶
save(state: AgentState, thread_id: str, checkpoint_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Save agent state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
AgentState
|
Current agent state to persist |
required |
thread_id
|
str
|
Unique identifier for the conversation thread |
required |
checkpoint_id
|
str | None
|
Optional specific checkpoint ID. If not provided, a new ID will be generated. |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata for querying/filtering checkpoints |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Checkpoint ID that can be used to restore this state |
Source code in .sdk/src/tulip/memory/checkpointer.py
load
abstractmethod
async
¶
Load agent state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier to load from |
required |
checkpoint_id
|
str | None
|
Optional specific checkpoint ID. If not provided, loads the latest checkpoint. |
None
|
Returns:
| Type | Description |
|---|---|
AgentState | None
|
Restored AgentState or None if not found |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_checkpoints
abstractmethod
async
¶
List available checkpoints for a thread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
limit
|
int
|
Maximum number of checkpoint IDs to return |
10
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of checkpoint IDs, newest first |
Source code in .sdk/src/tulip/memory/checkpointer.py
delete
async
¶
Delete a checkpoint or all checkpoints for a thread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to delete. If None, deletes all checkpoints for the thread. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if deletion was successful |
Source code in .sdk/src/tulip/memory/checkpointer.py
exists
async
¶
Check if a checkpoint exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to check. If None, checks if any checkpoint exists for the thread. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the checkpoint exists |
Source code in .sdk/src/tulip/memory/checkpointer.py
close
async
¶
Close any resources (connections, files, etc.).
Override in subclasses if cleanup is needed.
search
async
¶
Full-text search across checkpoints.
Requires: capabilities.search = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query |
required |
limit
|
int
|
Maximum results |
10
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints with scores |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If backend doesn't support search |
Source code in .sdk/src/tulip/memory/checkpointer.py
query_by_metadata
async
¶
Query checkpoints by metadata field.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Metadata field name |
required |
value
|
Any
|
Value to match |
required |
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
get_metadata
async
¶
Get checkpoint metadata.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint (latest if None) |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
Metadata dict or None if not found |
Source code in .sdk/src/tulip/memory/checkpointer.py
vacuum
async
¶
Delete old checkpoints.
Requires: capabilities.vacuum = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
older_than_days
|
int
|
Delete checkpoints older than this |
30
|
Returns:
| Type | Description |
|---|---|
int
|
Number of deleted checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
copy_thread
async
¶
Copy a thread to create a branch.
Requires: capabilities.branching = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_thread_id
|
str
|
Source thread to copy from |
required |
dest_thread_id
|
str
|
Destination thread ID |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if successful |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_threads
async
¶
List all thread IDs.
Requires: capabilities.list_threads = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum threads to return |
100
|
pattern
|
str
|
Pattern to filter threads (backend-specific) |
'*'
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of thread IDs |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_with_metadata
async
¶
List checkpoints with their metadata.
Requires: capabilities.list_with_metadata = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of {thread_id, checkpoint_id, metadata, ...} dicts |
Source code in .sdk/src/tulip/memory/checkpointer.py
CheckpointerCapabilities
dataclass
¶
CheckpointerCapabilities(search: bool = False, metadata_query: bool = False, vacuum: bool = False, branching: bool = False, ttl: bool = False, list_threads: bool = False, list_with_metadata: bool = False, persistent_checkpoint_ids: bool = False)
Capabilities supported by a checkpointer.
Use this to discover what features a checkpointer supports before calling optional methods.
Example
if checkpointer.capabilities.search: ... results = await checkpointer.search("error handling")
Object storage¶
S3Backend ¶
S3Backend(bucket: str, prefix: str = 'tulip/checkpoints/', *, endpoint_url: str | None = None, region_name: str | None = None, aws_access_key_id: str | None = None, aws_secret_access_key: str | None = None, create_bucket: bool = True, _client: Any = None)
Bases: BaseCheckpointer
S3-compatible object-storage-backed checkpointer.
Example (AWS S3, ambient credentials)::
checkpointer = S3Backend(bucket="my-checkpoints")
agent = Agent(config=cfg, checkpointer=checkpointer)
Example (MinIO / local)::
checkpointer = S3Backend(
bucket="checkpoints",
endpoint_url="http://localhost:9000",
aws_access_key_id="minioadmin",
aws_secret_access_key="minioadmin",
)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bucket
|
str
|
Bucket name. |
required |
prefix
|
str
|
Key prefix for all objects. Default |
'tulip/checkpoints/'
|
endpoint_url
|
str | None
|
Custom S3 endpoint (set for MinIO / R2 / B2). |
None
|
region_name
|
str | None
|
AWS region (also used when auto-creating the bucket). |
None
|
aws_access_key_id
|
str | None
|
Explicit access key. When |
None
|
aws_secret_access_key
|
str | None
|
Explicit secret key (pairs with
|
None
|
create_bucket
|
bool
|
Create the bucket on first use if it doesn't exist. Defaults to True (convenient for dev / MinIO / tests). |
True
|
_client
|
Any
|
Injection seam for tests — a pre-built boto3 S3 client bypasses the lazy import and credential resolution. |
None
|
Capabilities: metadata_query, vacuum, branching,
list_threads, list_with_metadata, persistent_checkpoint_ids.
Source code in .sdk/src/tulip/memory/backends/s3.py
exists
async
¶
Check if a checkpoint exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to check. If None, checks if any checkpoint exists for the thread. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the checkpoint exists |
Source code in .sdk/src/tulip/memory/checkpointer.py
search
async
¶
Full-text search across checkpoints.
Requires: capabilities.search = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query |
required |
limit
|
int
|
Maximum results |
10
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints with scores |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If backend doesn't support search |
Source code in .sdk/src/tulip/memory/checkpointer.py
query_by_metadata
async
¶
Query checkpoints by metadata field.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Metadata field name |
required |
value
|
Any
|
Value to match |
required |
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
Other backends¶
The file-system, HTTP-API, and in-memory backends implement the
BaseCheckpointer contract natively. Redis, PostgreSQL, MySQL, and
OpenSearch ship as simple key-value backends (Pydantic models) that the
StorageBackendAdapter (below) wraps into the same contract — use the
factory functions rather than passing them to AgentConfig.checkpointer
directly.
RedisBackend ¶
Bases: BaseModel
Redis checkpoint backend.
Fast key-value storage with optional TTL for checkpoints.
Example
backend = RedisBackend(url="redis://localhost:6379") await backend.save("thread_1", state.model_dump()) data = await backend.load("thread_1")
Source code in .sdk/src/tulip/memory/backends/redis.py
save
async
¶
Save checkpoint to Redis.
Source code in .sdk/src/tulip/memory/backends/redis.py
load
async
¶
Load checkpoint from Redis.
Source code in .sdk/src/tulip/memory/backends/redis.py
delete
async
¶
Delete checkpoint from Redis.
exists
async
¶
Check if checkpoint exists.
list_threads
async
¶
List all thread IDs matching pattern.
Source code in .sdk/src/tulip/memory/backends/redis.py
PostgreSQLBackend ¶
PostgreSQLBackend(host: str = 'localhost', port: int = 5432, database: str = 'tulip', user: str = 'postgres', password: str | SecretStr = '', dsn: str | None = None, **kwargs: Any)
Bases: BaseModel
PostgreSQL checkpoint backend.
Production-grade persistent storage with ACID guarantees.
Features: - Connection pooling - Transaction support - JSON/JSONB storage - Indexing for fast lookups - Concurrent access safe
Example
backend = PostgreSQLBackend( ... host="localhost", ... database="myapp", ... user="postgres", ... password="secret", ... ) await backend.save("thread_1", state.model_dump()) data = await backend.load("thread_1")
With DSN
backend = PostgreSQLBackend(dsn="postgresql://user:pass@localhost:5432/mydb")
Source code in .sdk/src/tulip/memory/backends/postgresql.py
save
async
¶
save(thread_id: str, data: dict[str, Any], checkpoint_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Save checkpoint to PostgreSQL.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
data
|
dict[str, Any]
|
Checkpoint data |
required |
checkpoint_id
|
str | None
|
Optional checkpoint ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata for querying |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Checkpoint ID |
Source code in .sdk/src/tulip/memory/backends/postgresql.py
load
async
¶
Load checkpoint from PostgreSQL.
Source code in .sdk/src/tulip/memory/backends/postgresql.py
delete
async
¶
Delete checkpoint from PostgreSQL.
Source code in .sdk/src/tulip/memory/backends/postgresql.py
exists
async
¶
Check if checkpoint exists.
Source code in .sdk/src/tulip/memory/backends/postgresql.py
list_threads
async
¶
List all thread IDs matching pattern.
Source code in .sdk/src/tulip/memory/backends/postgresql.py
get_metadata
async
¶
Get checkpoint metadata.
Source code in .sdk/src/tulip/memory/backends/postgresql.py
query_by_metadata
async
¶
Query checkpoints by metadata field.
Uses PostgreSQL JSONB operators for efficient querying.
Source code in .sdk/src/tulip/memory/backends/postgresql.py
search_data
async
¶
Search checkpoints by data field using JSON path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
JSON path (e.g., "messages", "confidence") |
required |
value
|
Any
|
Value to match |
required |
limit
|
int
|
Maximum results |
100
|
Example
results = await backend.search_data("agent_id", "agent-123")
Source code in .sdk/src/tulip/memory/backends/postgresql.py
count
async
¶
Count checkpoints matching pattern.
Source code in .sdk/src/tulip/memory/backends/postgresql.py
vacuum
async
¶
Delete old checkpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
older_than_days
|
int
|
Delete checkpoints older than this |
30
|
Returns:
| Type | Description |
|---|---|
int
|
Number of deleted rows |
Source code in .sdk/src/tulip/memory/backends/postgresql.py
MySQLBackend ¶
MySQLBackend(host: str = 'localhost', port: int = 3306, database: str = 'tulip', user: str = 'root', password: str | SecretStr = '', dsn: str | None = None, **kwargs: Any)
Bases: BaseModel
MySQL checkpoint backend.
Production-grade persistent storage using the official MySQL Connector/Python asyncio API.
Features: - Async connection pooling - JSON storage - Upsert-based checkpoint replacement - Metadata queries via MySQL JSON functions
Example
backend = MySQLBackend( ... host="localhost", ... database="myapp", ... user="root", ... password="secret", ... ) await backend.save("thread_1", state.model_dump()) data = await backend.load("thread_1")
With DSN
backend = MySQLBackend(dsn="mysql://user:pass@localhost:3306/mydb")
Source code in .sdk/src/tulip/memory/backends/mysql.py
save
async
¶
save(thread_id: str, data: dict[str, Any], checkpoint_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Save checkpoint to MySQL.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
data
|
dict[str, Any]
|
Checkpoint data |
required |
checkpoint_id
|
str | None
|
Optional checkpoint ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata for querying |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Checkpoint ID |
Source code in .sdk/src/tulip/memory/backends/mysql.py
load
async
¶
Load checkpoint from MySQL.
Source code in .sdk/src/tulip/memory/backends/mysql.py
delete
async
¶
Delete checkpoint from MySQL.
Source code in .sdk/src/tulip/memory/backends/mysql.py
exists
async
¶
Check if checkpoint exists.
Source code in .sdk/src/tulip/memory/backends/mysql.py
list_threads
async
¶
List all thread IDs matching pattern.
Source code in .sdk/src/tulip/memory/backends/mysql.py
get_metadata
async
¶
Get checkpoint metadata.
Source code in .sdk/src/tulip/memory/backends/mysql.py
query_by_metadata
async
¶
Query checkpoints by metadata field.
Uses MySQL JSON_CONTAINS against a one-key JSON object.
Source code in .sdk/src/tulip/memory/backends/mysql.py
search_data
async
¶
Search checkpoints by a top-level data field.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Top-level JSON key (for example, "messages" or "agent_id") |
required |
value
|
Any
|
Value to match |
required |
limit
|
int
|
Maximum results |
100
|
Source code in .sdk/src/tulip/memory/backends/mysql.py
count
async
¶
Count checkpoints matching pattern.
Source code in .sdk/src/tulip/memory/backends/mysql.py
vacuum
async
¶
Delete old checkpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
older_than_days
|
int
|
Delete checkpoints older than this |
30
|
Returns:
| Type | Description |
|---|---|
int
|
Number of deleted rows |
Source code in .sdk/src/tulip/memory/backends/mysql.py
OpenSearchBackend ¶
OpenSearchBackend(hosts: list[str] | None = None, index_name: str = 'tulip-checkpoints', username: str | None = None, password: str | None = None, **kwargs: Any)
Bases: BaseModel
OpenSearch checkpoint backend.
Scalable document storage with full-text search capabilities.
Example
backend = OpenSearchBackend(hosts=["localhost:9200"]) await backend.save("thread_1", state.model_dump()) data = await backend.load("thread_1") results = await backend.search("user query")
Source code in .sdk/src/tulip/memory/backends/opensearch.py
save
async
¶
Save checkpoint to OpenSearch.
Source code in .sdk/src/tulip/memory/backends/opensearch.py
load
async
¶
Load checkpoint from OpenSearch.
Source code in .sdk/src/tulip/memory/backends/opensearch.py
delete
async
¶
Delete checkpoint from OpenSearch.
Source code in .sdk/src/tulip/memory/backends/opensearch.py
exists
async
¶
Check if checkpoint exists.
Source code in .sdk/src/tulip/memory/backends/opensearch.py
list_threads
async
¶
List all thread IDs.
Source code in .sdk/src/tulip/memory/backends/opensearch.py
search
async
¶
Search checkpoints by content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query |
required |
limit
|
int
|
Maximum results |
10
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints with scores |
Source code in .sdk/src/tulip/memory/backends/opensearch.py
get_by_metadata
async
¶
Get checkpoints by metadata field.
Source code in .sdk/src/tulip/memory/backends/opensearch.py
FileCheckpointer ¶
Bases: BaseCheckpointer
File-based checkpointer for persistent local storage.
Stores each checkpoint as a JSON file, organized by thread ID. Provides durable storage that survives process restarts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_dir
|
str | Path
|
Base directory for checkpoint storage. Defaults to ".tulip_checkpoints" in current directory. |
'.tulip_checkpoints'
|
pretty
|
bool
|
Whether to format JSON for readability (default True) |
True
|
Example
Source code in .sdk/src/tulip/memory/backends/file.py
capabilities
property
¶
Return the capabilities of this checkpointer.
Override in subclasses to advertise supported features.
exists
async
¶
Check if a checkpoint exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to check. If None, checks if any checkpoint exists for the thread. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the checkpoint exists |
Source code in .sdk/src/tulip/memory/checkpointer.py
close
async
¶
Close any resources (connections, files, etc.).
Override in subclasses if cleanup is needed.
search
async
¶
Full-text search across checkpoints.
Requires: capabilities.search = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query |
required |
limit
|
int
|
Maximum results |
10
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints with scores |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If backend doesn't support search |
Source code in .sdk/src/tulip/memory/checkpointer.py
query_by_metadata
async
¶
Query checkpoints by metadata field.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Metadata field name |
required |
value
|
Any
|
Value to match |
required |
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
get_metadata
async
¶
Get checkpoint metadata.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint (latest if None) |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
Metadata dict or None if not found |
Source code in .sdk/src/tulip/memory/checkpointer.py
vacuum
async
¶
Delete old checkpoints.
Requires: capabilities.vacuum = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
older_than_days
|
int
|
Delete checkpoints older than this |
30
|
Returns:
| Type | Description |
|---|---|
int
|
Number of deleted checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
copy_thread
async
¶
Copy a thread to create a branch.
Requires: capabilities.branching = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_thread_id
|
str
|
Source thread to copy from |
required |
dest_thread_id
|
str
|
Destination thread ID |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if successful |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_threads
async
¶
List all thread IDs.
Requires: capabilities.list_threads = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum threads to return |
100
|
pattern
|
str
|
Pattern to filter threads (backend-specific) |
'*'
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of thread IDs |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_with_metadata
async
¶
List checkpoints with their metadata.
Requires: capabilities.list_with_metadata = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of {thread_id, checkpoint_id, metadata, ...} dicts |
Source code in .sdk/src/tulip/memory/checkpointer.py
save
async
¶
save(state: AgentState, thread_id: str, checkpoint_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Save agent state to a JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
AgentState
|
Current agent state |
required |
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Optional specific checkpoint ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata for querying/filtering checkpoints |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Checkpoint ID for the saved state |
Source code in .sdk/src/tulip/memory/backends/file.py
load
async
¶
Load agent state from a JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint ID (latest if None) |
None
|
Returns:
| Type | Description |
|---|---|
AgentState | None
|
Restored AgentState or None if not found |
Source code in .sdk/src/tulip/memory/backends/file.py
list_checkpoints
async
¶
List available checkpoints for a thread.
Reads checkpoint files and returns IDs sorted by creation time (newest first).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
limit
|
int
|
Maximum number to return |
10
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of checkpoint IDs, newest first |
Source code in .sdk/src/tulip/memory/backends/file.py
delete
async
¶
Delete checkpoint file(s).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to delete (all if None) |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if deletion was successful |
Source code in .sdk/src/tulip/memory/backends/file.py
get_storage_path ¶
get_disk_usage
async
¶
Get total disk usage in bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str | None
|
Specific thread (all threads if None) |
None
|
Returns:
| Type | Description |
|---|---|
int
|
Total size in bytes |
Source code in .sdk/src/tulip/memory/backends/file.py
HTTPCheckpointer ¶
HTTPCheckpointer(base_url: str, headers: dict[str, str] | None = None, auth: tuple[str, str] | None = None, timeout: float = 30.0)
Bases: BaseCheckpointer
HTTP API-based checkpointer for remote storage.
Stores checkpoints via HTTP API calls, suitable for distributed systems or cloud-based storage backends.
The API is expected to implement the following endpoints: - POST /threads/{thread_id}/checkpoints - Create checkpoint - GET /threads/{thread_id}/checkpoints/{checkpoint_id} - Get checkpoint - GET /threads/{thread_id}/checkpoints - List checkpoints - DELETE /threads/{thread_id}/checkpoints/{checkpoint_id} - Delete checkpoint
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_url
|
str
|
Base URL of the checkpoint API |
required |
headers
|
dict[str, str] | None
|
Additional headers to include in requests |
None
|
auth
|
tuple[str, str] | None
|
Authentication tuple (username, password) for basic auth |
None
|
timeout
|
float
|
Request timeout in seconds |
30.0
|
Example
Source code in .sdk/src/tulip/memory/backends/http.py
capabilities
property
¶
Return the capabilities of this checkpointer.
Override in subclasses to advertise supported features.
exists
async
¶
Check if a checkpoint exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to check. If None, checks if any checkpoint exists for the thread. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the checkpoint exists |
Source code in .sdk/src/tulip/memory/checkpointer.py
search
async
¶
Full-text search across checkpoints.
Requires: capabilities.search = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query |
required |
limit
|
int
|
Maximum results |
10
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints with scores |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If backend doesn't support search |
Source code in .sdk/src/tulip/memory/checkpointer.py
query_by_metadata
async
¶
Query checkpoints by metadata field.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Metadata field name |
required |
value
|
Any
|
Value to match |
required |
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
get_metadata
async
¶
Get checkpoint metadata.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint (latest if None) |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
Metadata dict or None if not found |
Source code in .sdk/src/tulip/memory/checkpointer.py
vacuum
async
¶
Delete old checkpoints.
Requires: capabilities.vacuum = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
older_than_days
|
int
|
Delete checkpoints older than this |
30
|
Returns:
| Type | Description |
|---|---|
int
|
Number of deleted checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
copy_thread
async
¶
Copy a thread to create a branch.
Requires: capabilities.branching = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_thread_id
|
str
|
Source thread to copy from |
required |
dest_thread_id
|
str
|
Destination thread ID |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if successful |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_threads
async
¶
List all thread IDs.
Requires: capabilities.list_threads = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum threads to return |
100
|
pattern
|
str
|
Pattern to filter threads (backend-specific) |
'*'
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of thread IDs |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_with_metadata
async
¶
List checkpoints with their metadata.
Requires: capabilities.list_with_metadata = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of {thread_id, checkpoint_id, metadata, ...} dicts |
Source code in .sdk/src/tulip/memory/checkpointer.py
close
async
¶
save
async
¶
save(state: AgentState, thread_id: str, checkpoint_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Save agent state via HTTP POST.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
AgentState
|
Current agent state |
required |
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Optional specific checkpoint ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata for querying/filtering checkpoints |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Checkpoint ID for the saved state |
Raises:
| Type | Description |
|---|---|
HTTPError
|
If the request fails |
Source code in .sdk/src/tulip/memory/backends/http.py
load
async
¶
Load agent state via HTTP GET.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint ID (latest if None) |
None
|
Returns:
| Type | Description |
|---|---|
AgentState | None
|
Restored AgentState or None if not found |
Source code in .sdk/src/tulip/memory/backends/http.py
list_checkpoints
async
¶
List available checkpoints via HTTP GET.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
limit
|
int
|
Maximum number to return |
10
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of checkpoint IDs, newest first |
Source code in .sdk/src/tulip/memory/backends/http.py
delete
async
¶
Delete checkpoint(s) via HTTP DELETE.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to delete (all if None) |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if deletion was successful |
Source code in .sdk/src/tulip/memory/backends/http.py
health_check
async
¶
Check if the API is reachable.
Returns:
| Type | Description |
|---|---|
bool
|
True if the API responds successfully |
Source code in .sdk/src/tulip/memory/backends/http.py
__aenter__
async
¶
__aexit__
async
¶
MemoryCheckpointer ¶
Bases: BaseCheckpointer
In-memory checkpointer for testing and development.
Stores all checkpoints in a dictionary. Data is not persistent and will be lost when the process terminates.
Useful for: - Unit and integration testing - Development and prototyping - Short-lived agent sessions - As a fast caching layer
Capabilities: - list_threads: Yes - persistent_checkpoint_ids: Yes (within process lifetime)
Example
Source code in .sdk/src/tulip/memory/backends/memory.py
exists
async
¶
Check if a checkpoint exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to check. If None, checks if any checkpoint exists for the thread. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the checkpoint exists |
Source code in .sdk/src/tulip/memory/checkpointer.py
close
async
¶
Close any resources (connections, files, etc.).
Override in subclasses if cleanup is needed.
search
async
¶
Full-text search across checkpoints.
Requires: capabilities.search = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query |
required |
limit
|
int
|
Maximum results |
10
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints with scores |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If backend doesn't support search |
Source code in .sdk/src/tulip/memory/checkpointer.py
query_by_metadata
async
¶
Query checkpoints by metadata field.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Metadata field name |
required |
value
|
Any
|
Value to match |
required |
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of matching checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
get_metadata
async
¶
Get checkpoint metadata.
Requires: capabilities.metadata_query = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint (latest if None) |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
Metadata dict or None if not found |
Source code in .sdk/src/tulip/memory/checkpointer.py
vacuum
async
¶
Delete old checkpoints.
Requires: capabilities.vacuum = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
older_than_days
|
int
|
Delete checkpoints older than this |
30
|
Returns:
| Type | Description |
|---|---|
int
|
Number of deleted checkpoints |
Source code in .sdk/src/tulip/memory/checkpointer.py
copy_thread
async
¶
Copy a thread to create a branch.
Requires: capabilities.branching = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_thread_id
|
str
|
Source thread to copy from |
required |
dest_thread_id
|
str
|
Destination thread ID |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if successful |
Source code in .sdk/src/tulip/memory/checkpointer.py
list_with_metadata
async
¶
List checkpoints with their metadata.
Requires: capabilities.list_with_metadata = True
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum results |
100
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of {thread_id, checkpoint_id, metadata, ...} dicts |
Source code in .sdk/src/tulip/memory/checkpointer.py
save
async
¶
save(state: AgentState, thread_id: str, checkpoint_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Save agent state to memory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
AgentState
|
Current agent state |
required |
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Optional specific checkpoint ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata for the checkpoint |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Checkpoint ID for the saved state |
Source code in .sdk/src/tulip/memory/backends/memory.py
load
async
¶
Load agent state from memory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint ID (latest if None) |
None
|
Returns:
| Type | Description |
|---|---|
AgentState | None
|
Restored AgentState or None if not found |
Source code in .sdk/src/tulip/memory/backends/memory.py
list_checkpoints
async
¶
List available checkpoints for a thread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
limit
|
int
|
Maximum number to return |
10
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of checkpoint IDs, newest first |
Source code in .sdk/src/tulip/memory/backends/memory.py
delete
async
¶
Delete checkpoint(s) from memory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
Thread identifier |
required |
checkpoint_id
|
str | None
|
Specific checkpoint to delete (all if None) |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if deletion was successful |
Source code in .sdk/src/tulip/memory/backends/memory.py
clear ¶
get_thread_ids ¶
list_threads
async
¶
List all thread IDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum threads to return |
100
|
pattern
|
str
|
Pattern to filter (supports * as wildcard) |
'*'
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of thread IDs |
Source code in .sdk/src/tulip/memory/backends/memory.py
get_checkpoint_count ¶
Get count of stored checkpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str | None
|
Specific thread (all threads if None) |
None
|
Returns:
| Type | Description |
|---|---|
int
|
Number of checkpoints |
Source code in .sdk/src/tulip/memory/backends/memory.py
Adapters¶
StorageBackendAdapter wraps any of the simple key-value backends
above into the full BaseCheckpointer interface. For the common
backends, use the factory functions — redis_checkpointer(...),
postgresql_checkpointer(...), mysql_checkpointer(...),
opensearch_checkpointer(...), s3_checkpointer(...) — which build
the adapter for you.
StorageBackendAdapter ¶
Bases: BaseCheckpointer
Adapter that wraps simple storage backends to implement BaseCheckpointer.
Storage backends have a simple interface: - save(thread_id: str, data: dict) -> None - load(thread_id: str) -> dict | None - delete(thread_id: str) -> bool - exists(thread_id: str) -> bool - list_threads() -> list[str]
This adapter converts between AgentState and dict representations.
Key improvement: Checkpoint IDs are now stored IN the backend, not in memory. This ensures persistence across restarts.
Storage schema:
- {thread_id}:{checkpoint_id} -> checkpoint data
- {thread_id}:latest -> latest checkpoint (for quick access)
- {thread_id}:_checkpoints -> list of checkpoint metadata
Example
from tulip.memory.backends import RedisBackend from tulip.memory.backends.adapters import StorageBackendAdapter
Create storage backend¶
storage = RedisBackend(url="redis://localhost:6379")
Wrap with adapter for use with Agent¶
checkpointer = StorageBackendAdapter(storage)
Use with Agent¶
agent = Agent(model=model, checkpointer=checkpointer)
Concurrency
The {thread}:_checkpoints index is updated under a per-thread
asyncio.Lock, so concurrent saves/removals to the same thread are
safe within a single process. The lock is per adapter instance and
does NOT serialize separate processes sharing one store, so
cross-process writes to the same thread can still drop index entries.
Making that case safe requires backend-native atomic index updates;
tracked in issue #301.
Initialize adapter with a storage backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backend
|
Any
|
Storage backend with save/load/delete/exists methods |
required |
Source code in .sdk/src/tulip/memory/backends/adapters.py
capabilities
property
¶
Derive capabilities from backend methods.
save
async
¶
save(state: AgentState, thread_id: str, checkpoint_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Save agent state with persistent checkpoint ID tracking.
Source code in .sdk/src/tulip/memory/backends/adapters.py
load
async
¶
Load agent state from the storage backend.
Source code in .sdk/src/tulip/memory/backends/adapters.py
list_checkpoints
async
¶
List available checkpoints from persistent index.
Source code in .sdk/src/tulip/memory/backends/adapters.py
delete
async
¶
Delete checkpoint(s) with index update.
Source code in .sdk/src/tulip/memory/backends/adapters.py
exists
async
¶
Check if checkpoint exists.
Source code in .sdk/src/tulip/memory/backends/adapters.py
search
async
¶
Delegate to backend search.
Source code in .sdk/src/tulip/memory/backends/adapters.py
query_by_metadata
async
¶
Delegate to backend metadata query.
Source code in .sdk/src/tulip/memory/backends/adapters.py
get_metadata
async
¶
Get checkpoint metadata from index or backend.
Source code in .sdk/src/tulip/memory/backends/adapters.py
vacuum
async
¶
Delegate to backend vacuum.
copy_thread
async
¶
Copy all checkpoints from one thread to another (branching).
Source code in .sdk/src/tulip/memory/backends/adapters.py
list_threads
async
¶
Delegate to backend list_threads.
Source code in .sdk/src/tulip/memory/backends/adapters.py
list_with_metadata
async
¶
Delegate to backend list_with_metadata.
Source code in .sdk/src/tulip/memory/backends/adapters.py
redis_checkpointer ¶
redis_checkpointer(url: str = 'redis://localhost:6379', prefix: str = 'tulip:state:', **kwargs: Any) -> StorageBackendAdapter
Create a Redis-backed checkpointer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Redis URL |
'redis://localhost:6379'
|
prefix
|
str
|
Key prefix for all checkpoints |
'tulip:state:'
|
**kwargs
|
Any
|
Additional RedisBackend options (ttl_seconds, db) |
{}
|
Returns:
| Type | Description |
|---|---|
StorageBackendAdapter
|
StorageBackendAdapter wrapping RedisBackend |
Capabilities
- ttl: Yes (via ttl_seconds)
- list_threads: Yes
- persistent_checkpoint_ids: Yes
Example
checkpointer = redis_checkpointer("redis://localhost:6379") agent = Agent(model=model, checkpointer=checkpointer)
Source code in .sdk/src/tulip/memory/backends/adapters.py
postgresql_checkpointer ¶
postgresql_checkpointer(host: str = 'localhost', port: int = 5432, database: str = 'tulip', user: str = 'postgres', password: str = '', dsn: str | None = None, **kwargs: Any) -> StorageBackendAdapter
Create a PostgreSQL-backed checkpointer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
host
|
str
|
PostgreSQL host |
'localhost'
|
port
|
int
|
PostgreSQL port |
5432
|
database
|
str
|
Database name |
'tulip'
|
user
|
str
|
Database user |
'postgres'
|
password
|
str
|
Database password |
''
|
dsn
|
str | None
|
Connection string (overrides other params) |
None
|
**kwargs
|
Any
|
Additional PostgreSQLBackend options |
{}
|
Returns:
| Type | Description |
|---|---|
StorageBackendAdapter
|
StorageBackendAdapter wrapping PostgreSQLBackend |
Capabilities
- search: Yes (via search_data)
- metadata_query: Yes (via query_by_metadata)
- vacuum: Yes
- list_threads: Yes
- persistent_checkpoint_ids: Yes
Example
checkpointer = postgresql_checkpointer(database="myapp") agent = Agent(model=model, checkpointer=checkpointer)
Source code in .sdk/src/tulip/memory/backends/adapters.py
mysql_checkpointer ¶
mysql_checkpointer(host: str = 'localhost', port: int = 3306, database: str = 'tulip', user: str = 'root', password: str = '', dsn: str | None = None, **kwargs: Any) -> StorageBackendAdapter
Create a MySQL-backed checkpointer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
host
|
str
|
MySQL host |
'localhost'
|
port
|
int
|
MySQL port |
3306
|
database
|
str
|
Database name |
'tulip'
|
user
|
str
|
Database user |
'root'
|
password
|
str
|
Database password |
''
|
dsn
|
str | None
|
Connection string (overrides other params) |
None
|
**kwargs
|
Any
|
Additional MySQLBackend options |
{}
|
Returns:
| Type | Description |
|---|---|
StorageBackendAdapter
|
StorageBackendAdapter wrapping MySQLBackend |
Capabilities
- search: Yes (via search_data)
- metadata_query: Yes (via query_by_metadata)
- vacuum: Yes
- list_threads: Yes
- persistent_checkpoint_ids: Yes
Example
checkpointer = mysql_checkpointer(database="myapp") agent = Agent(model=model, checkpointer=checkpointer)
Source code in .sdk/src/tulip/memory/backends/adapters.py
opensearch_checkpointer ¶
opensearch_checkpointer(hosts: list[str] | None = None, index_name: str = 'tulip-checkpoints', **kwargs: Any) -> StorageBackendAdapter
Create an OpenSearch-backed checkpointer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hosts
|
list[str] | None
|
OpenSearch hosts |
None
|
index_name
|
str
|
Index name for checkpoints |
'tulip-checkpoints'
|
**kwargs
|
Any
|
Additional OpenSearchBackend options (username, password, use_ssl) |
{}
|
Returns:
| Type | Description |
|---|---|
StorageBackendAdapter
|
StorageBackendAdapter wrapping OpenSearchBackend |
Capabilities
- search: Yes (full-text search)
- metadata_query: Yes (via get_by_metadata)
- list_threads: Yes
- persistent_checkpoint_ids: Yes
Example
checkpointer = opensearch_checkpointer(hosts=["localhost:9200"]) agent = Agent(model=model, checkpointer=checkpointer)
Source code in .sdk/src/tulip/memory/backends/adapters.py
s3_checkpointer ¶
Create an S3-compatible object-storage-backed checkpointer.
Works against AWS S3, MinIO, Cloudflare R2, and any other
S3-compatible endpoint. S3Backend is a native BaseCheckpointer;
this factory is a thin convenience alias for parity with the other
backend factories.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bucket
|
str
|
Bucket name |
required |
prefix
|
str
|
Object key prefix |
'tulip/checkpoints/'
|
**kwargs
|
Any
|
Additional S3Backend options (endpoint_url, region_name, aws_access_key_id, aws_secret_access_key) |
{}
|
Example
checkpointer = s3_checkpointer( ... bucket="my-checkpoints", ... endpoint_url="http://localhost:9000", # MinIO ... ) agent = Agent(config=cfg, checkpointer=checkpointer)