langgraph-checkpoint-postgres 2.0.25__tar.gz → 3.0.1__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.25 → langgraph_checkpoint_postgres-3.0.1}/PKG-INFO +3 -3
  2. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/__init__.py +7 -6
  3. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/_ainternal.py +1 -2
  4. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/_internal.py +1 -2
  5. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/aio.py +9 -8
  6. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/base.py +3 -3
  7. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/shallow.py +28 -26
  8. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/aio.py +9 -7
  9. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/base.py +12 -10
  10. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/pyproject.toml +15 -7
  11. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_async_store.py +0 -6
  12. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_store.py +3 -3
  13. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/uv.lock +208 -221
  14. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/.gitignore +0 -0
  15. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/Makefile +0 -0
  16. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/README.md +0 -0
  17. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/py.typed +0 -0
  18. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/__init__.py +0 -0
  19. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/py.typed +0 -0
  20. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/__init__.py +0 -0
  21. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/compose-postgres.yml +0 -0
  22. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/conftest.py +0 -0
  23. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/embed_test_utils.py +0 -0
  24. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_async.py +0 -0
  25. {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_sync.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-checkpoint-postgres
3
- Version: 2.0.25
3
+ Version: 3.0.1
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.1.2
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
@@ -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
 
@@ -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
@@ -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)
@@ -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
@@ -4,7 +4,7 @@ import random
4
4
  import warnings
5
5
  from collections.abc import Sequence
6
6
  from importlib.metadata import version as get_version
7
- from typing import Any, Optional, cast
7
+ from typing import Any, cast
8
8
 
9
9
  from langchain_core.runnables import RunnableConfig
10
10
  from langgraph.checkpoint.base import (
@@ -16,7 +16,7 @@ from langgraph.checkpoint.base import (
16
16
  from langgraph.checkpoint.serde.types import TASKS
17
17
  from psycopg.types.json import Jsonb
18
18
 
19
- MetadataInput = Optional[dict[str, Any]]
19
+ MetadataInput = dict[str, Any] | None
20
20
 
21
21
  try:
22
22
  major, minor = get_version("langgraph").split(".")[:2]
@@ -81,7 +81,7 @@ MIGRATIONS = [
81
81
  """
82
82
  CREATE INDEX CONCURRENTLY IF NOT EXISTS checkpoint_writes_thread_id_idx ON checkpoint_writes(thread_id);
83
83
  """,
84
- """ALTER TABLE checkpoint_writes ADD COLUMN task_path TEXT NOT NULL DEFAULT '';""",
84
+ """ALTER TABLE checkpoint_writes ADD COLUMN IF NOT EXISTS task_path TEXT NOT NULL DEFAULT '';""",
85
85
  ]
86
86
 
87
87
  SELECT_SQL = """
@@ -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 (
@@ -77,7 +77,7 @@ MIGRATIONS = [
77
77
  CREATE INDEX CONCURRENTLY IF NOT EXISTS checkpoint_writes_thread_id_idx ON checkpoint_writes(thread_id);
78
78
  """,
79
79
  """
80
- ALTER TABLE checkpoint_writes ADD COLUMN task_path TEXT NOT NULL DEFAULT '';
80
+ ALTER TABLE checkpoint_writes ADD COLUMN IF NOT EXISTS task_path TEXT NOT NULL DEFAULT '';
81
81
  """,
82
82
  ]
83
83
 
@@ -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
 
@@ -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", "")
@@ -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.25"
7
+ version = "3.0.1"
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.1.2,<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
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
 
4
4
  import asyncio
5
5
  import itertools
6
- import sys
7
6
  import uuid
8
7
  from collections.abc import AsyncIterator
9
8
  from concurrent.futures import ThreadPoolExecutor
@@ -34,9 +33,6 @@ TTL_MINUTES = TTL_SECONDS / 60
34
33
 
35
34
  @pytest.fixture(scope="function", params=["default", "pipe", "pool"])
36
35
  async def store(request) -> AsyncIterator[AsyncPostgresStore]:
37
- if sys.version_info < (3, 10):
38
- pytest.skip("Async Postgres tests require Python 3.10+")
39
-
40
36
  database = f"test_{uuid.uuid4().hex[:16]}"
41
37
  uri_parts = DEFAULT_URI.split("/")
42
38
  uri_base = "/".join(uri_parts[:-1])
@@ -358,8 +354,6 @@ async def _create_vector_store(
358
354
  text_fields: list[str] | None = None,
359
355
  ) -> AsyncIterator[AsyncPostgresStore]:
360
356
  """Create a store with vector search enabled."""
361
- if sys.version_info < (3, 10):
362
- pytest.skip("Async Postgres tests require Python 3.10+")
363
357
 
364
358
  database = f"test_{uuid.uuid4().hex[:16]}"
365
359
  uri_parts = DEFAULT_URI.split("/")
@@ -754,7 +754,7 @@ def _cosine_similarity(X: list[float], Y: list[list[float]]) -> list[float]:
754
754
 
755
755
  similarities = []
756
756
  for y in Y:
757
- dot_product = sum(a * b for a, b in zip(X, y))
757
+ dot_product = sum(a * b for a, b in zip(X, y, strict=False))
758
758
  norm1 = sum(a * a for a in X) ** 0.5
759
759
  norm2 = sum(a * a for a in y) ** 0.5
760
760
  similarity = dot_product / (norm1 * norm2) if norm1 > 0 and norm2 > 0 else 0.0
@@ -771,7 +771,7 @@ def _inner_product(X: list[float], Y: list[list[float]]) -> list[float]:
771
771
 
772
772
  similarities = []
773
773
  for y in Y:
774
- similarity = sum(a * b for a, b in zip(X, y))
774
+ similarity = sum(a * b for a, b in zip(X, y, strict=False))
775
775
  similarities.append(similarity)
776
776
 
777
777
  return similarities
@@ -785,7 +785,7 @@ def _neg_l2_distance(X: list[float], Y: list[list[float]]) -> list[float]:
785
785
 
786
786
  similarities = []
787
787
  for y in Y:
788
- similarity = sum((a - b) ** 2 for a, b in zip(X, y)) ** 0.5
788
+ similarity = sum((a - b) ** 2 for a, b in zip(X, y, strict=False)) ** 0.5
789
789
  similarities.append(-similarity)
790
790
 
791
791
  return similarities