langgraph-checkpoint-postgres 2.0.24__tar.gz → 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/PKG-INFO +3 -3
  2. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/__init__.py +9 -8
  3. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/_ainternal.py +1 -2
  4. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/_internal.py +1 -2
  5. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/aio.py +11 -10
  6. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/base.py +16 -2
  7. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/shallow.py +30 -28
  8. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/aio.py +9 -7
  9. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/base.py +12 -10
  10. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/pyproject.toml +15 -7
  11. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_async.py +0 -4
  12. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_async_store.py +0 -6
  13. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_store.py +3 -3
  14. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_sync.py +0 -4
  15. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/uv.lock +207 -220
  16. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/.gitignore +0 -0
  17. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/Makefile +0 -0
  18. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/README.md +0 -0
  19. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/py.typed +0 -0
  20. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/__init__.py +0 -0
  21. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/py.typed +0 -0
  22. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/__init__.py +0 -0
  23. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/compose-postgres.yml +0 -0
  24. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/conftest.py +0 -0
  25. {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/embed_test_utils.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-checkpoint-postgres
3
- Version: 2.0.24
3
+ Version: 3.0.0
4
4
  Summary: Library with a Postgres implementation of LangGraph checkpoint saver.
5
5
  Project-URL: Repository, https://www.github.com/langchain-ai/langgraph
6
6
  License-Expression: MIT
7
- Requires-Python: >=3.9
8
- Requires-Dist: langgraph-checkpoint<3.0.0,>=2.0.21
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: langgraph-checkpoint<4.0.0,>=2.1.2
9
9
  Requires-Dist: orjson>=3.10.1
10
10
  Requires-Dist: psycopg-pool>=3.2.0
11
11
  Requires-Dist: psycopg>=3.2.0
@@ -14,7 +14,7 @@ from langgraph.checkpoint.base import (
14
14
  CheckpointMetadata,
15
15
  CheckpointTuple,
16
16
  get_checkpoint_id,
17
- get_checkpoint_metadata,
17
+ get_serializable_checkpoint_metadata,
18
18
  )
19
19
  from langgraph.checkpoint.serde.base import SerializerProtocol
20
20
  from psycopg import Capabilities, Connection, Cursor, Pipeline
@@ -94,6 +94,7 @@ class PostgresSaver(BasePostgresSaver):
94
94
  for v, migration in zip(
95
95
  range(version + 1, len(self.MIGRATIONS)),
96
96
  self.MIGRATIONS[version + 1 :],
97
+ strict=False,
97
98
  ):
98
99
  cur.execute(migration)
99
100
  cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
@@ -115,12 +116,12 @@ class PostgresSaver(BasePostgresSaver):
115
116
 
116
117
  Args:
117
118
  config: The config to use for listing the checkpoints.
118
- filter: Additional filtering criteria for metadata. Defaults to None.
119
- before: If provided, only checkpoints before the specified checkpoint ID are returned. Defaults to None.
120
- limit: The maximum number of checkpoints to return. Defaults to None.
119
+ filter: Additional filtering criteria for metadata.
120
+ before: If provided, only checkpoints before the specified checkpoint ID are returned.
121
+ limit: The maximum number of checkpoints to return.
121
122
 
122
123
  Yields:
123
- Iterator[CheckpointTuple]: An iterator of checkpoint tuples.
124
+ An iterator of checkpoint tuples.
124
125
 
125
126
  Examples:
126
127
  >>> from langgraph.checkpoint.postgres import PostgresSaver
@@ -182,7 +183,7 @@ class PostgresSaver(BasePostgresSaver):
182
183
  """Get a checkpoint tuple from the database.
183
184
 
184
185
  This method retrieves a checkpoint tuple from the Postgres database based on the
185
- provided config. If the config contains a "checkpoint_id" key, the checkpoint with
186
+ provided config. If the config contains a `checkpoint_id` key, the checkpoint with
186
187
  the matching thread ID and timestamp is retrieved. Otherwise, the latest checkpoint
187
188
  for the given thread ID is retrieved.
188
189
 
@@ -190,7 +191,7 @@ class PostgresSaver(BasePostgresSaver):
190
191
  config: The config to use for retrieving the checkpoint.
191
192
 
192
193
  Returns:
193
- Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
194
+ The retrieved checkpoint tuple, or None if no matching checkpoint was found.
194
195
 
195
196
  Examples:
196
197
 
@@ -325,7 +326,7 @@ class PostgresSaver(BasePostgresSaver):
325
326
  checkpoint["id"],
326
327
  checkpoint_id,
327
328
  Jsonb(copy),
328
- Jsonb(get_checkpoint_metadata(config, metadata)),
329
+ Jsonb(get_serializable_checkpoint_metadata(config, metadata)),
329
330
  ),
330
331
  )
331
332
  return next_config
@@ -2,13 +2,12 @@
2
2
 
3
3
  from collections.abc import AsyncIterator
4
4
  from contextlib import asynccontextmanager
5
- from typing import Union
6
5
 
7
6
  from psycopg import AsyncConnection
8
7
  from psycopg.rows import DictRow
9
8
  from psycopg_pool import AsyncConnectionPool
10
9
 
11
- Conn = Union[AsyncConnection[DictRow], AsyncConnectionPool[AsyncConnection[DictRow]]]
10
+ Conn = AsyncConnection[DictRow] | AsyncConnectionPool[AsyncConnection[DictRow]]
12
11
 
13
12
 
14
13
  @asynccontextmanager
@@ -2,13 +2,12 @@
2
2
 
3
3
  from collections.abc import Iterator
4
4
  from contextlib import contextmanager
5
- from typing import Union
6
5
 
7
6
  from psycopg import Connection
8
7
  from psycopg.rows import DictRow
9
8
  from psycopg_pool import ConnectionPool
10
9
 
11
- Conn = Union[Connection[DictRow], ConnectionPool[Connection[DictRow]]]
10
+ Conn = Connection[DictRow] | ConnectionPool[Connection[DictRow]]
12
11
 
13
12
 
14
13
  @contextmanager
@@ -14,7 +14,7 @@ from langgraph.checkpoint.base import (
14
14
  CheckpointMetadata,
15
15
  CheckpointTuple,
16
16
  get_checkpoint_id,
17
- get_checkpoint_metadata,
17
+ get_serializable_checkpoint_metadata,
18
18
  )
19
19
  from langgraph.checkpoint.serde.base import SerializerProtocol
20
20
  from psycopg import AsyncConnection, AsyncCursor, AsyncPipeline, Capabilities
@@ -99,6 +99,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
99
99
  for v, migration in zip(
100
100
  range(version + 1, len(self.MIGRATIONS)),
101
101
  self.MIGRATIONS[version + 1 :],
102
+ strict=False,
102
103
  ):
103
104
  await cur.execute(migration)
104
105
  await cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
@@ -121,11 +122,11 @@ class AsyncPostgresSaver(BasePostgresSaver):
121
122
  Args:
122
123
  config: Base configuration for filtering checkpoints.
123
124
  filter: Additional filtering criteria for metadata.
124
- before: If provided, only checkpoints before the specified checkpoint ID are returned. Defaults to None.
125
+ before: If provided, only checkpoints before the specified checkpoint ID are returned.
125
126
  limit: Maximum number of checkpoints to return.
126
127
 
127
128
  Yields:
128
- AsyncIterator[CheckpointTuple]: An asynchronous iterator of matching checkpoint tuples.
129
+ An asynchronous iterator of matching checkpoint tuples.
129
130
  """
130
131
  where, args = self._search_where(config, filter, before)
131
132
  query = self.SELECT_SQL + where + " ORDER BY checkpoint_id DESC"
@@ -169,7 +170,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
169
170
  """Get a checkpoint tuple from the database asynchronously.
170
171
 
171
172
  This method retrieves a checkpoint tuple from the Postgres database based on the
172
- provided config. If the config contains a "checkpoint_id" key, the checkpoint with
173
+ provided config. If the config contains a `checkpoint_id` key, the checkpoint with
173
174
  the matching thread ID and "checkpoint_id" is retrieved. Otherwise, the latest checkpoint
174
175
  for the given thread ID is retrieved.
175
176
 
@@ -177,7 +178,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
177
178
  config: The config to use for retrieving the checkpoint.
178
179
 
179
180
  Returns:
180
- Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
181
+ The retrieved checkpoint tuple, or None if no matching checkpoint was found.
181
182
  """
182
183
  thread_id = config["configurable"]["thread_id"]
183
184
  checkpoint_id = get_checkpoint_id(config)
@@ -283,7 +284,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
283
284
  checkpoint["id"],
284
285
  checkpoint_id,
285
286
  Jsonb(copy),
286
- Jsonb(get_checkpoint_metadata(config, metadata)),
287
+ Jsonb(get_serializable_checkpoint_metadata(config, metadata)),
287
288
  ),
