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.
Files changed (15) hide show
  1. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/PKG-INFO +2 -2
  2. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/__init__.py +19 -24
  3. langgraph_checkpoint_postgres-2.0.5/langgraph/checkpoint/postgres/_ainternal.py +24 -0
  4. langgraph_checkpoint_postgres-2.0.5/langgraph/checkpoint/postgres/_internal.py +22 -0
  5. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/aio.py +46 -45
  6. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/base.py +3 -2
  7. langgraph_checkpoint_postgres-2.0.5/langgraph/store/postgres/aio.py +369 -0
  8. langgraph_checkpoint_postgres-2.0.5/langgraph/store/postgres/base.py +974 -0
  9. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/pyproject.toml +2 -2
  10. langgraph_checkpoint_postgres-2.0.3/langgraph/store/postgres/aio.py +0 -218
  11. langgraph_checkpoint_postgres-2.0.3/langgraph/store/postgres/base.py +0 -457
  12. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/README.md +0 -0
  13. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/checkpoint/postgres/py.typed +0 -0
  14. {langgraph_checkpoint_postgres-2.0.3 → langgraph_checkpoint_postgres-2.0.5}/langgraph/store/postgres/__init__.py +0 -0
  15. {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
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.2,<3.0.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, Iterator, Optional, Sequence, Union
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 = Union[Connection[DictRow], ConnectionPool[Connection[DictRow]]]
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 PostgresSaver(conn, pipe)
67
+ yield cls(conn, pipe)
77
68
  else:
78
- yield PostgresSaver(conn)
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 _get_connection(self.conn) as conn:
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 self.lock, conn.pipeline(), conn.cursor(
392
- binary=True, row_factory=dict_row
393
- ) as cur:
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 self.lock, conn.transaction(), conn.cursor(
398
- binary=True, row_factory=dict_row
399
- ) as cur:
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, AsyncIterator, Iterator, Optional, Sequence, Union
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 = Union[AsyncConnection[DictRow], AsyncConnectionPool[AsyncConnection[DictRow]]]
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 AsyncPostgresSaver(conn=conn, pipe=pipe, serde=serde)
72
+ yield cls(conn=conn, pipe=pipe, serde=serde)
84
73
  else:
85
- yield AsyncPostgresSaver(conn=conn, serde=serde)
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
- "configurable": {
162
- "thread_id": value["thread_id"],
163
- "checkpoint_ns": value["checkpoint_ns"],
164
- "checkpoint_id": value["parent_checkpoint_id"],
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
- if value["parent_checkpoint_id"]
168
- else None,
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
- "configurable": {
221
- "thread_id": thread_id,
222
- "checkpoint_ns": checkpoint_ns,
223
- "checkpoint_id": value["parent_checkpoint_id"],
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
- if value["parent_checkpoint_id"]
227
- else None,
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 _get_connection(self.conn) as conn:
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 self.lock, conn.pipeline(), conn.cursor(
350
- binary=True, row_factory=dict_row
351
- ) as cur:
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 self.lock, conn.transaction(), conn.cursor(
356
- binary=True, row_factory=dict_row
357
- ) as cur:
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 self.lock, conn.cursor(
361
- binary=True, row_factory=dict_row
362
- ) as cur:
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 typing import Any, List, Optional, Sequence, Tuple, cast
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
- ) -> Tuple[str, List[Any]]:
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