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.
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/PKG-INFO +3 -3
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/__init__.py +7 -6
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/_ainternal.py +1 -2
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/_internal.py +1 -2
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/aio.py +9 -8
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/base.py +3 -3
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/shallow.py +28 -26
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/aio.py +9 -7
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/base.py +12 -10
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/pyproject.toml +15 -7
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_async_store.py +0 -6
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_store.py +3 -3
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/uv.lock +208 -221
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/.gitignore +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/Makefile +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/README.md +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/checkpoint/postgres/py.typed +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/__init__.py +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/langgraph/store/postgres/py.typed +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/__init__.py +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/compose-postgres.yml +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/conftest.py +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/embed_test_utils.py +0 -0
- {langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_async.py +0 -0
- {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:
|
|
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.
|
|
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
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -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,
|
|
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 =
|
|
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
|
|
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,
|
|
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
|
|
|
@@ -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", "")
|
|
@@ -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.1"
|
|
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.1.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
|
|
@@ -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("/")
|
{langgraph_checkpoint_postgres-2.0.25 → langgraph_checkpoint_postgres-3.0.1}/tests/test_store.py
RENAMED
|
@@ -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
|