288
289
  )
289
290
  return next_config
@@ -444,11 +445,11 @@ class AsyncPostgresSaver(BasePostgresSaver):
444
445
  Args:
445
446
  config: Base configuration for filtering checkpoints.
446
447
  filter: Additional filtering criteria for metadata.
447
- before: If provided, only checkpoints before the specified checkpoint ID are returned. Defaults to None.
448
+ before: If provided, only checkpoints before the specified checkpoint ID are returned.
448
449
  limit: Maximum number of checkpoints to return.
449
450
 
450
451
  Yields:
451
- Iterator[CheckpointTuple]: An iterator of matching checkpoint tuples.
452
+ An iterator of matching checkpoint tuples.
452
453
  """
453
454
  try:
454
455
  # check if we are in the main thread, only bg threads can block
@@ -476,7 +477,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
476
477
  """Get a checkpoint tuple from the database.
477
478
 
478
479
  This method retrieves a checkpoint tuple from the Postgres database based on the
479
- provided config. If the config contains a "checkpoint_id" key, the checkpoint with
480
+ provided config. If the config contains a `checkpoint_id` key, the checkpoint with
480
481
  the matching thread ID and "checkpoint_id" is retrieved. Otherwise, the latest checkpoint
481
482
  for the given thread ID is retrieved.
