langgraph-checkpoint-postgres 2.0.23__tar.gz → 2.0.24__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 (27) hide show
  1. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/PKG-INFO +1 -1
  2. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/checkpoint/postgres/__init__.py +8 -11
  3. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/checkpoint/postgres/aio.py +8 -10
  4. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/checkpoint/postgres/base.py +1 -2
  5. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/checkpoint/postgres/shallow.py +12 -12
  6. langgraph_checkpoint_postgres-2.0.24/langgraph/store/postgres/__init__.py +4 -0
  7. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/pyproject.toml +1 -1
  8. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/test_async.py +37 -7
  9. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/test_store.py +40 -2
  10. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/test_sync.py +36 -7
  11. langgraph_checkpoint_postgres-2.0.24/uv.lock +1211 -0
  12. langgraph_checkpoint_postgres-2.0.23/langgraph/store/postgres/__init__.py +0 -4
  13. langgraph_checkpoint_postgres-2.0.23/uv.lock +0 -1225
  14. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/.gitignore +0 -0
  15. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/Makefile +0 -0
  16. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/README.md +0 -0
  17. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/checkpoint/postgres/_ainternal.py +0 -0
  18. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/checkpoint/postgres/_internal.py +0 -0
  19. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/checkpoint/postgres/py.typed +0 -0
  20. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/store/postgres/aio.py +5 -5
  21. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/store/postgres/base.py +8 -8
  22. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/langgraph/store/postgres/py.typed +0 -0
  23. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/__init__.py +0 -0
  24. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/compose-postgres.yml +0 -0
  25. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/conftest.py +0 -0
  26. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/embed_test_utils.py +0 -0
  27. {langgraph_checkpoint_postgres-2.0.23 → langgraph_checkpoint_postgres-2.0.24}/tests/test_async_store.py +2 -2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-checkpoint-postgres
3
- Version: 2.0.23
3
+ Version: 2.0.24
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,11 +7,6 @@ from contextlib import contextmanager
7
7
  from typing import Any
8
8
 
9
9
  from langchain_core.runnables import RunnableConfig
