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.
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/PKG-INFO +3 -3
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/__init__.py +9 -8
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/_ainternal.py +1 -2
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/_internal.py +1 -2
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/aio.py +11 -10
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/base.py +16 -2
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/shallow.py +30 -28
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/aio.py +9 -7
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/base.py +12 -10
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/pyproject.toml +15 -7
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_async.py +0 -4
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_async_store.py +0 -6
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_store.py +3 -3
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_sync.py +0 -4
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/uv.lock +207 -220
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/.gitignore +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/Makefile +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/README.md +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/checkpoint/postgres/py.typed +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/__init__.py +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/langgraph/store/postgres/py.typed +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/__init__.py +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/compose-postgres.yml +0 -0
- {langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/conftest.py +0 -0
- {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:
|
|
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.
|
|
8
|
-
Requires-Dist: langgraph-checkpoint<
|
|
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
|
-
|
|
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.
|
|
119
|
-
before: If provided, only checkpoints before the specified checkpoint ID are returned.
|
|
120
|
-
limit: The maximum number of checkpoints to return.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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,
|
|
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:
|
|
190
|
-
serde:
|
|
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:
|
|
261
|
+
config: RunnableConfig | None,
|
|
261
262
|
*,
|
|
262
|
-
filter:
|
|
263
|
-
before:
|
|
264
|
-
limit:
|
|
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) ->
|
|
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
|
-
|
|
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(
|
|
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:
|
|
546
|
-
serde:
|
|
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:
|
|
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:
|
|
623
|
+
config: RunnableConfig | None,
|
|
622
624
|
*,
|
|
623
|
-
filter:
|
|
624
|
-
before:
|
|
625
|
-
limit:
|
|
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) ->
|
|
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
|
-
|
|
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(
|
|
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:
|
|
866
|
+
config: RunnableConfig | None,
|
|
865
867
|
*,
|
|
866
|
-
filter:
|
|
867
|
-
before:
|
|
868
|
-
limit:
|
|
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) ->
|
|
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
|
-
|
|
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,
|
|
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
|
|
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(
|
|
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=
|
|
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
|
|
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(
|
|
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(
|
|
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 = "
|
|
7
|
+
version = "3.0.0"
|
|
8
8
|
description = "Library with a Postgres implementation of LangGraph checkpoint saver."
|
|
9
9
|
authors = []
|
|
10
|
-
requires-python = ">=3.
|
|
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.
|
|
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
|
-
|
|
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
|
{langgraph_checkpoint_postgres-2.0.24 → langgraph_checkpoint_postgres-3.0.0}/tests/test_async.py
RENAMED
|
@@ -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
|