482
483
 
@@ -484,7 +485,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
484
485
  config: The config to use for retrieving the checkpoint.
485
486
 
486
487
  Returns:
487
- Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
488
+ The retrieved checkpoint tuple, or None if no matching checkpoint was found.
488
489
  """
489
490
  try:
490
491
  # check if we are in the main thread, only bg threads can block
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import random
4
+ import warnings
4
5
  from collections.abc import Sequence
5
- from typing import Any, Optional, cast
6
+ from importlib.metadata import version as get_version
7
+ from typing import Any, cast
6
8
 
7
9
  from langchain_core.runnables import RunnableConfig
8
10
  from langgraph.checkpoint.base import (
@@ -14,7 +16,19 @@ from langgraph.checkpoint.base import (
14
16
  from langgraph.checkpoint.serde.types import TASKS
15
17
  from psycopg.types.json import Jsonb
16
18
 
17
- MetadataInput = Optional[dict[str, Any]]
19
+ MetadataInput = dict[str, Any] | None
20
+
21
+ try:
22
+ major, minor = get_version("langgraph").split(".")[:2]
23
+ if int(major) == 0 and int(minor) < 5:
24
+ warnings.warn(
25
+ "You're using incompatible versions of langgraph and checkpoint-postgres. Please upgrade langgraph to avoid unexpected behavior.",
26
+ DeprecationWarning,
27
+ stacklevel=2,
28
+ )
29
+ except Exception:
30
+ # skip version check if running from source
31
+ pass
18
32
 
19
33
  """
20
34
  To add a new migration, add a new string to the MIGRATIONS list.
@@ -3,7 +3,7 @@ import threading
3
3
  import warnings
4
4
  from collections.abc import AsyncIterator, Iterator, Sequence
5
5
  from contextlib import asynccontextmanager, contextmanager
6
- from typing import Any, Optional
6
+ from typing import Any
7
7
 
8
8
  from langchain_core.runnables import RunnableConfig
9
9
  from langgraph.checkpoint.base import (
@@ -12,7 +12,7 @@ from langgraph.checkpoint.base import (
12
12
  Checkpoint,
13
13
  CheckpointMetadata,
14
14
  CheckpointTuple,
15
- get_checkpoint_metadata,
15
+ get_serializable_checkpoint_metadata,
16
16
  )
17
17
  from langgraph.checkpoint.serde.base import SerializerProtocol
18
18
  from langgraph.checkpoint.serde.types import TASKS
@@ -151,7 +151,7 @@ def _dump_blobs(
151
151
  checkpoint_ns: str,
152
152
  values: dict[str, Any],
153
153
  versions: ChannelVersions,
154
- ) -> list[tuple[str, str, str, str, Optional[bytes]]]:
154
+ ) -> list[tuple[str, str, str, str, bytes | None]]:
155
155
  if not versions:
156
156
  return []
157
157
 