10
- from psycopg import Capabilities, Connection, Cursor, Pipeline
11
- from psycopg.rows import DictRow, dict_row
12
- from psycopg.types.json import Jsonb
13
- from psycopg_pool import ConnectionPool
14
-
15
10
  from langgraph.checkpoint.base import (
16
11
  WRITES_IDX_MAP,
17
12
  ChannelVersions,
@@ -21,10 +16,15 @@ from langgraph.checkpoint.base import (
21
16
  get_checkpoint_id,
22
17
  get_checkpoint_metadata,
23
18
  )
19
+ from langgraph.checkpoint.serde.base import SerializerProtocol
20
+ from psycopg import Capabilities, Connection, Cursor, Pipeline
21
+ from psycopg.rows import DictRow, dict_row
22
+ from psycopg.types.json import Jsonb
23
+ from psycopg_pool import ConnectionPool
24
+
24
25
  from langgraph.checkpoint.postgres import _internal
25
26
  from langgraph.checkpoint.postgres.base import BasePostgresSaver
26
27
  from langgraph.checkpoint.postgres.shallow import ShallowPostgresSaver
27
- from langgraph.checkpoint.serde.base import SerializerProtocol
28
28
 
29
29
  Conn = _internal.Conn # For backward compatibility
30
30
 
@@ -284,10 +284,7 @@ class PostgresSaver(BasePostgresSaver):
284
284
  configurable = config["configurable"].copy()
285
285
  thread_id = configurable.pop("thread_id")
286
286
  checkpoint_ns = configurable.pop("checkpoint_ns")
287
- checkpoint_id = configurable.pop(
288
- "checkpoint_id", configurable.pop("thread_ts", None)
289
- )
290
-
287
+ checkpoint_id = configurable.pop("checkpoint_id", None)
291
288
  copy = checkpoint.copy()
292
289
  copy["channel_values"] = copy["channel_values"].copy()
293
290
  next_config = {
@@ -453,7 +450,7 @@ class PostgresSaver(BasePostgresSaver):
453
450
  {
454
451
  **value["checkpoint"],
455
452
  "channel_values": {
456
- **value["checkpoint"].get("channel_values"),
453
+ **(value["checkpoint"].get("channel_values") or {}),
457
454
  **self._load_blobs(value["channel_values"]),
458
455
  },
459
456
  },
@@ -7,11 +7,6 @@ from contextlib import asynccontextmanager
7
7
  from typing import Any
8
8
 
9
9
  from langchain_core.runnables import RunnableConfig
10
- from psycopg import AsyncConnection, AsyncCursor, AsyncPipeline, Capabilities
11
- from psycopg.rows import DictRow, dict_row
12
- from psycopg.types.json import Jsonb
13
- from psycopg_pool import AsyncConnectionPool
14
-
15
10
  from langgraph.checkpoint.base import (
16
11
  WRITES_IDX_MAP,
17
12
  ChannelVersions,
@@ -21,10 +16,15 @@ from langgraph.checkpoint.base import (
21
16
  get_checkpoint_id,
22
17
  get_checkpoint_metadata,
23
18
  )
19
+ from langgraph.checkpoint.serde.base import SerializerProtocol
20
+ from psycopg import AsyncConnection, AsyncCursor, AsyncPipeline, Capabilities
21
+ from psycopg.rows import DictRow, dict_row
22
+ from psycopg.types.json import Jsonb
23
+ from psycopg_pool import AsyncConnectionPool
24
+
24
25
  from langgraph.checkpoint.postgres import _ainternal
25
26
  from langgraph.checkpoint.postgres.base import BasePostgresSaver
26
27
  from langgraph.checkpoint.postgres.shallow import AsyncShallowPostgresSaver
27
- from langgraph.checkpoint.serde.base import SerializerProtocol
28
28
 
29
29
  Conn = _ainternal.Conn # For backward compatibility
30
30
 
@@ -240,9 +240,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
240
240
  configurable = config["configurable"].copy()
241
241
  thread_id = configurable.pop("thread_id")
242
242
  checkpoint_ns = configurable.pop("checkpoint_ns")
243
- checkpoint_id = configurable.pop(
244
- "checkpoint_id", configurable.pop("thread_ts", None)
245
- )
243
+ checkpoint_id = configurable.pop("checkpoint_id", None)
246
244
 
247
245
  copy = checkpoint.copy()
248
246
  copy["channel_values"] = copy["channel_values"].copy()
@@ -411,7 +409,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
411
409
  {
412
410
  **value["checkpoint"],
413
411
  "channel_values": {
414
- **value["checkpoint"].get("channel_values"),
412
+ **(value["checkpoint"].get("channel_values") or {}),
415
413
  **self._load_blobs(value["channel_values"]),
416
414
  },
417
415
  },
@@ -5,8 +5,6 @@ from collections.abc import Sequence
5
5
  from typing import Any, Optional, cast
6
6
 
7
7
  from langchain_core.runnables import RunnableConfig
8
- from psycopg.types.json import Jsonb
9
-
10
8
  from langgraph.checkpoint.base import (
11
9
  WRITES_IDX_MAP,
12
10
  BaseCheckpointSaver,
@@ -14,6 +12,7 @@ from langgraph.checkpoint.base import (
14
12
  get_checkpoint_id,
15
13
  )
16
14
  from langgraph.checkpoint.serde.types import TASKS
15
+ from psycopg.types.json import Jsonb
17
16
 
18
17
  MetadataInput = Optional[dict[str, Any]]
19
18
 
@@ -6,6 +6,16 @@ from contextlib import asynccontextmanager, contextmanager
6
6
  from typing import Any, Optional
7
7
 
8
8
  from langchain_core.runnables import RunnableConfig
9
+ from langgraph.checkpoint.base import (
10
+ WRITES_IDX_MAP,
11
+ ChannelVersions,
12
+ Checkpoint,
13
+ CheckpointMetadata,
14
+ CheckpointTuple,
15
+ get_checkpoint_metadata,
16
+ )
17
+ from langgraph.checkpoint.serde.base import SerializerProtocol
18
+ from langgraph.checkpoint.serde.types import TASKS
9
19
  from psycopg import (
10
20
  AsyncConnection,
11
21
  AsyncCursor,
@@ -19,18 +29,8 @@ from psycopg.rows import DictRow, dict_row
19
29
  from psycopg.types.json import Jsonb
20
30
  from psycopg_pool import AsyncConnectionPool, ConnectionPool
21
31
 
22
- from langgraph.checkpoint.base import (
23
- WRITES_IDX_MAP,
24
- ChannelVersions,
25
- Checkpoint,
26
- CheckpointMetadata,
27
- CheckpointTuple,
28
- get_checkpoint_metadata,
29
- )
30
32
  from langgraph.checkpoint.postgres import _ainternal, _internal
31
33
  from langgraph.checkpoint.postgres.base import BasePostgresSaver
32
- from langgraph.checkpoint.serde.base import SerializerProtocol
33
- from langgraph.checkpoint.serde.types import TASKS
34
34
 
35
35
  """
36
36
  To add a new migration, add a new string to the MIGRATIONS list.
@@ -191,7 +191,7 @@ class ShallowPostgresSaver(BasePostgresSaver):
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. "
194
- "Use PostgresSaver instead, and invoke the graph with `graph.invoke(..., checkpoint_during=False)`.",
194
+ "Use PostgresSaver instead, and invoke the graph with `graph.invoke(..., durability='exit')`.",
195
195
  DeprecationWarning,
196
196
  stacklevel=2,
197
197
  )
@@ -547,7 +547,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
547
547
  ) -> None:
548
548
  warnings.warn(
549
549
  "AsyncShallowPostgresSaver is deprecated as of version 2.0.20 and will be removed in 3.0.0. "
550
- "Use AsyncPostgresSaver instead, and invoke the graph with `await graph.ainvoke(..., checkpoint_during=False)`.",
550
+ "Use AsyncPostgresSaver instead, and invoke the graph with `await graph.ainvoke(..., durability='exit')`.",
551
551
  DeprecationWarning,
552
552
  stacklevel=2,
553
553
  )
@@ -0,0 +1,4 @@
1
+ from langgraph.store.postgres.aio import AsyncPostgresStore
2
+ from langgraph.store.postgres.base import PoolConfig, PostgresStore
3
+
4
+ __all__ = ["AsyncPostgresStore", "PoolConfig", "PostgresStore"]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "langgraph-checkpoint-postgres"
7
- version = "2.0.23"
7
+ version = "2.0.24"
8
8
  description = "Library with a Postgres implementation of LangGraph checkpoint saver."
9
9
  authors = []
10
10
  requires-python = ">=3.9"
@@ -6,10 +6,6 @@ from uuid import uuid4
6
6
 
7
7
  import pytest
8
8
  from langchain_core.runnables import RunnableConfig
9
- from psycopg import AsyncConnection
10
- from psycopg.rows import dict_row
11
- from psycopg_pool import AsyncConnectionPool
12
-
13
9
  from langgraph.checkpoint.base import (
14
10
  EXCLUDED_METADATA_KEYS,
15
11
  Checkpoint,
@@ -17,11 +13,15 @@ from langgraph.checkpoint.base import (
17
13
  create_checkpoint,
18
14
  empty_checkpoint,
19
15
  )
16
+ from langgraph.checkpoint.serde.types import TASKS
17
+ from psycopg import AsyncConnection
18
+ from psycopg.rows import dict_row
19
+ from psycopg_pool import AsyncConnectionPool
20
+
20
21
  from langgraph.checkpoint.postgres.aio import (
21
22
  AsyncPostgresSaver,
22
23
  AsyncShallowPostgresSaver,
23
24
  )
24
- from langgraph.checkpoint.serde.types import TASKS
25
25
  from tests.conftest import DEFAULT_POSTGRES_URI
26
26
 
27
27
 
@@ -161,8 +161,7 @@ def test_data():
161
161
  config_1: RunnableConfig = {
162
162
  "configurable": {
163
163
  "thread_id": "thread-1",
164
- # for backwards compatibility testing
165
- "thread_ts": "1",
164
+ "checkpoint_id": "1",
166
165
  "checkpoint_ns": "",
167
166
  }
168
167
  }
@@ -345,3 +344,34 @@ async def test_pending_sends_migration(saver_name: str) -> None:
345
344
  TASKS: ["send-1", "send-2", "send-3"]
346
345
  }
347
346
  assert TASKS in search_results[0].checkpoint["channel_versions"]
347
+
348
+
349
+ @pytest.mark.parametrize("saver_name", ["base", "pool", "pipe"])
350
+ async def test_get_checkpoint_no_channel_values(
351
+ monkeypatch, saver_name: str, test_data
352
+ ) -> None:
353
+ """Backwards compatibility test that verifies a checkpoint with no channel_values key can be retrieved without throwing an error."""
354
+ async with _saver(saver_name) as saver:
355
+ config = {
356
+ "configurable": {
357
+ "thread_id": "thread-2",
358
+ "checkpoint_ns": "",
359
+ "__super_private_key": "super_private_value",
360
+ },
361
+ "metadata": {"run_id": "my_run_id"},
362
+ }
363
+ chkpnt: Checkpoint = create_checkpoint(empty_checkpoint(), {}, 1)
364
+ await saver.aput(config, chkpnt, {}, {})
365
+
366
+ load_checkpoint_tuple = saver._load_checkpoint_tuple
367
+
368
+ def patched_load_checkpoint_tuple(value):
369
+ value["checkpoint"].pop("channel_values", None)
370
+ return load_checkpoint_tuple(value)
371
+
372
+ monkeypatch.setattr(
373
+ saver, "_load_checkpoint_tuple", patched_load_checkpoint_tuple
374
+ )
375
+
376
+ checkpoint = await saver.aget_tuple(config)
377
+ assert checkpoint.checkpoint["channel_values"] == {}
@@ -9,8 +9,6 @@ from uuid import uuid4
9
9
 
10
10
  import pytest
11
11
  from langchain_core.embeddings import Embeddings
12
- from psycopg import Connection
13
-
14
12
  from langgraph.store.base import (
15
13
  GetOp,
16
14
  Item,
@@ -19,6 +17,8 @@ from langgraph.store.base import (
19
17
  PutOp,
20
18
  SearchOp,
21
19
  )
20
+ from psycopg import Connection
21
+
22
22
  from langgraph.store.postgres import PostgresStore
23
23
  from tests.conftest import (
24
24
  DEFAULT_URI,
@@ -861,3 +861,41 @@ def test_store_ttl(store):
861
861
  # Now has been (TTL_SECONDS-2)*2 > TTL_SECONDS + TTL_SECONDS/2
862
862
  res = store.search(ns, query="bar", refresh_ttl=False)
863
863
  assert len(res) == 0
864
+
865
+
866
+ @pytest.mark.parametrize(
867
+ "vector_type,distance_type",
868
+ [
869
+ ("vector", "cosine"),
870
+ ("vector", "inner_product"),
871
+ ("halfvec", "cosine"),
872
+ ("halfvec", "inner_product"),
873
+ ],
874
+ )
875
+ def test_non_ascii(
876
+ request: Any,
877
+ fake_embeddings: CharacterEmbeddings,
878
+ vector_type: str,
879
+ distance_type: str,
880
+ ) -> None:
881
+ """Test support for non-ascii characters"""
882
+ with _create_vector_store(vector_type, distance_type, fake_embeddings) as store:
883
+ store.put(("user_123", "memories"), "1", {"text": "这是中文"}) # Chinese
884
+ store.put(
885
+ ("user_123", "memories"), "2", {"text": "これは日本語です"}
886
+ ) # Japanese
887
+ store.put(("user_123", "memories"), "3", {"text": "이건 한국어야"}) # Korean
888
+ store.put(("user_123", "memories"), "4", {"text": "Это русский"}) # Russian
889
+ store.put(("user_123", "memories"), "5", {"text": "यह रूसी है"}) # Hindi
890
+
891
+ result1 = store.search(("user_123", "memories"), query="这是中文")
892
+ result2 = store.search(("user_123", "memories"), query="これは日本語です")
893
+ result3 = store.search(("user_123", "memories"), query="이건 한국어야")
894
+ result4 = store.search(("user_123", "memories"), query="Это русский")
895
+ result5 = store.search(("user_123", "memories"), query="यह रूसी है")
896
+
897
+ assert result1[0].key == "1"
898
+ assert result2[0].key == "2"
899
+ assert result3[0].key == "3"
900
+ assert result4[0].key == "4"
901
+ assert result5[0].key == "5"
@@ -7,10 +7,6 @@ from uuid import uuid4
7
7
 
8
8
  import pytest
9
9
  from langchain_core.runnables import RunnableConfig
10
- from psycopg import Connection
11
- from psycopg.rows import dict_row
12
- from psycopg_pool import ConnectionPool
13
-
14
10
  from langgraph.checkpoint.base import (
15
11
  EXCLUDED_METADATA_KEYS,
16
12
  Checkpoint,
@@ -18,8 +14,12 @@ from langgraph.checkpoint.base import (
18
14
  create_checkpoint,
19
15
  empty_checkpoint,
20
16
  )
21
- from langgraph.checkpoint.postgres import PostgresSaver, ShallowPostgresSaver
22
17
  from langgraph.checkpoint.serde.types import TASKS
18
+ from psycopg import Connection
19
+ from psycopg.rows import dict_row
20
+ from psycopg_pool import ConnectionPool
21
+
22
+ from langgraph.checkpoint.postgres import PostgresSaver, ShallowPostgresSaver
23
23
  from tests.conftest import DEFAULT_POSTGRES_URI
24
24
 
25
25
 
@@ -143,8 +143,7 @@ def test_data():
143
143
  config_1: RunnableConfig = {
144
144
  "configurable": {
145
145
  "thread_id": "thread-1",
146
- # for backwards compatibility testing
147
- "thread_ts": "1",
146
+ "checkpoint_id": "1",
148
147
  "checkpoint_ns": "",
149
148
  }
150
149
  }
@@ -333,3 +332,33 @@ def test_pending_sends_migration(saver_name: str) -> None:
333
332
  TASKS: ["send-1", "send-2", "send-3"]
334
333
  }
335
334
  assert TASKS in search_results[0].checkpoint["channel_versions"]
335
+
336
+
337
+ @pytest.mark.parametrize("saver_name", ["base", "pool", "pipe"])
338
+ def test_get_checkpoint_no_channel_values(
339
+ monkeypatch, saver_name: str, test_data
340
+ ) -> None:
341
+ """Backwards compatibility test that verifies a checkpoint with no channel_values key can be retrieved without throwing an error."""
342
+ with _saver(saver_name) as saver:
343
+ config = {
344
+ "configurable": {
345
+ "thread_id": "thread-2",
346
+ "checkpoint_ns": "",
347
+ "__super_private_key": "super_private_value",
348
+ },
349
+ }
350
+ chkpnt: Checkpoint = create_checkpoint(empty_checkpoint(), {}, 1)
351
+ saver.put(config, chkpnt, {}, {})
352
+
353
+ load_checkpoint_tuple = saver._load_checkpoint_tuple
354
+
355
+ def patched_load_checkpoint_tuple(value):
356
+ value["checkpoint"].pop("channel_values", None)
357
+ return load_checkpoint_tuple(value)
358
+
359
+ monkeypatch.setattr(
360
+ saver, "_load_checkpoint_tuple", patched_load_checkpoint_tuple
361
+ )
362
+
363
+ checkpoint = saver.get_tuple(config)
364
+ assert checkpoint.checkpoint["channel_values"] == {}