Skip to content

fix(checkpoint-redis): resolve key generation inconsistency in shallo… #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,4 @@ libs/redis/docs/.Trash*
.python-version
.idea/*
examples/.Trash*
.claude
28 changes: 19 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Development Commands

### Setup and Dependencies

```bash
make install # Install all dependencies with poetry
make redis-start # Start Redis Stack container (includes RedisJSON and RediSearch)
make redis-stop # Stop Redis container
poetry install --all-extras # Install all dependencies with poetry (from README)
make redis-start # Start Redis Stack container (includes RedisJSON and RediSearch)
make redis-stop # Stop Redis container
```

### Testing

```bash
make test # Run tests with verbose output
make test-all # Run all tests including API tests
make test # Run tests with verbose output
make test-all # Run all tests including API tests
pytest tests/test_specific.py # Run specific test file
pytest tests/test_specific.py::test_function # Run specific test
pytest --run-api-tests # Include API integration tests
pytest --run-api-tests # Include API integration tests
```

### Code Quality

```bash
make lint # Format code and run type checking
make format # Format code with black and isort
make lint # Run formatting, type checking, and other linters
make check-types # Run mypy type checking
make check # Run both linting and tests
```

### Development

```bash
make clean # Remove cache and build artifacts
```
Expand All @@ -38,12 +42,14 @@ make clean # Remove cache and build artifacts
### Core Components

**Checkpoint Savers** (`langgraph/checkpoint/redis/`):

- `base.py`: `BaseRedisSaver` - Abstract base class with shared Redis operations, schemas, and TTL management
- `__init__.py`: `RedisSaver` - Standard sync implementation
- `__init__.py`: `RedisSaver` - Standard sync implementation
- `aio.py`: `AsyncRedisSaver` - Async implementation
- `shallow.py` / `ashallow.py`: Shallow variants that store only latest checkpoint

**Stores** (`langgraph/store/redis/`):

- `base.py`: `BaseRedisStore` - Abstract base with Redis operations, vector search, and TTL support
- `__init__.py`: `RedisStore` - Sync store with key-value and vector search
- `aio.py`: `AsyncRedisStore` - Async store implementation
Expand All @@ -63,14 +69,17 @@ make clean # Remove cache and build artifacts
**Type System**: Heavy use of generics (`BaseRedisSaver[RedisClientType, IndexType]`) to maintain type safety across sync/async variants while sharing implementation code.

### Redis Key Patterns

- Checkpoints: `checkpoint:{thread_id}:{namespace}:{checkpoint_id}`
- Checkpoint blobs: `checkpoint_blob:{thread_id}:{namespace}:{channel}:{version}`
- Checkpoint writes: `checkpoint_write:{thread_id}:{namespace}:{checkpoint_id}:{task_id}`
- Store items: `store:{uuid}`
- Store vectors: `store_vectors:{uuid}`

### Testing Strategy

Tests are organized by functionality:

- `test_sync.py` / `test_async.py`: Core checkpoint functionality
- `test_store.py` / `test_async_store.py`: Store operations
- `test_cluster_mode.py`: Redis Cluster specific tests
Expand All @@ -79,7 +88,8 @@ Tests are organized by functionality:
- `test_semantic_search_*.py`: Vector search capabilities

### Important Dependencies

- Requires Redis with RedisJSON and RediSearch modules
- Uses `redisvl` for vector operations and search index management
- Uses `python-ulid` for unique document IDs
- Integrates with LangGraph's checkpoint and store base classes
- Integrates with LangGraph's checkpoint and store base classes
4 changes: 2 additions & 2 deletions langgraph/checkpoint/redis/aio.py
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ async def _aload_pending_sends(
num_results=100,
)
res = await self.checkpoint_writes_index.search(parent_writes_query)

# Sort results for deterministic order
docs = sorted(
res.docs,
Expand All @@ -1013,7 +1013,7 @@ async def _aload_pending_sends(
getattr(d, "idx", 0),
),
)

# Convert to expected format
return [
(d.type.encode(), blob)
Expand Down
20 changes: 17 additions & 3 deletions langgraph/checkpoint/redis/ashallow.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@
REDIS_KEY_SEPARATOR,
BaseRedisSaver,
)
from langgraph.checkpoint.redis.util import safely_decode
from langgraph.checkpoint.redis.util import (
safely_decode,
to_storage_safe_id,
to_storage_safe_str,
)

SCHEMAS = [
{
Expand Down Expand Up @@ -812,7 +816,13 @@ def _make_shallow_redis_checkpoint_blob_key_pattern(
) -> str:
"""Create a pattern to match all blob keys for a thread and namespace."""
return (
REDIS_KEY_SEPARATOR.join([CHECKPOINT_BLOB_PREFIX, thread_id, checkpoint_ns])
REDIS_KEY_SEPARATOR.join(
[
CHECKPOINT_BLOB_PREFIX,
str(to_storage_safe_id(thread_id)),
to_storage_safe_str(checkpoint_ns),
]
)
+ ":*"
)

Expand All @@ -823,7 +833,11 @@ def _make_shallow_redis_checkpoint_writes_key_pattern(
"""Create a pattern to match all writes keys for a thread and namespace."""
return (
REDIS_KEY_SEPARATOR.join(
[CHECKPOINT_WRITE_PREFIX, thread_id, checkpoint_ns]
[
CHECKPOINT_WRITE_PREFIX,
str(to_storage_safe_id(thread_id)),
to_storage_safe_str(checkpoint_ns),
]
)
+ ":*"
)
20 changes: 17 additions & 3 deletions langgraph/checkpoint/redis/shallow.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
REDIS_KEY_SEPARATOR,
BaseRedisSaver,
)
from langgraph.checkpoint.redis.util import safely_decode
from langgraph.checkpoint.redis.util import (
safely_decode,
to_storage_safe_id,
to_storage_safe_str,
)

SCHEMAS = [
{
Expand Down Expand Up @@ -713,7 +717,13 @@ def _make_shallow_redis_checkpoint_blob_key_pattern(
) -> str:
"""Create a pattern to match all blob keys for a thread and namespace."""
return (
REDIS_KEY_SEPARATOR.join([CHECKPOINT_BLOB_PREFIX, thread_id, checkpoint_ns])
REDIS_KEY_SEPARATOR.join(
[
CHECKPOINT_BLOB_PREFIX,
str(to_storage_safe_id(thread_id)),
to_storage_safe_str(checkpoint_ns),
]
)
+ ":*"
)

Expand All @@ -724,7 +734,11 @@ def _make_shallow_redis_checkpoint_writes_key_pattern(
"""Create a pattern to match all writes keys for a thread and namespace."""
return (
REDIS_KEY_SEPARATOR.join(
[CHECKPOINT_WRITE_PREFIX, thread_id, checkpoint_ns]
[
CHECKPOINT_WRITE_PREFIX,
str(to_storage_safe_id(thread_id)),
to_storage_safe_str(checkpoint_ns),
]
)
+ ":*"
)
Loading