@@ -186,8 +186,8 @@ class ShallowPostgresSaver(BasePostgresSaver):
186
186
  def __init__(
187
187
  self,
188
188
  conn: _internal.Conn,
189
- pipe: Optional[Pipeline] = None,
190
- serde: Optional[SerializerProtocol] = None,
189
+ pipe: Pipeline | None = None,
190
+ serde: SerializerProtocol | None = None,
191
191
  ) -> None:
192
192
  warnings.warn(
193
193
  "ShallowPostgresSaver is deprecated as of version 2.0.20 and will be removed in 3.0.0. "
@@ -249,6 +249,7 @@ class ShallowPostgresSaver(BasePostgresSaver):
249
249
  for v, migration in zip(
250
250
  range(version + 1, len(self.MIGRATIONS)),
251
251
  self.MIGRATIONS[version + 1 :],
252
+ strict=False,
252
253
  ):
253
254
  cur.execute(migration)
254
255
  cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
@@ -257,11 +258,11 @@ class ShallowPostgresSaver(BasePostgresSaver):
257
258
 
258
259
  def list(
259
260
  self,
260
- config: Optional[RunnableConfig],
261
+ config: RunnableConfig | None,
261
262
  *,
262
- filter: Optional[dict[str, Any]] = None,
263
- before: Optional[RunnableConfig] = None,
264
- limit: Optional[int] = None,
263
+ filter: dict[str, Any] | None = None,
264
+ before: RunnableConfig | None = None,
265
+ limit: int | None = None,
265
266
  ) -> Iterator[CheckpointTuple]:
266
267
  """List checkpoints from the database.
267
268
 
@@ -299,7 +300,7 @@ class ShallowPostgresSaver(BasePostgresSaver):
299
300
  pending_writes=self._load_writes(value["pending_writes"]),
300
301
  )
301
302
 
302
- def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
303
+ def get_tuple(self, config: RunnableConfig) -> CheckpointTuple | None:
303
304
  """Get a checkpoint tuple from the database.
304
305
 
305
306
  This method retrieves a checkpoint tuple from the Postgres database based on the
@@ -309,7 +310,7 @@ class ShallowPostgresSaver(BasePostgresSaver):
309
310
  config: The config to use for retrieving the checkpoint.
310
311
 
311
312
  Returns:
312
- Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
313
+ The retrieved checkpoint tuple, or None if no matching checkpoint was found.
313
314
 
314
315
  Examples:
315
316
 
@@ -441,7 +442,7 @@ class ShallowPostgresSaver(BasePostgresSaver):
441
442
  thread_id,
442
443
  checkpoint_ns,
443
444
  Jsonb(copy),
444
- Jsonb(get_checkpoint_metadata(config, metadata)),
445
+ Jsonb(get_serializable_checkpoint_metadata(config, metadata)),
445
446
  ),
446
447
  )
447
448
  return next_config
@@ -542,8 +543,8 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
542
543
  def __init__(
543
544
  self,
544
545
  conn: _ainternal.Conn,
545
- pipe: Optional[AsyncPipeline] = None,
546
- serde: Optional[SerializerProtocol] = None,
546
+ pipe: AsyncPipeline | None = None,
547
+ serde: SerializerProtocol | None = None,
547
548
  ) -> None:
548
549
  warnings.warn(
549
550
  "AsyncShallowPostgresSaver is deprecated as of version 2.0.20 and will be removed in 3.0.0. "
@@ -570,7 +571,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
570
571
  conn_string: str,
571
572
  *,
572
573
  pipeline: bool = False,
573
- serde: Optional[SerializerProtocol] = None,
574
+ serde: SerializerProtocol | None = None,
574
575
  ) -> AsyncIterator["AsyncShallowPostgresSaver"]:
575
576
  """Create a new AsyncShallowPostgresSaver instance from a connection string.
576
577
 
@@ -610,6 +611,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
610
611
  for v, migration in zip(
611
612
  range(version + 1, len(self.MIGRATIONS)),
612
613
  self.MIGRATIONS[version + 1 :],
614
+ strict=False,
613
615
  ):
614
616
  await cur.execute(migration)
615
617
  await cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
@@ -618,11 +620,11 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
618
620
 
619
621
  async def alist(
620
622
  self,
621
- config: Optional[RunnableConfig],
623
+ config: RunnableConfig | None,
622
624
  *,
623
- filter: Optional[dict[str, Any]] = None,
624
- before: Optional[RunnableConfig] = None,
625
- limit: Optional[int] = None,
625
+ filter: dict[str, Any] | None = None,
626
+ before: RunnableConfig | None = None,
627
+ limit: int | None = None,
626
628
  ) -> AsyncIterator[CheckpointTuple]:
627
629
  """List checkpoints from the database asynchronously.
