Skip to content

Commit f2cfa6e

Browse files
improve mypy warnings and update package ranges
1 parent f408e3a commit f2cfa6e

File tree

8 files changed

+154
-177
lines changed

8 files changed

+154
-177
lines changed

langgraph/checkpoint/redis/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ def put(
280280
# store at top-level for filters in list()
281281
if all(key in metadata for key in ["source", "step"]):
282282
checkpoint_data["source"] = metadata["source"]
283-
checkpoint_data["step"] = metadata["step"] # type: ignore
283+
checkpoint_data["step"] = metadata["step"]
284284

285285
# Create the checkpoint key
286286
checkpoint_key = BaseRedisSaver._make_redis_checkpoint_key(

langgraph/checkpoint/redis/aio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ async def aput(
568568
# store at top-level for filters in list()
569569
if all(key in metadata for key in ["source", "step"]):
570570
checkpoint_data["source"] = metadata["source"]
571-
checkpoint_data["step"] = metadata["step"] # type: ignore
571+
checkpoint_data["step"] = metadata["step"]
572572

573573
# Prepare checkpoint key
574574
checkpoint_key = BaseRedisSaver._make_redis_checkpoint_key(

langgraph/checkpoint/redis/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ def _dump_metadata(self, metadata: CheckpointMetadata) -> str:
383383
# NOTE: we're using JSON serializer (not msgpack), so we need to remove null characters before writing
384384
return serialized_metadata.decode().replace("\\u0000", "")
385385

386-
def get_next_version(self, current: Optional[str], channel: ChannelProtocol) -> str:
386+
def get_next_version(self, current: Optional[str], channel: None = None) -> str:
387387
"""Generate next version number."""
388388
if current is None:
389389
current_v = 0

langgraph/store/redis/__init__.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -578,16 +578,14 @@ def _batch_search_ops(
578578
if self.cluster_mode:
579579
for key in refresh_keys:
580580
ttl = self._redis.ttl(key)
581-
if ttl > 0: # type: ignore
581+
if ttl > 0:
582582
self._redis.expire(key, ttl_seconds)
583583
else:
584584
pipeline = self._redis.pipeline(transaction=True)
585585
for key in refresh_keys:
586586
# Only refresh TTL if the key exists and has a TTL
587587
ttl = self._redis.ttl(key)
588-
if (
589-
ttl > 0
590-
): # Only refresh if key exists and has TTL # type: ignore
588+
if ttl > 0: # Only refresh if key exists and has TTL
591589
pipeline.expire(key, ttl_seconds)
592590
if pipeline.command_stack:
593591
pipeline.execute()
@@ -645,16 +643,14 @@ def _batch_search_ops(
645643
if self.cluster_mode:
646644
for key in refresh_keys:
647645
ttl = self._redis.ttl(key)
648-
if ttl > 0: # type: ignore
646+
if ttl > 0:
649647
self._redis.expire(key, ttl_seconds)
650648
else:
651649
pipeline = self._redis.pipeline(transaction=True)
652650
for key in refresh_keys:
653651
# Only refresh TTL if the key exists and has a TTL
654652
ttl = self._redis.ttl(key)
655-
if (
656-
ttl > 0
657-
): # Only refresh if key exists and has TTL # type: ignore
653+
if ttl > 0: # Only refresh if key exists and has TTL
658654
pipeline.expire(key, ttl_seconds)
659655
if pipeline.command_stack:
660656
pipeline.execute()

langgraph/store/redis/aio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def __init__(
8787

8888
# Set up store configuration
8989
self.index_config = index
90-
self.ttl_config = ttl # type: ignore
90+
self.ttl_config = ttl
9191

9292
if self.index_config:
9393
self.index_config = self.index_config.copy()

poetry.lock

Lines changed: 114 additions & 137 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ packages = [{ include = "langgraph" }]
1919

2020
[tool.poetry.dependencies]
2121
python = ">=3.9,<3.14"
22-
langgraph-checkpoint = "^2.0.26"
22+
langgraph-checkpoint = ">=2.0.26"
2323
redisvl = ">=0.5.1,<1.0.0"
2424

2525
[tool.poetry.group.dev.dependencies]
@@ -31,7 +31,7 @@ anyio = "^4.4.0"
3131
pytest-asyncio = "^0.21.1"
3232
pytest-xdist = {extras = ["psutil"], version = "^3.6.1"}
3333
pytest-mock = "^3.11.1"
34-
mypy = "^1.10.0"
34+
mypy = ">=1.11.0,<2"
3535
aioconsole = "^0.8.1"
3636
langchain-openai = "^0.3.2"
3737
testcontainers = "^4.9.1"

tests/test_async_aget_tuple_checkpoint_id.py

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,24 @@ async def saver(redis_url: str) -> AsyncGenerator[AsyncRedisSaver, None]:
2222
@pytest.mark.asyncio
2323
async def test_aget_tuple_returns_correct_checkpoint_id(saver: AsyncRedisSaver):
2424
"""Test that aget_tuple returns the correct checkpoint_id when not specified in config.
25-
25+
2626
This test reproduces the issue described in GitHub issue #64 where AsyncRedisSaver
2727
aget_tuple was returning None for checkpoint_id while the sync version worked correctly.
2828
"""
2929
# Create a unique thread ID
3030
thread_id = str(uuid.uuid4())
31-
31+
3232
# Config with only thread_id and checkpoint_ns (no checkpoint_id)
3333
runnable_config: RunnableConfig = {
3434
"configurable": {"thread_id": thread_id, "checkpoint_ns": ""}
3535
}
36-
36+
3737
# Put several checkpoints
3838
checkpoint_ids = []
3939
for run in range(3):
4040
checkpoint_id = str(run)
4141
checkpoint_ids.append(checkpoint_id)
42-
42+
4343
await saver.aput(
4444
{
4545
"configurable": {
@@ -56,38 +56,40 @@ async def test_aget_tuple_returns_correct_checkpoint_id(saver: AsyncRedisSaver):
5656
},
5757
{},
5858
)
59-
59+
6060
# Get the tuple using the config without checkpoint_id
6161
# This should return the latest checkpoint
6262
get_tuple = await saver.aget_tuple(runnable_config)
63-
63+
6464
# Verify the checkpoint_id is not None and matches the expected value
65-
assert get_tuple is not None, f"Expected checkpoint tuple, got None for run {run}"
66-
65+
assert (
66+
get_tuple is not None
67+
), f"Expected checkpoint tuple, got None for run {run}"
68+
6769
returned_checkpoint_id = get_tuple.config["configurable"]["checkpoint_id"]
6870
assert returned_checkpoint_id is not None, (
6971
f"Expected checkpoint_id to be set, got None for run {run}. "
7072
f"This indicates the bug where aget_tuple returns None for checkpoint_id."
7173
)
72-
74+
7375
# Since we're getting the latest checkpoint each time, it should be the current checkpoint_id
74-
assert returned_checkpoint_id == checkpoint_id, (
75-
f"Expected checkpoint_id {checkpoint_id}, got {returned_checkpoint_id} for run {run}"
76-
)
76+
assert (
77+
returned_checkpoint_id == checkpoint_id
78+
), f"Expected checkpoint_id {checkpoint_id}, got {returned_checkpoint_id} for run {run}"
7779

7880

7981
@pytest.mark.asyncio
8082
async def test_aget_tuple_with_explicit_checkpoint_id(saver: AsyncRedisSaver):
8183
"""Test that aget_tuple works correctly when checkpoint_id is explicitly provided."""
8284
# Create a unique thread ID
8385
thread_id = str(uuid.uuid4())
84-
86+
8587
# Put several checkpoints
8688
checkpoint_ids = []
8789
for run in range(3):
8890
checkpoint_id = str(run)
8991
checkpoint_ids.append(checkpoint_id)
90-
92+
9193
await saver.aput(
9294
{
9395
"configurable": {
@@ -104,36 +106,38 @@ async def test_aget_tuple_with_explicit_checkpoint_id(saver: AsyncRedisSaver):
104106
},
105107
{},
106108
)
107-
109+
108110
# Test retrieving each checkpoint by explicit checkpoint_id
109111
for checkpoint_id in checkpoint_ids:
110112
config_with_id: RunnableConfig = {
111113
"configurable": {
112114
"thread_id": thread_id,
113115
"checkpoint_id": checkpoint_id,
114-
"checkpoint_ns": ""
116+
"checkpoint_ns": "",
115117
}
116118
}
117-
119+
118120
get_tuple = await saver.aget_tuple(config_with_id)
119-
120-
assert get_tuple is not None, f"Expected checkpoint tuple, got None for checkpoint_id {checkpoint_id}"
121-
121+
122+
assert (
123+
get_tuple is not None
124+
), f"Expected checkpoint tuple, got None for checkpoint_id {checkpoint_id}"
125+
122126
returned_checkpoint_id = get_tuple.config["configurable"]["checkpoint_id"]
123-
assert returned_checkpoint_id == checkpoint_id, (
124-
f"Expected checkpoint_id {checkpoint_id}, got {returned_checkpoint_id}"
125-
)
127+
assert (
128+
returned_checkpoint_id == checkpoint_id
129+
), f"Expected checkpoint_id {checkpoint_id}, got {returned_checkpoint_id}"
126130

127131

128132
@pytest.mark.asyncio
129133
async def test_aget_tuple_no_checkpoint_returns_none(saver: AsyncRedisSaver):
130134
"""Test that aget_tuple returns None when no checkpoint exists for the thread."""
131135
# Use a thread ID that doesn't exist
132136
thread_id = str(uuid.uuid4())
133-
137+
134138
runnable_config: RunnableConfig = {
135139
"configurable": {"thread_id": thread_id, "checkpoint_ns": ""}
136140
}
137-
141+
138142
get_tuple = await saver.aget_tuple(runnable_config)
139-
assert get_tuple is None, "Expected None when no checkpoint exists for thread"
143+
assert get_tuple is None, "Expected None when no checkpoint exists for thread"

0 commit comments

Comments
 (0)