langgraph-checkpoint-postgres 2.0.3__tar.gz → 2.0.5__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.3 → langgraph_checkpoint_postgres-2.0.5}/PKG-INFO +2 -2
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/__init__.py +19 -24
- langgraph_checkpoint_postgres-2.0.5/langgraph/checkpoint/postgres/_ainternal.py +24 -0
- langgraph_checkpoint_postgres-2.0.5/langgraph/checkpoint/postgres/_internal.py +22 -0
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/aio.py +46 -45
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/base.py +3 -2
- langgraph_checkpoint_postgres-2.0.5/langgraph/store/postgres/aio.py +369 -0
- langgraph_checkpoint_postgres-2.0.5/langgraph/store/postgres/base.py +974 -0
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/pyproject.toml +2 -2
- langgraph_checkpoint_postgres-2.0.3/langgraph/store/postgres/aio.py +0 -218
- langgraph_checkpoint_postgres-2.0.3/langgraph/store/postgres/base.py +0 -457
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/README.md +0 -0
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/py.typed +0 -0
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/store/postgres/__init__.py +0 -0
- {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/store/postgres/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: langgraph-checkpoint-postgres
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: Library with a Postgres implementation of LangGraph checkpoint saver.
|
|
5
5
|
Home-page: https://www.github.com/langchain-ai/langgraph
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Requires-Dist: langgraph-checkpoint (>=2.0.
|
|
14
|
+
Requires-Dist: langgraph-checkpoint (>=2.0.7,<3.0.0)
|
|
15
15
|
Requires-Dist: orjson (>=3.10.1)
|
|
16
16
|
Requires-Dist: psycopg (>=3.0.0,<4.0.0)
|
|
17
17
|
Requires-Dist: psycopg-pool (>=3.0.0,<4.0.0)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import threading
|
|
2
|
+
from collections.abc import Iterator, Sequence
|
|
2
3
|
from contextlib import contextmanager
|
|
3
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, Optional
|
|
4
5
|
|
|
5
6
|
from langchain_core.runnables import RunnableConfig
|
|
6
7
|
from psycopg import Capabilities, Connection, Cursor, Pipeline
|
|
@@ -17,21 +18,11 @@ from langgraph.checkpoint.base import (
|
|
|
17
18
|
CheckpointTuple,
|
|
18
19
|
get_checkpoint_id,
|
|
19
20
|
)
|
|
21
|
+
from langgraph.checkpoint.postgres import _internal
|
|
20
22
|
from langgraph.checkpoint.postgres.base import BasePostgresSaver
|
|
21
23
|
from langgraph.checkpoint.serde.base import SerializerProtocol
|
|
22
24
|
|
|
23
|
-
Conn =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@contextmanager
|
|
27
|
-
def _get_connection(conn: Conn) -> Iterator[Connection[DictRow]]:
|
|
28
|
-
if isinstance(conn, Connection):
|
|
29
|
-
yield conn
|
|
30
|
-
elif isinstance(conn, ConnectionPool):
|
|
31
|
-
with conn.connection() as conn:
|
|
32
|
-
yield conn
|
|
33
|
-
else:
|
|
34
|
-
raise TypeError(f"Invalid connection type: {type(conn)}")
|
|
25
|
+
Conn = _internal.Conn # For backward compatibility
|
|
35
26
|
|
|
36
27
|
|
|
37
28
|
class PostgresSaver(BasePostgresSaver):
|
|
@@ -39,7 +30,7 @@ class PostgresSaver(BasePostgresSaver):
|
|
|
39
30
|
|
|
40
31
|
def __init__(
|
|
41
32
|
self,
|
|
42
|
-
conn: Conn,
|
|
33
|
+
conn: _internal.Conn,
|
|
43
34
|
pipe: Optional[Pipeline] = None,
|
|
44
35
|
serde: Optional[SerializerProtocol] = None,
|
|
45
36
|
) -> None:
|
|
@@ -73,9 +64,9 @@ class PostgresSaver(BasePostgresSaver):
|
|
|
73
64
|
) as conn:
|
|
74
65
|
if pipeline:
|
|
75
66
|
with conn.pipeline() as pipe:
|
|
76
|
-
yield
|
|
67
|
+
yield cls(conn, pipe)
|
|
77
68
|
else:
|
|
78
|
-
yield
|
|
69
|
+
yield cls(conn)
|
|
79
70
|
|
|
80
71
|
def setup(self) -> None:
|
|
81
72
|
"""Set up the checkpoint database asynchronously.
|
|
@@ -373,7 +364,7 @@ class PostgresSaver(BasePostgresSaver):
|
|
|
373
364
|
Will be applied regardless of whether the PostgresSaver instance was initialized with a pipeline.
|
|
374
365
|
If pipeline mode is not supported, will fall back to using transaction context manager.
|
|
375
366
|
"""
|
|
376
|
-
with
|
|
367
|
+
with _internal.get_connection(self.conn) as conn:
|
|
377
368
|
if self.pipe:
|
|
378
369
|
# a connection in pipeline mode can be used concurrently
|
|
379
370
|
# in multiple threads/coroutines, but only one cursor can be
|
|
@@ -388,19 +379,23 @@ class PostgresSaver(BasePostgresSaver):
|
|
|
388
379
|
# a connection not in pipeline mode can only be used by one
|
|
389
380
|
# thread/coroutine at a time, so we acquire a lock
|
|
390
381
|
if self.supports_pipeline:
|
|
391
|
-
with
|
|
392
|
-
|
|
393
|
-
|
|
382
|
+
with (
|
|
383
|
+
self.lock,
|
|
384
|
+
conn.pipeline(),
|
|
385
|
+
conn.cursor(binary=True, row_factory=dict_row) as cur,
|
|
386
|
+
):
|
|
394
387
|
yield cur
|
|
395
388
|
else:
|
|
396
389
|
# Use connection's transaction context manager when pipeline mode not supported
|
|
397
|
-
with
|
|
398
|
-
|
|
399
|
-
|
|
390
|
+
with (
|
|
391
|
+
self.lock,
|
|
392
|
+
conn.transaction(),
|
|
393
|
+
conn.cursor(binary=True, row_factory=dict_row) as cur,
|
|
394
|
+
):
|
|
400
395
|
yield cur
|
|
401
396
|
else:
|
|
402
397
|
with self.lock, conn.cursor(binary=True, row_factory=dict_row) as cur:
|
|
403
398
|
yield cur
|
|
404
399
|
|
|
405
400
|
|
|
406
|
-
__all__ = ["PostgresSaver", "Conn"]
|
|
401
|
+
__all__ = ["PostgresSaver", "BasePostgresSaver", "Conn"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Shared async utility functions for the Postgres checkpoint & storage classes."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import AsyncIterator
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
from psycopg import AsyncConnection
|
|
8
|
+
from psycopg.rows import DictRow
|
|
9
|
+
from psycopg_pool import AsyncConnectionPool
|
|
10
|
+
|
|
11
|
+
Conn = Union[AsyncConnection[DictRow], AsyncConnectionPool[AsyncConnection[DictRow]]]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@asynccontextmanager
|
|
15
|
+
async def get_connection(
|
|
16
|
+
conn: Conn,
|
|
17
|
+
) -> AsyncIterator[AsyncConnection[DictRow]]:
|
|
18
|
+
if isinstance(conn, AsyncConnection):
|
|
19
|
+
yield conn
|
|
20
|
+
elif isinstance(conn, AsyncConnectionPool):
|
|
21
|
+
async with conn.connection() as conn:
|
|
22
|
+
yield conn
|
|
23
|
+
else:
|
|
24
|
+
raise TypeError(f"Invalid connection type: {type(conn)}")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Shared utility functions for the Postgres checkpoint & storage classes."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
from psycopg import Connection
|
|
8
|
+
from psycopg.rows import DictRow
|
|
9
|
+
from psycopg_pool import ConnectionPool
|
|
10
|
+
|
|
11
|
+
Conn = Union[Connection[DictRow], ConnectionPool[Connection[DictRow]]]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@contextmanager
|
|
15
|
+
def get_connection(conn: Conn) -> Iterator[Connection[DictRow]]:
|
|
16
|
+
if isinstance(conn, Connection):
|
|
17
|
+
yield conn
|
|
18
|
+
elif isinstance(conn, ConnectionPool):
|
|
19
|
+
with conn.connection() as conn:
|
|
20
|
+
yield conn
|
|
21
|
+
else:
|
|
22
|
+
raise TypeError(f"Invalid connection type: {type(conn)}")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
2
3
|
from contextlib import asynccontextmanager
|
|
3
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, Optional
|
|
4
5
|
|
|
5
6
|
from langchain_core.runnables import RunnableConfig
|
|
6
7
|
from psycopg import AsyncConnection, AsyncCursor, AsyncPipeline, Capabilities
|
|
@@ -17,23 +18,11 @@ from langgraph.checkpoint.base import (
|
|
|
17
18
|
CheckpointTuple,
|
|
18
19
|
get_checkpoint_id,
|
|
19
20
|
)
|
|
21
|
+
from langgraph.checkpoint.postgres import _ainternal
|
|
20
22
|
from langgraph.checkpoint.postgres.base import BasePostgresSaver
|
|
21
23
|
from langgraph.checkpoint.serde.base import SerializerProtocol
|
|
22
24
|
|
|
23
|
-
Conn =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@asynccontextmanager
|
|
27
|
-
async def _get_connection(
|
|
28
|
-
conn: Conn,
|
|
29
|
-
) -> AsyncIterator[AsyncConnection[DictRow]]:
|
|
30
|
-
if isinstance(conn, AsyncConnection):
|
|
31
|
-
yield conn
|
|
32
|
-
elif isinstance(conn, AsyncConnectionPool):
|
|
33
|
-
async with conn.connection() as conn:
|
|
34
|
-
yield conn
|
|
35
|
-
else:
|
|
36
|
-
raise TypeError(f"Invalid connection type: {type(conn)}")
|
|
25
|
+
Conn = _ainternal.Conn # For backward compatibility
|
|
37
26
|
|
|
38
27
|
|
|
39
28
|
class AsyncPostgresSaver(BasePostgresSaver):
|
|
@@ -41,7 +30,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
41
30
|
|
|
42
31
|
def __init__(
|
|
43
32
|
self,
|
|
44
|
-
conn: Conn,
|
|
33
|
+
conn: _ainternal.Conn,
|
|
45
34
|
pipe: Optional[AsyncPipeline] = None,
|
|
46
35
|
serde: Optional[SerializerProtocol] = None,
|
|
47
36
|
) -> None:
|
|
@@ -80,9 +69,9 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
80
69
|
) as conn:
|
|
81
70
|
if pipeline:
|
|
82
71
|
async with conn.pipeline() as pipe:
|
|
83
|
-
yield
|
|
72
|
+
yield cls(conn=conn, pipe=pipe, serde=serde)
|
|
84
73
|
else:
|
|
85
|
-
yield
|
|
74
|
+
yield cls(conn=conn, serde=serde)
|
|
86
75
|
|
|
87
76
|
async def setup(self) -> None:
|
|
88
77
|
"""Set up the checkpoint database asynchronously.
|
|
@@ -157,15 +146,17 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
157
146
|
value["pending_sends"],
|
|
158
147
|
),
|
|
159
148
|
self._load_metadata(value["metadata"]),
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
149
|
+
(
|
|
150
|
+
{
|
|
151
|
+
"configurable": {
|
|
152
|
+
"thread_id": value["thread_id"],
|
|
153
|
+
"checkpoint_ns": value["checkpoint_ns"],
|
|
154
|
+
"checkpoint_id": value["parent_checkpoint_id"],
|
|
155
|
+
}
|
|
165
156
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
157
|
+
if value["parent_checkpoint_id"]
|
|
158
|
+
else None
|
|
159
|
+
),
|
|
169
160
|
await asyncio.to_thread(self._load_writes, value["pending_writes"]),
|
|
170
161
|
)
|
|
171
162
|
|
|
@@ -216,15 +207,17 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
216
207
|
value["pending_sends"],
|
|
217
208
|
),
|
|
218
209
|
self._load_metadata(value["metadata"]),
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
"
|
|
222
|
-
|
|
223
|
-
|
|
210
|
+
(
|
|
211
|
+
{
|
|
212
|
+
"configurable": {
|
|
213
|
+
"thread_id": thread_id,
|
|
214
|
+
"checkpoint_ns": checkpoint_ns,
|
|
215
|
+
"checkpoint_id": value["parent_checkpoint_id"],
|
|
216
|
+
}
|
|
224
217
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
218
|
+
if value["parent_checkpoint_id"]
|
|
219
|
+
else None
|
|
220
|
+
),
|
|
228
221
|
await asyncio.to_thread(self._load_writes, value["pending_writes"]),
|
|
229
222
|
)
|
|
230
223
|
|
|
@@ -331,7 +324,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
331
324
|
Will be applied regardless of whether the AsyncPostgresSaver instance was initialized with a pipeline.
|
|
332
325
|
If pipeline mode is not supported, will fall back to using transaction context manager.
|
|
333
326
|
"""
|
|
334
|
-
async with
|
|
327
|
+
async with _ainternal.get_connection(self.conn) as conn:
|
|
335
328
|
if self.pipe:
|
|
336
329
|
# a connection in pipeline mode can be used concurrently
|
|
337
330
|
# in multiple threads/coroutines, but only one cursor can be
|
|
@@ -346,20 +339,25 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
346
339
|
# a connection not in pipeline mode can only be used by one
|
|
347
340
|
# thread/coroutine at a time, so we acquire a lock
|
|
348
341
|
if self.supports_pipeline:
|
|
349
|
-
async with
|
|
350
|
-
|
|
351
|
-
|
|
342
|
+
async with (
|
|
343
|
+
self.lock,
|
|
344
|
+
conn.pipeline(),
|
|
345
|
+
conn.cursor(binary=True, row_factory=dict_row) as cur,
|
|
346
|
+
):
|
|
352
347
|
yield cur
|
|
353
348
|
else:
|
|
354
349
|
# Use connection's transaction context manager when pipeline mode not supported
|
|
355
|
-
async with
|
|
356
|
-
|
|
357
|
-
|
|
350
|
+
async with (
|
|
351
|
+
self.lock,
|
|
352
|
+
conn.transaction(),
|
|
353
|
+
conn.cursor(binary=True, row_factory=dict_row) as cur,
|
|
354
|
+
):
|
|
358
355
|
yield cur
|
|
359
356
|
else:
|
|
360
|
-
async with
|
|
361
|
-
|
|
362
|
-
|
|
357
|
+
async with (
|
|
358
|
+
self.lock,
|
|
359
|
+
conn.cursor(binary=True, row_factory=dict_row) as cur,
|
|
360
|
+
):
|
|
363
361
|
yield cur
|
|
364
362
|
|
|
365
363
|
def list(
|
|
@@ -388,7 +386,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
388
386
|
while True:
|
|
389
387
|
try:
|
|
390
388
|
yield asyncio.run_coroutine_threadsafe(
|
|
391
|
-
anext(aiter_),
|
|
389
|
+
anext(aiter_), # noqa: F821
|
|
392
390
|
self.loop,
|
|
393
391
|
).result()
|
|
394
392
|
except StopAsyncIteration:
|
|
@@ -467,3 +465,6 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
467
465
|
return asyncio.run_coroutine_threadsafe(
|
|
468
466
|
self.aput_writes(config, writes, task_id), self.loop
|
|
469
467
|
).result()
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
__all__ = ["AsyncPostgresSaver", "Conn"]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import random
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import Any, Optional, cast
|
|
3
4
|
|
|
4
5
|
from langchain_core.runnables import RunnableConfig
|
|
5
6
|
from psycopg.types.json import Jsonb
|
|
@@ -249,7 +250,7 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
|
|
|
249
250
|
config: Optional[RunnableConfig],
|
|
250
251
|
filter: MetadataInput,
|
|
251
252
|
before: Optional[RunnableConfig] = None,
|
|
252
|
-
) ->
|
|
253
|
+
) -> tuple[str, list[Any]]:
|
|
253
254
|
"""Return WHERE clause predicates for alist() given config, filter, before.
|
|
254
255
|
|
|
255
256
|
This method returns a tuple of a string and a tuple of values. The string
|