628
630
 
@@ -662,7 +664,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
662
664
  ),
663
665
  )
664
666
 
665
- async def aget_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
667
+ async def aget_tuple(self, config: RunnableConfig) -> CheckpointTuple | None:
666
668
  """Get a checkpoint tuple from the database asynchronously.
667
669
 
668
670
  This method retrieves a checkpoint tuple from the Postgres database based on the
@@ -672,7 +674,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
672
674
  config: The config to use for retrieving the checkpoint.
673
675
 
674
676
  Returns:
675
- Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
677
+ The retrieved checkpoint tuple, or None if no matching checkpoint was found.
676
678
  """
677
679
  thread_id = config["configurable"]["thread_id"]
678
680
  checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
@@ -774,7 +776,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
774
776
  thread_id,
775
777
  checkpoint_ns,
776
778
  Jsonb(copy),
777
- Jsonb(get_checkpoint_metadata(config, metadata)),
779
+ Jsonb(get_serializable_checkpoint_metadata(config, metadata)),
778
780
  ),
779
781
  )
780
782
  return next_config
@@ -861,11 +863,11 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
861
863
 
862
864
  def list(
863
865
  self,
864
- config: Optional[RunnableConfig],
866
+ config: RunnableConfig | None,
865
867
  *,
866
- filter: Optional[dict[str, Any]] = None,
867
- before: Optional[RunnableConfig] = None,
868
- limit: Optional[int] = None,
868
+ filter: dict[str, Any] | None = None,
869
+ before: RunnableConfig | None = None,
870
+ limit: int | None = None,
869
871
  ) -> Iterator[CheckpointTuple]:
870
872
  """List checkpoints from the database.
871
873
 
@@ -883,7 +885,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
883
885
  except StopAsyncIteration:
884
886
  break
885
887
 
886
- def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
888
+ def get_tuple(self, config: RunnableConfig) -> CheckpointTuple | None:
887
889
  """Get a checkpoint tuple from the database.
888
890
 
889
891
  This method retrieves a checkpoint tuple from the Postgres database based on the
@@ -893,7 +895,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
893
895
  config: The config to use for retrieving the checkpoint.
894
896
 
895
897
  Returns:
896
- Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
898
+ The retrieved checkpoint tuple, or None if no matching checkpoint was found.
897
899
  """
898
900
  try:
899
901
  # check if we are in the main thread, only bg threads can block
@@ -2,10 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import logging
5
- from collections.abc import AsyncIterator, Iterable, Sequence
5
+ from collections.abc import AsyncIterator, Callable, Iterable, Sequence
6
6
  from contextlib import asynccontextmanager
7
7
  from types import TracebackType
8
- from typing import Any, Callable, cast
8
+ from typing import Any, cast
9
9
 
10
10
  import orjson
11
11
  from langgraph.store.base import (
@@ -339,7 +339,7 @@ class AsyncPostgresStore(AsyncBatchedBaseStore, BasePostgresStore[_ainternal.Con
339
339
 
340
340
  Args:
341
341
  timeout: Maximum time to wait for the task to stop, in seconds.
342
- If None, wait indefinitely.
342
+ If `None`, wait indefinitely.
343
343
 
344
344
  Returns:
345
345
  bool: True if the task was successfully stopped or wasn't running,
@@ -465,7 +465,9 @@ class AsyncPostgresStore(AsyncBatchedBaseStore, BasePostgresStore[_ainternal.Con
465
465
  query,
466
466
  [
467
467
  p
468
- for (ns, k, pathname, _), vector in zip(txt_params, vectors)
468
+ for (ns, k, pathname, _), vector in zip(
469
+ txt_params, vectors, strict=False
470
+ )
469
471
  for p in (ns, k, pathname, vector)
470
472
  ],
471
473
  )
@@ -486,13 +488,13 @@ class AsyncPostgresStore(AsyncBatchedBaseStore, BasePostgresStore[_ainternal.Con
486
488
  vectors = await self.embeddings.aembed_documents(
487
489
  [query for _, query in embedding_requests]
488
490
  )
489
- for (idx, _), vector in zip(embedding_requests, vectors):
491
+ for (idx, _), vector in zip(embedding_requests, vectors, strict=False):
490
492
  _paramslist = queries[idx][1]
491
493
  for i in range(len(_paramslist)):
492
494
  if _paramslist[i] is PLACEHOLDER:
493
495
  _paramslist[i] = vector
494
496
 
495
- for (idx, _), (query, params) in zip(search_ops, queries):
497
+ for (idx, _), (query, params) in zip(search_ops, queries, strict=False):
496
498
  await cur.execute(query, params)
497
499
  rows = cast(list[Row], await cur.fetchall())
498
500
  items = [
@@ -510,7 +512,7 @@ class AsyncPostgresStore(AsyncBatchedBaseStore, BasePostgresStore[_ainternal.Con
510
512
  cur: AsyncCursor[DictRow],
511
513
  ) -> None:
512
514
  queries = self._get_batch_list_namespaces_queries(list_ops)
513
- for (query, params), (idx, _) in zip(queries, list_ops):
515
+ for (query, params), (idx, _) in zip(queries, list_ops, strict=False):
514
516
  await cur.execute(query, params)
515
517
  rows = cast(list[dict], await cur.fetchall())
516
518
  namespaces = [_decode_ns_bytes(row["truncated_prefix"]) for row in rows]
@@ -6,18 +6,16 @@ import json
6
6
  import logging
7
7
  import threading
8
8
  from collections import defaultdict
9
- from collections.abc import Iterable, Iterator, Sequence
9
+ from collections.abc import Callable, Iterable, Iterator, Sequence
10
10
  from contextlib import contextmanager
11
11
  from datetime import datetime
12
12
  from typing import (
13
13
  TYPE_CHECKING,
14
14
  Any,
15
- Callable,
16
15
  Generic,
17
16
  Literal,
18
17
  NamedTuple,
19
18
  TypeVar,
20
- Union,
21
19
  cast,
22
20
  )
23
21
 
@@ -141,7 +139,7 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS store_vectors_embedding_idx ON store_vec
141
139
  ]
142
140
 
143
141
 
144
- C = TypeVar("C", bound=Union[_pg_internal.Conn, _ainternal.Conn])
142
+ C = TypeVar("C", bound=_pg_internal.Conn | _ainternal.Conn)
145
143
 
146
144
 
147
145
  class PoolConfig(TypedDict, total=False):
@@ -255,7 +253,7 @@ class BasePostgresStore(Generic[C]):
255
253
 
256
254
  results = []
257
255
  for namespace, items in namespace_groups.items():
258
- _, keys = zip(*items)
256
+ _, keys = zip(*items, strict=False)
259
257
  this_refresh_ttls = refresh_ttls[namespace]
260
258
 
261
259
  query = """
@@ -868,7 +866,7 @@ class PostgresStore(BaseStore, BasePostgresStore[_pg_internal.Conn]):
868
866
 
869
867
  Args:
870
868
  timeout: Maximum time to wait for the thread to stop, in seconds.
871
- If None, wait indefinitely.
869
+ If `None`, wait indefinitely.
872
870
 
873
871
  Returns:
874
872
  bool: True if the thread was successfully stopped or wasn't running,
@@ -1014,7 +1012,9 @@ class PostgresStore(BaseStore, BasePostgresStore[_pg_internal.Conn]):
1014
1012
  query,
1015
1013
  [
1016
1014
  p
1017
- for (ns, k, pathname, _), vector in zip(txt_params, vectors)
1015
+ for (ns, k, pathname, _), vector in zip(
1016
+ txt_params, vectors, strict=False
1017
+ )
1018
1018
  for p in (ns, k, pathname, vector)
1019
1019
  ],
1020
1020
  )
@@ -1035,13 +1035,15 @@ class PostgresStore(BaseStore, BasePostgresStore[_pg_internal.Conn]):
1035
1035
  embeddings = self.embeddings.embed_documents(
1036
1036
  [query for _, query in embedding_requests]
1037
1037
  )
1038
- for (idx, _), embedding in zip(embedding_requests, embeddings):
1038
+ for (idx, _), embedding in zip(
1039
+ embedding_requests, embeddings, strict=False
1040
+ ):
1039
1041
  _paramslist = queries[idx][1]
1040
1042
  for i in range(len(_paramslist)):
1041
1043
  if _paramslist[i] is PLACEHOLDER:
1042
1044
  _paramslist[i] = embedding
1043
1045
 
1044
- for (idx, _), (query, params) in zip(search_ops, queries):
1046
+ for (idx, _), (query, params) in zip(search_ops, queries, strict=False):
1045
1047
  cur.execute(query, params)
1046
1048
  rows = cast(list[Row], cur.fetchall())
1047
1049
  results[idx] = [
@@ -1058,7 +1060,7 @@ class PostgresStore(BaseStore, BasePostgresStore[_pg_internal.Conn]):
1058
1060
  cur: Cursor[DictRow],
1059
1061
  ) -> None:
1060
1062
  for (query, params), (idx, _) in zip(
1061
- self._get_batch_list_namespaces_queries(list_ops), list_ops
1063
+ self._get_batch_list_namespaces_queries(list_ops), list_ops, strict=False
1062
1064
  ):
1063
1065
  cur.execute(query, params)
1064
1066
  results[idx] = [_decode_ns_bytes(row["truncated_prefix"]) for row in cur]
@@ -4,15 +4,15 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "langgraph-checkpoint-postgres"
7
- version = "2.0.24"
7
+ version = "3.0.0"
8
8
  description = "Library with a Postgres implementation of LangGraph checkpoint saver."
9
9
  authors = []
10
- requires-python = ">=3.9"
10
+ requires-python = ">=3.10"
11
11
  readme = "README.md"
12
12
  license = "MIT"
13
13
  license-files = ['LICENSE']
14
14
  dependencies = [
15
- "langgraph-checkpoint>=2.0.21,<3.0.0",
15
+ "langgraph-checkpoint>=2.1.2,<4.0.0",
16
16
  "orjson>=3.10.1",
17
17
  "psycopg>=3.2.0",
18
18
  "psycopg-pool>=3.2.0",
@@ -22,18 +22,24 @@ dependencies = [
22
22
  Repository = "https://www.github.com/langchain-ai/langgraph"
23
23
 
24
24
  [dependency-groups]
25
- dev = [
26
- "ruff",
27
- "codespell",
25
+ test = [
28
26
  "pytest",
29
27
  "anyio",
30
28
  "pytest-asyncio",
31
29
  "pytest-mock",
32
- "mypy",
33
30
  "psycopg[binary]",
34
31
  "langgraph-checkpoint",
35
32
  "pytest-watcher",
36
33
  ]
34
+ lint = [
35
+ "ruff",
36
+ "codespell",
37
+ "mypy",
38
+ ]
39
+ dev = [
40
+ {include-group = "test"},
41
+ {include-group = "lint"},
42
+ ]
37
43
 
38
44
  [tool.uv]
39
45
  default-groups = ['dev']
@@ -55,8 +61,10 @@ lint.select = [
55
61
  "UP", # pyupgrade
56
62
  "B", # flake8-bugbear
57
63
  "I", # isort
64
+ "UP", # pyupgrade
58
65
  ]
59
66
  lint.ignore = ["E501", "B008"]
67
+ target-version = "py310"
60
68
 
61
69
  [tool.mypy]
62
70
  # https://mypy.readthedocs.io/en/stable/config_file.html
@@ -187,13 +187,11 @@ def test_data():
187
187
  metadata_1: CheckpointMetadata = {
188
188
  "source": "input",
189
189
  "step": 2,
190
- "writes": {},
191
190
  "score": 1,
192
191
  }
193
192
  metadata_2: CheckpointMetadata = {
194
193
  "source": "loop",
195
194
  "step": 1,
196
- "writes": {"foo": "bar"},
197
195
  "score": None,
198
196
  }
199
197
  metadata_3: CheckpointMetadata = {}
@@ -220,7 +218,6 @@ async def test_combined_metadata(saver_name: str, test_data) -> None:
220
218
  metadata: CheckpointMetadata = {
221
219
  "source": "loop",
222
220
  "step": 1,
223
- "writes": {"foo": "bar"},
224
221
  "score": None,
225
222
  }
226
223
  await saver.aput(config, chkpnt, metadata, {})
@@ -246,7 +243,6 @@ async def test_asearch(saver_name: str, test_data) -> None:
246
243
  query_1 = {"source": "input"} # search by 1 key
247
244
  query_2 = {
248
245
  "step": 1,
249
- "writes": {"foo": "bar"},
250
246
  } # search by multiple keys
251
247
  query_3: dict[str, Any] = {} # search by no keys, return all checkpoints
252
248
  query_4 = {"source": "update", "step": 1} # no match