langgraph-checkpoint-postgres 3.1.0a3__tar.gz → 3.1.0a4__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-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/PKG-INFO +2 -2
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/checkpoint/postgres/__init__.py +100 -45
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/checkpoint/postgres/aio.py +82 -44
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/checkpoint/postgres/base.py +243 -103
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/pyproject.toml +2 -2
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/uv.lock +86 -69
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/.gitignore +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/LICENSE +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/Makefile +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/README.md +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/checkpoint/postgres/_ainternal.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/checkpoint/postgres/_internal.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/checkpoint/postgres/py.typed +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/checkpoint/postgres/shallow.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/store/postgres/__init__.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/store/postgres/aio.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/store/postgres/base.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/langgraph/store/postgres/py.typed +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/__init__.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/compose-postgres.yml +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/conftest.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/embed_test_utils.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/test_async.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/test_async_store.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/test_store.py +0 -0
- {langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/test_sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-checkpoint-postgres
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.0a4
|
|
4
4
|
Summary: Library with a Postgres implementation of LangGraph checkpoint saver.
|
|
5
5
|
Project-URL: Source, https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-postgres
|
|
6
6
|
Project-URL: Twitter, https://x.com/langchain_oss
|
|
@@ -9,7 +9,7 @@ Project-URL: Reddit, https://www.reddit.com/r/LangChain/
|
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
|
-
Requires-Dist: langgraph-checkpoint<5.0.0,>=4.1.
|
|
12
|
+
Requires-Dist: langgraph-checkpoint<5.0.0,>=4.1.0a4
|
|
13
13
|
Requires-Dist: orjson>=3.11.5
|
|
14
14
|
Requires-Dist: psycopg-pool>=3.2.0
|
|
15
15
|
Requires-Dist: psycopg>=3.2.0
|
|
@@ -2,19 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import threading
|
|
4
4
|
from collections import defaultdict
|
|
5
|
-
from collections.abc import Iterator, Sequence
|
|
5
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
6
6
|
from contextlib import contextmanager
|
|
7
7
|
from typing import Any, cast
|
|
8
8
|
|
|
9
9
|
from langchain_core.runnables import RunnableConfig
|
|
10
10
|
from langgraph.checkpoint.base import (
|
|
11
|
-
DELTA_SENTINEL,
|
|
12
11
|
WRITES_IDX_MAP,
|
|
13
12
|
ChannelVersions,
|
|
14
13
|
Checkpoint,
|
|
15
14
|
CheckpointMetadata,
|
|
16
15
|
CheckpointTuple,
|
|
17
|
-
|
|
16
|
+
DeltaChannelHistory,
|
|
18
17
|
get_checkpoint_id,
|
|
19
18
|
get_serializable_checkpoint_metadata,
|
|
20
19
|
)
|
|
@@ -27,10 +26,10 @@ from psycopg_pool import ConnectionPool
|
|
|
27
26
|
|
|
28
27
|
from langgraph.checkpoint.postgres import _internal
|
|
29
28
|
from langgraph.checkpoint.postgres.base import (
|
|
30
|
-
|
|
31
|
-
SELECT_DELTA_STAGE2_SQL,
|
|
29
|
+
_DELTA_PAGE_SIZE,
|
|
32
30
|
BasePostgresSaver,
|
|
33
|
-
|
|
31
|
+
_build_delta_stage1_sql,
|
|
32
|
+
_build_delta_stage2_sql,
|
|
34
33
|
_DeltaStage2Row,
|
|
35
34
|
)
|
|
36
35
|
from langgraph.checkpoint.postgres.shallow import ShallowPostgresSaver
|
|
@@ -311,9 +310,7 @@ class PostgresSaver(BasePostgresSaver):
|
|
|
311
310
|
# others are stored in blobs table
|
|
312
311
|
blob_values = {}
|
|
313
312
|
for k, v in checkpoint["channel_values"].items():
|
|
314
|
-
if v
|
|
315
|
-
copy["channel_values"].pop(k)
|
|
316
|
-
elif isinstance(v, _DeltaSnapshot):
|
|
313
|
+
if isinstance(v, _DeltaSnapshot):
|
|
317
314
|
blob_values[k] = copy["channel_values"].pop(k)
|
|
318
315
|
copy["channel_values"][k] = True
|
|
319
316
|
elif v is None or isinstance(v, (str, int, float, bool)):
|
|
@@ -444,53 +441,111 @@ class PostgresSaver(BasePostgresSaver):
|
|
|
444
441
|
with conn.cursor(binary=True, row_factory=dict_row) as cur:
|
|
445
442
|
yield cur
|
|
446
443
|
|
|
447
|
-
def
|
|
448
|
-
self, config: RunnableConfig,
|
|
449
|
-
) ->
|
|
450
|
-
"""Fast-path override of `BaseCheckpointSaver.
|
|
451
|
-
|
|
452
|
-
Two-stage query
|
|
453
|
-
|
|
454
|
-
|
|
444
|
+
def get_delta_channel_history(
|
|
445
|
+
self, *, config: RunnableConfig, channels: Sequence[str]
|
|
446
|
+
) -> Mapping[str, DeltaChannelHistory]:
|
|
447
|
+
"""Fast-path override of `BaseCheckpointSaver.get_delta_channel_history`.
|
|
448
|
+
|
|
449
|
+
Two-stage query, both stages cover ALL requested channels:
|
|
450
|
+
|
|
451
|
+
* Stage 1 (paged): dynamic SELECT over `checkpoints` with K parallel
|
|
452
|
+
JSONB key lookups (one column pair per channel) — no subquery, no
|
|
453
|
+
aggregation. Pages newest-first by `checkpoint_id` with a cursor;
|
|
454
|
+
page size is `_DELTA_PAGE_SIZE`. Stops paging when every channel
|
|
455
|
+
has found its seed or the chain is exhausted.
|
|
456
|
+
|
|
457
|
+
* Stage 2 (per-channel UNION ALL): one branch per channel reading
|
|
458
|
+
`checkpoint_writes` filtered to that channel's specific
|
|
459
|
+
`chain_cids`, plus one branch per channel that has a seed reading
|
|
460
|
+
`checkpoint_blobs` for that channel + version. Avoids the
|
|
461
|
+
over-fetch of a single `channel = ANY(channels)` filter when
|
|
462
|
+
channels have different chain depths.
|
|
455
463
|
"""
|
|
464
|
+
if not channels:
|
|
465
|
+
return {}
|
|
466
|
+
channels = list(channels)
|
|
456
467
|
thread_id = config["configurable"]["thread_id"]
|
|
457
468
|
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
|
|
458
469
|
checkpoint_id = get_checkpoint_id(config)
|
|
459
470
|
if checkpoint_id is None:
|
|
460
471
|
target = self.get_tuple(config)
|
|
461
472
|
if target is None:
|
|
462
|
-
return
|
|
473
|
+
return {ch: {"writes": []} for ch in channels}
|
|
463
474
|
checkpoint_id = target.config["configurable"]["checkpoint_id"]
|
|
464
475
|
|
|
476
|
+
# Stage 1: paged K-JSONB-lookup scan, walking the parent chain in
|
|
477
|
+
# Python after each page. Stops as soon as every channel has its seed.
|
|
478
|
+
stage1_sql = _build_delta_stage1_sql(channels, paged=True)
|
|
479
|
+
parent_of: dict[str, str | None] = {}
|
|
480
|
+
ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels]
|
|
481
|
+
hs_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels]
|
|
482
|
+
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
|
|
483
|
+
seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels}
|
|
484
|
+
walk_cursor_by_ch: dict[str, str | None] = {}
|
|
485
|
+
seeded: set[str] = set()
|
|
486
|
+
cursor: str | None = None
|
|
487
|
+
|
|
465
488
|
with self._cursor() as cur:
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
489
|
+
while True:
|
|
490
|
+
stage1_params: list[Any] = []
|
|
491
|
+
for ch in channels:
|
|
492
|
+
stage1_params.extend([ch, ch])
|
|
493
|
+
stage1_params.extend(
|
|
494
|
+
[thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE]
|
|
495
|
+
)
|
|
496
|
+
cur.execute(stage1_sql, stage1_params)
|
|
497
|
+
page = cur.fetchall()
|
|
498
|
+
if not page:
|
|
499
|
+
break
|
|
500
|
+
oldest = self._ingest_stage1_page(
|
|
501
|
+
cast("list[Mapping[str, Any]]", page),
|
|
502
|
+
channels,
|
|
503
|
+
parent_of,
|
|
504
|
+
ver_by_i_by_cid,
|
|
505
|
+
hs_by_i_by_cid,
|
|
506
|
+
)
|
|
507
|
+
self._try_advance_walks(
|
|
508
|
+
checkpoint_id,
|
|
509
|
+
channels,
|
|
510
|
+
parent_of,
|
|
511
|
+
ver_by_i_by_cid,
|
|
512
|
+
hs_by_i_by_cid,
|
|
513
|
+
chain_by_ch,
|
|
514
|
+
seed_ver_by_ch,
|
|
515
|
+
walk_cursor_by_ch,
|
|
516
|
+
seeded,
|
|
517
|
+
)
|
|
518
|
+
# Stop if every channel is seeded, or the page was short
|
|
519
|
+
# (chain exhausted — no more rows to fetch).
|
|
520
|
+
if len(seeded) == len(channels) or len(page) < _DELTA_PAGE_SIZE:
|
|
521
|
+
break
|
|
522
|
+
cursor = oldest
|
|
523
|
+
|
|
524
|
+
# Stage 2: per-channel UNION ALL — one writes branch per channel
|
|
525
|
+
# with non-empty chain, plus one blob branch per seeded channel.
|
|
526
|
+
channels_with_chain = [ch for ch in channels if chain_by_ch[ch]]
|
|
527
|
+
channels_with_seed = [ch for ch in channels if seed_ver_by_ch[ch] is not None]
|
|
528
|
+
stage2_sql = _build_delta_stage2_sql(
|
|
529
|
+
channels_with_chain=channels_with_chain,
|
|
530
|
+
channels_with_seed=channels_with_seed,
|
|
473
531
|
)
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
(
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
channel=channel,
|
|
492
|
-
chain_cids=chain_cids,
|
|
493
|
-
seed_version=seed_version,
|
|
532
|
+
|
|
533
|
+
if stage2_sql:
|
|
534
|
+
stage2_params: list[Any] = []
|
|
535
|
+
for ch in channels_with_chain:
|
|
536
|
+
stage2_params.extend([thread_id, checkpoint_ns, ch, chain_by_ch[ch]])
|
|
537
|
+
for ch in channels_with_seed:
|
|
538
|
+
stage2_params.extend([thread_id, checkpoint_ns, ch, seed_ver_by_ch[ch]])
|
|
539
|
+
with self._cursor() as cur:
|
|
540
|
+
cur.execute(stage2_sql, stage2_params)
|
|
541
|
+
stage2_rows = cur.fetchall()
|
|
542
|
+
else:
|
|
543
|
+
stage2_rows = []
|
|
544
|
+
|
|
545
|
+
return self._build_delta_channels_writes_history(
|
|
546
|
+
channels=channels,
|
|
547
|
+
chain_by_ch=chain_by_ch,
|
|
548
|
+
seed_ver_by_ch=seed_ver_by_ch,
|
|
494
549
|
stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows),
|
|
495
550
|
)
|
|
496
551
|
|
|
@@ -2,19 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
from collections import defaultdict
|
|
5
|
-
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
5
|
+
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
|
|
6
6
|
from contextlib import asynccontextmanager
|
|
7
7
|
from typing import Any, cast
|
|
8
8
|
|
|
9
9
|
from langchain_core.runnables import RunnableConfig
|
|
10
10
|
from langgraph.checkpoint.base import (
|
|
11
|
-
DELTA_SENTINEL,
|
|
12
11
|
WRITES_IDX_MAP,
|
|
13
12
|
ChannelVersions,
|
|
14
13
|
Checkpoint,
|
|
15
14
|
CheckpointMetadata,
|
|
16
15
|
CheckpointTuple,
|
|
17
|
-
|
|
16
|
+
DeltaChannelHistory,
|
|
18
17
|
get_checkpoint_id,
|
|
19
18
|
get_serializable_checkpoint_metadata,
|
|
20
19
|
)
|
|
@@ -27,10 +26,10 @@ from psycopg_pool import AsyncConnectionPool
|
|
|
27
26
|
|
|
28
27
|
from langgraph.checkpoint.postgres import _ainternal
|
|
29
28
|
from langgraph.checkpoint.postgres.base import (
|
|
30
|
-
|
|
31
|
-
SELECT_DELTA_STAGE2_SQL,
|
|
29
|
+
_DELTA_PAGE_SIZE,
|
|
32
30
|
BasePostgresSaver,
|
|
33
|
-
|
|
31
|
+
_build_delta_stage1_sql,
|
|
32
|
+
_build_delta_stage2_sql,
|
|
34
33
|
_DeltaStage2Row,
|
|
35
34
|
)
|
|
36
35
|
from langgraph.checkpoint.postgres.shallow import AsyncShallowPostgresSaver
|
|
@@ -270,9 +269,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
270
269
|
# others are stored in blobs table
|
|
271
270
|
blob_values = {}
|
|
272
271
|
for k, v in checkpoint["channel_values"].items():
|
|
273
|
-
if v
|
|
274
|
-
copy["channel_values"].pop(k)
|
|
275
|
-
elif isinstance(v, _DeltaSnapshot):
|
|
272
|
+
if isinstance(v, _DeltaSnapshot):
|
|
276
273
|
blob_values[k] = copy["channel_values"].pop(k)
|
|
277
274
|
copy["channel_values"][k] = True
|
|
278
275
|
elif v is None or isinstance(v, (str, int, float, bool)):
|
|
@@ -405,53 +402,94 @@ class AsyncPostgresSaver(BasePostgresSaver):
|
|
|
405
402
|
async with conn.cursor(binary=True, row_factory=dict_row) as cur:
|
|
406
403
|
yield cur
|
|
407
404
|
|
|
408
|
-
async def
|
|
409
|
-
self, config: RunnableConfig,
|
|
410
|
-
) ->
|
|
411
|
-
"""Fast-path override of `BaseCheckpointSaver.
|
|
405
|
+
async def aget_delta_channel_history(
|
|
406
|
+
self, *, config: RunnableConfig, channels: Sequence[str]
|
|
407
|
+
) -> Mapping[str, DeltaChannelHistory]:
|
|
408
|
+
"""Fast-path override of `BaseCheckpointSaver.aget_delta_channel_history`.
|
|
412
409
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
See `PostgresSaver.get_delta_channel_history` for design notes; this is
|
|
411
|
+
the async equivalent with internal stage-1 paging and per-channel
|
|
412
|
+
UNION ALL stage-2.
|
|
416
413
|
"""
|
|
414
|
+
if not channels:
|
|
415
|
+
return {}
|
|
416
|
+
channels = list(channels)
|
|
417
417
|
thread_id = config["configurable"]["thread_id"]
|
|
418
418
|
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
|
|
419
419
|
checkpoint_id = get_checkpoint_id(config)
|
|
420
420
|
if checkpoint_id is None:
|
|
421
421
|
target = await self.aget_tuple(config)
|
|
422
422
|
if target is None:
|
|
423
|
-
return
|
|
423
|
+
return {ch: {"writes": []} for ch in channels}
|
|
424
424
|
checkpoint_id = target.config["configurable"]["checkpoint_id"]
|
|
425
425
|
|
|
426
|
+
stage1_sql = _build_delta_stage1_sql(channels, paged=True)
|
|
427
|
+
parent_of: dict[str, str | None] = {}
|
|
428
|
+
ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels]
|
|
429
|
+
hs_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels]
|
|
430
|
+
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
|
|
431
|
+
seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels}
|
|
432
|
+
walk_cursor_by_ch: dict[str, str | None] = {}
|
|
433
|
+
seeded: set[str] = set()
|
|
434
|
+
cursor: str | None = None
|
|
435
|
+
|
|
426
436
|
async with self._cursor() as cur:
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
437
|
+
while True:
|
|
438
|
+
stage1_params: list[Any] = []
|
|
439
|
+
for ch in channels:
|
|
440
|
+
stage1_params.extend([ch, ch])
|
|
441
|
+
stage1_params.extend(
|
|
442
|
+
[thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE]
|
|
443
|
+
)
|
|
444
|
+
await cur.execute(stage1_sql, stage1_params)
|
|
445
|
+
page = await cur.fetchall()
|
|
446
|
+
if not page:
|
|
447
|
+
break
|
|
448
|
+
oldest = self._ingest_stage1_page(
|
|
449
|
+
cast("list[Mapping[str, Any]]", page),
|
|
450
|
+
channels,
|
|
451
|
+
parent_of,
|
|
452
|
+
ver_by_i_by_cid,
|
|
453
|
+
hs_by_i_by_cid,
|
|
454
|
+
)
|
|
455
|
+
self._try_advance_walks(
|
|
456
|
+
checkpoint_id,
|
|
457
|
+
channels,
|
|
458
|
+
parent_of,
|
|
459
|
+
ver_by_i_by_cid,
|
|
460
|
+
hs_by_i_by_cid,
|
|
461
|
+
chain_by_ch,
|
|
462
|
+
seed_ver_by_ch,
|
|
463
|
+
walk_cursor_by_ch,
|
|
464
|
+
seeded,
|
|
465
|
+
)
|
|
466
|
+
if len(seeded) == len(channels) or len(page) < _DELTA_PAGE_SIZE:
|
|
467
|
+
break
|
|
468
|
+
cursor = oldest
|
|
469
|
+
|
|
470
|
+
channels_with_chain = [ch for ch in channels if chain_by_ch[ch]]
|
|
471
|
+
channels_with_seed = [ch for ch in channels if seed_ver_by_ch[ch] is not None]
|
|
472
|
+
stage2_sql = _build_delta_stage2_sql(
|
|
473
|
+
channels_with_chain=channels_with_chain,
|
|
474
|
+
channels_with_seed=channels_with_seed,
|
|
434
475
|
)
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
channel=channel,
|
|
453
|
-
chain_cids=chain_cids,
|
|
454
|
-
seed_version=seed_version,
|
|
476
|
+
|
|
477
|
+
if stage2_sql:
|
|
478
|
+
stage2_params: list[Any] = []
|
|
479
|
+
for ch in channels_with_chain:
|
|
480
|
+
stage2_params.extend([thread_id, checkpoint_ns, ch, chain_by_ch[ch]])
|
|
481
|
+
for ch in channels_with_seed:
|
|
482
|
+
stage2_params.extend([thread_id, checkpoint_ns, ch, seed_ver_by_ch[ch]])
|
|
483
|
+
async with self._cursor() as cur:
|
|
484
|
+
await cur.execute(stage2_sql, stage2_params)
|
|
485
|
+
stage2_rows = await cur.fetchall()
|
|
486
|
+
else:
|
|
487
|
+
stage2_rows = []
|
|
488
|
+
|
|
489
|
+
return self._build_delta_channels_writes_history(
|
|
490
|
+
channels=channels,
|
|
491
|
+
chain_by_ch=chain_by_ch,
|
|
492
|
+
seed_ver_by_ch=seed_ver_by_ch,
|
|
455
493
|
stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows),
|
|
456
494
|
)
|
|
457
495
|
|
|
@@ -2,23 +2,26 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import random
|
|
4
4
|
import warnings
|
|
5
|
-
from collections.abc import Sequence
|
|
5
|
+
from collections.abc import Mapping, Sequence
|
|
6
6
|
from importlib.metadata import version as get_version
|
|
7
7
|
from typing import Any, TypedDict, cast
|
|
8
8
|
|
|
9
9
|
from langchain_core.runnables import RunnableConfig
|
|
10
10
|
from langgraph.checkpoint.base import (
|
|
11
|
-
DELTA_SENTINEL,
|
|
12
11
|
WRITES_IDX_MAP,
|
|
13
12
|
BaseCheckpointSaver,
|
|
14
13
|
ChannelVersions,
|
|
14
|
+
DeltaChannelHistory,
|
|
15
15
|
PendingWrite,
|
|
16
|
-
_ChannelWritesHistory,
|
|
17
16
|
get_checkpoint_id,
|
|
18
17
|
)
|
|
19
18
|
from langgraph.checkpoint.serde.types import TASKS
|
|
20
19
|
from psycopg.types.json import Jsonb
|
|
21
20
|
|
|
21
|
+
# Page size for stage-1 paged scan in `get_delta_channel_history`. Internal
|
|
22
|
+
# constant — exposing this as a kwarg is left as a follow-up.
|
|
23
|
+
_DELTA_PAGE_SIZE = 1024
|
|
24
|
+
|
|
22
25
|
MetadataInput = dict[str, Any] | None
|
|
23
26
|
|
|
24
27
|
try:
|
|
@@ -157,10 +160,11 @@ INSERT_CHECKPOINT_WRITES_SQL = """
|
|
|
157
160
|
|
|
158
161
|
|
|
159
162
|
class _DeltaStage2Row(TypedDict, total=False):
|
|
160
|
-
"""One row from `
|
|
163
|
+
"""One row from `_build_delta_stage2_sql` (a UNION ALL of writes and blobs)."""
|
|
161
164
|
|
|
162
165
|
_kind: str # "w" or "b"
|
|
163
166
|
checkpoint_id: str | None # "w" rows only
|
|
167
|
+
channel: str | None # set on both "w" and "b" rows
|
|
164
168
|
type: str | None
|
|
165
169
|
blob: bytes | None
|
|
166
170
|
task_id: str | None # "w" rows only
|
|
@@ -168,48 +172,126 @@ class _DeltaStage2Row(TypedDict, total=False):
|
|
|
168
172
|
version: str | None # "b" rows only
|
|
169
173
|
|
|
170
174
|
|
|
171
|
-
#
|
|
172
|
-
# metadata (no blob bytes) to walk the parent chain and locate the
|
|
173
|
-
# nearest snapshot marker. Stage 2 fetches only the chain-limited
|
|
174
|
-
# writes and the single seed snapshot blob.
|
|
175
|
+
# Multi-channel two-stage DeltaChannel reconstruction.
|
|
175
176
|
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
177
|
+
# Stage 1 scans checkpoint metadata (no blob bytes) and emits one row per
|
|
178
|
+
# checkpoint with K parallel JSONB key lookups (one column pair per
|
|
179
|
+
# requested delta channel: ver_i / hs_i). No subqueries, no aggregation.
|
|
180
|
+
# Python walks the parent chain once across all channels.
|
|
181
|
+
#
|
|
182
|
+
# Stage 2 fetches all writes and the seed blobs for ALL channels in a
|
|
183
|
+
# single roundtrip via `channel = ANY(%s)` and chain/seed-version
|
|
184
|
+
# filtering.
|
|
185
|
+
#
|
|
186
|
+
# Empirical comparison vs an alternative "ship full channel_versions /
|
|
187
|
+
# channel_values JSONB and let Python pick" form (1000 checkpoints,
|
|
188
|
+
# 8 total channels in graph, 3 delta channels requested):
|
|
189
|
+
#
|
|
190
|
+
# Postgres execution: A=0.24ms vs B=0.38ms (both negligible)
|
|
191
|
+
# End-to-end latency: A=6.83ms vs B=2.28ms (B is 3.0x faster)
|
|
192
|
+
# Wire payload: A=836KB vs B=330KB (61% smaller)
|
|
193
|
+
# Buffer hits: identical (167 blocks)
|
|
194
|
+
#
|
|
195
|
+
# B (this dynamic-columns design) wins because it avoids JSONB
|
|
196
|
+
# serialization on the wire and JSONB-to-dict deserialization in
|
|
197
|
+
# psycopg. Even at K=8 (8 delta channels = 16 dynamic columns), B
|
|
198
|
+
# still beats A end-to-end (4.2ms vs 6.8ms).
|
|
189
199
|
|
|
190
|
-
SELECT_DELTA_STAGE2_SQL = """
|
|
191
|
-
SELECT 'w'::text AS _kind,
|
|
192
|
-
checkpoint_id,
|
|
193
|
-
type, blob, task_id, idx, NULL::text AS version
|
|
194
|
-
FROM checkpoint_writes
|
|
195
|
-
WHERE thread_id = %s AND checkpoint_ns = %s AND channel = %s
|
|
196
|
-
AND checkpoint_id = ANY(%s)
|
|
197
|
-
UNION ALL
|
|
198
|
-
SELECT 'b', NULL,
|
|
199
|
-
type, blob, NULL, NULL, version
|
|
200
|
-
FROM checkpoint_blobs
|
|
201
|
-
WHERE thread_id = %s AND checkpoint_ns = %s AND channel = %s
|
|
202
|
-
AND version = ANY(%s)
|
|
203
|
-
"""
|
|
204
200
|
|
|
201
|
+
def _build_delta_stage1_sql(channels: Sequence[str], *, paged: bool) -> str:
|
|
202
|
+
"""Build stage 1 SQL with 2K parallel JSONB key lookups.
|
|
203
|
+
|
|
204
|
+
For channels=["messages", "files"] (with `paged=True`) the result is::
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
SELECT checkpoint_id, parent_checkpoint_id,
|
|
207
|
+
checkpoint -> 'channel_versions' ->> %s AS ver_0,
|
|
208
|
+
(checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_0,
|
|
209
|
+
checkpoint -> 'channel_versions' ->> %s AS ver_1,
|
|
210
|
+
(checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_1
|
|
211
|
+
FROM checkpoints
|
|
212
|
+
WHERE thread_id = %s AND checkpoint_ns = %s
|
|
213
|
+
AND (%s::text IS NULL OR checkpoint_id < %s)
|
|
214
|
+
ORDER BY checkpoint_id DESC
|
|
215
|
+
LIMIT %s
|
|
208
216
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
217
|
+
Channel names are passed as `%s` parameters (safe from SQL injection).
|
|
218
|
+
Only the column aliases `ver_i` / `hs_i` are interpolated into the
|
|
219
|
+
SQL string (i is bounded by len(channels) and uses safe identifiers).
|
|
220
|
+
|
|
221
|
+
Caller must extend params with `[ch_0, ch_0, ch_1, ch_1, ...,
|
|
222
|
+
thread_id, ns, cursor, cursor, page_size]` when `paged=True`.
|
|
223
|
+
|
|
224
|
+
When `paged=False`, the WHERE has no cursor predicate and there's no
|
|
225
|
+
LIMIT/ORDER BY — kept as a non-public helper for tests/diagnostics.
|
|
226
|
+
"""
|
|
227
|
+
cols = []
|
|
228
|
+
for i in range(len(channels)):
|
|
229
|
+
cols.append(
|
|
230
|
+
f"checkpoint -> 'channel_versions' ->> %s AS ver_{i}, "
|
|
231
|
+
f"(checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_{i}"
|
|
232
|
+
)
|
|
233
|
+
sql = (
|
|
234
|
+
"SELECT checkpoint_id, parent_checkpoint_id, "
|
|
235
|
+
+ ", ".join(cols)
|
|
236
|
+
+ " FROM checkpoints WHERE thread_id = %s AND checkpoint_ns = %s"
|
|
237
|
+
)
|
|
238
|
+
if paged:
|
|
239
|
+
sql += (
|
|
240
|
+
" AND (%s::text IS NULL OR checkpoint_id < %s)"
|
|
241
|
+
" ORDER BY checkpoint_id DESC LIMIT %s"
|
|
242
|
+
)
|
|
243
|
+
return sql
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _build_delta_stage2_sql(
|
|
247
|
+
*,
|
|
248
|
+
channels_with_chain: Sequence[str],
|
|
249
|
+
channels_with_seed: Sequence[str],
|
|
250
|
+
) -> str:
|
|
251
|
+
"""Build stage 2 SQL as a per-channel UNION ALL.
|
|
252
|
+
|
|
253
|
+
For each channel with a non-empty chain, emit one branch reading
|
|
254
|
+
`checkpoint_writes` for that specific channel + chain_cids. For each
|
|
255
|
+
channel with a seed_version, emit one branch reading `checkpoint_blobs`
|
|
256
|
+
for that channel + version. This avoids the over-fetch of the prior
|
|
257
|
+
`channel = ANY(channels) AND checkpoint_id = ANY(union)` form when
|
|
258
|
+
channels have different chain depths.
|
|
259
|
+
|
|
260
|
+
The caller must pass parameters in matching order:
|
|
261
|
+
|
|
262
|
+
for ch in channels_with_chain:
|
|
263
|
+
params += [thread_id, checkpoint_ns, ch, chain_cids[ch]]
|
|
264
|
+
for ch in channels_with_seed:
|
|
265
|
+
params += [thread_id, checkpoint_ns, ch, seed_version[ch]]
|
|
266
|
+
|
|
267
|
+
Returns an empty SQL string if both channel lists are empty (caller
|
|
268
|
+
must skip executing in that case).
|
|
269
|
+
"""
|
|
270
|
+
branches: list[str] = []
|
|
271
|
+
for _ in channels_with_chain:
|
|
272
|
+
branches.append(
|
|
273
|
+
"SELECT 'w'::text AS _kind, "
|
|
274
|
+
"checkpoint_id, channel, "
|
|
275
|
+
"type, blob, task_id, idx, NULL::text AS version "
|
|
276
|
+
"FROM checkpoint_writes "
|
|
277
|
+
"WHERE thread_id = %s AND checkpoint_ns = %s AND channel = %s "
|
|
278
|
+
"AND checkpoint_id = ANY(%s)"
|
|
279
|
+
)
|
|
280
|
+
for _ in channels_with_seed:
|
|
281
|
+
branches.append(
|
|
282
|
+
"SELECT 'b'::text, NULL, channel, "
|
|
283
|
+
"type, blob, NULL, NULL, version "
|
|
284
|
+
"FROM checkpoint_blobs "
|
|
285
|
+
"WHERE thread_id = %s AND checkpoint_ns = %s AND channel = %s "
|
|
286
|
+
"AND version = %s"
|
|
287
|
+
)
|
|
288
|
+
return " UNION ALL ".join(branches)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# Stage 1 rows are dynamic-shape dicts: {checkpoint_id, parent_checkpoint_id,
|
|
292
|
+
# ver_0, hs_0, ver_1, hs_1, ...}. Walking is parameterized by the channel
|
|
293
|
+
# list to map indices back to channel names — no static TypedDict here.
|
|
294
|
+
# `dict[str, Any]` is the practical signature.
|
|
213
295
|
|
|
214
296
|
|
|
215
297
|
class BasePostgresSaver(BaseCheckpointSaver[str]):
|
|
@@ -255,86 +337,144 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
|
|
|
255
337
|
}
|
|
256
338
|
|
|
257
339
|
@staticmethod
|
|
258
|
-
def
|
|
259
|
-
stage1_rows: Sequence[
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
340
|
+
def _ingest_stage1_page(
|
|
341
|
+
stage1_rows: Sequence[Mapping[str, Any]],
|
|
342
|
+
channels: Sequence[str],
|
|
343
|
+
parent_of: dict[str, str | None],
|
|
344
|
+
ver_by_i_by_cid: list[dict[str, str | None]],
|
|
345
|
+
hs_by_i_by_cid: list[dict[str, bool]],
|
|
346
|
+
) -> str | None:
|
|
347
|
+
"""Fold one stage-1 page into the running walk-state mappings.
|
|
348
|
+
|
|
349
|
+
Returns the oldest checkpoint_id seen on this page (smallest, since
|
|
350
|
+
pages come back DESC). Caller uses it as the cursor for the next
|
|
351
|
+
page (`AND checkpoint_id < cursor`).
|
|
269
352
|
"""
|
|
270
|
-
|
|
271
|
-
ver_of: dict[str, str | None] = {}
|
|
272
|
-
snapshot_of: dict[str, bool] = {}
|
|
353
|
+
oldest: str | None = None
|
|
273
354
|
for r in stage1_rows:
|
|
274
|
-
cid = r["checkpoint_id"]
|
|
275
|
-
parent_of[cid] = r["parent_checkpoint_id"]
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
355
|
+
cid = cast(str, r["checkpoint_id"])
|
|
356
|
+
parent_of[cid] = cast("str | None", r["parent_checkpoint_id"])
|
|
357
|
+
for i in range(len(channels)):
|
|
358
|
+
ver_by_i_by_cid[i][cid] = cast("str | None", r.get(f"ver_{i}"))
|
|
359
|
+
hs_by_i_by_cid[i][cid] = bool(r.get(f"hs_{i}"))
|
|
360
|
+
# Rows are DESC; the last one is the smallest cid in the page.
|
|
361
|
+
oldest = cid
|
|
362
|
+
return oldest
|
|
363
|
+
|
|
364
|
+
@staticmethod
|
|
365
|
+
def _try_advance_walks(
|
|
366
|
+
target_id: str,
|
|
367
|
+
channels: Sequence[str],
|
|
368
|
+
parent_of: Mapping[str, str | None],
|
|
369
|
+
ver_by_i_by_cid: Sequence[Mapping[str, str | None]],
|
|
370
|
+
hs_by_i_by_cid: Sequence[Mapping[str, bool]],
|
|
371
|
+
chain_by_ch: dict[str, list[str]],
|
|
372
|
+
seed_ver_by_ch: dict[str, str | None],
|
|
373
|
+
walk_cursor_by_ch: dict[str, str | None],
|
|
374
|
+
seeded: set[str],
|
|
375
|
+
) -> None:
|
|
376
|
+
"""Advance each not-yet-seeded channel's walk as far as possible.
|
|
377
|
+
|
|
378
|
+
Uses the partial `parent_of` map accumulated so far. A walk stops
|
|
379
|
+
either because:
|
|
380
|
+
(a) it found a snapshot for its channel (channel becomes seeded),
|
|
381
|
+
(b) it reached a real root (parent_of[cid] is None — fully
|
|
382
|
+
materialized at this point), or
|
|
383
|
+
(c) the next ancestor cid isn't in `parent_of` yet (waiting for
|
|
384
|
+
a later page; the cursor stays put).
|
|
385
|
+
|
|
386
|
+
Mutates `chain_by_ch`, `seed_ver_by_ch`, `walk_cursor_by_ch`, and
|
|
387
|
+
`seeded` in place.
|
|
388
|
+
"""
|
|
389
|
+
for i, ch in enumerate(channels):
|
|
390
|
+
if ch in seeded:
|
|
391
|
+
continue
|
|
392
|
+
# First-time entry: cursor starts at the target's parent.
|
|
393
|
+
if ch not in walk_cursor_by_ch:
|
|
394
|
+
walk_cursor_by_ch[ch] = parent_of.get(target_id)
|
|
395
|
+
cur_cid = walk_cursor_by_ch[ch]
|
|
396
|
+
ch_chain = chain_by_ch[ch]
|
|
397
|
+
hs_i = hs_by_i_by_cid[i]
|
|
398
|
+
ver_i = ver_by_i_by_cid[i]
|
|
399
|
+
while cur_cid is not None:
|
|
400
|
+
if cur_cid not in parent_of:
|
|
401
|
+
# Need more pages to continue this walk.
|
|
402
|
+
break
|
|
403
|
+
ch_chain.append(cur_cid)
|
|
404
|
+
if hs_i.get(cur_cid, False):
|
|
405
|
+
seed_ver_by_ch[ch] = ver_i.get(cur_cid)
|
|
406
|
+
seeded.add(ch)
|
|
407
|
+
cur_cid = None
|
|
408
|
+
break
|
|
409
|
+
cur_cid = parent_of[cur_cid]
|
|
410
|
+
walk_cursor_by_ch[ch] = cur_cid
|
|
411
|
+
|
|
412
|
+
def _build_delta_channels_writes_history(
|
|
291
413
|
self,
|
|
292
414
|
*,
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
415
|
+
channels: Sequence[str],
|
|
416
|
+
chain_by_ch: Mapping[str, list[str]],
|
|
417
|
+
seed_ver_by_ch: Mapping[str, str | None],
|
|
296
418
|
stage2_rows: Sequence[_DeltaStage2Row],
|
|
297
|
-
) ->
|
|
298
|
-
"""
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
seed
|
|
419
|
+
) -> dict[str, DeltaChannelHistory]:
|
|
420
|
+
"""Demux stage 2 rows per channel; produce per-channel histories.
|
|
421
|
+
|
|
422
|
+
stage2_rows carry `channel` on every row. We build per-channel
|
|
423
|
+
`writes_by_cid` and per-channel `seed_blob` dicts, then assemble
|
|
424
|
+
a `DeltaChannelHistory` per requested channel. The `seed` key is omitted
|
|
425
|
+
when the walk reached root with no snapshot found, or when the
|
|
426
|
+
seed blob is sentinel "empty" — in both cases the consumer treats
|
|
427
|
+
absence as "start empty".
|
|
303
428
|
"""
|
|
304
|
-
|
|
305
|
-
|
|
429
|
+
# writes_by_ch_by_cid[channel][cid] = list of (type, blob, task_id, idx)
|
|
430
|
+
writes_by_ch_by_cid: dict[str, dict[str, list[tuple[str, bytes, str, int]]]] = {
|
|
431
|
+
ch: {} for ch in channels
|
|
432
|
+
}
|
|
433
|
+
# seed_blob_by_ver[(channel, version)] = (type, blob)
|
|
434
|
+
seed_blob_by_ver: dict[tuple[str, str], tuple[str, bytes]] = {}
|
|
306
435
|
|
|
307
436
|
for r in stage2_rows:
|
|
437
|
+
ch = cast(str, r["channel"])
|
|
308
438
|
kind = r["_kind"]
|
|
309
439
|
if kind == "w":
|
|
310
440
|
cid = cast(str, r["checkpoint_id"])
|
|
311
|
-
|
|
441
|
+
writes_by_ch_by_cid.setdefault(ch, {}).setdefault(cid, []).append(
|
|
312
442
|
cast(
|
|
313
443
|
"tuple[str, bytes, str, int]",
|
|
314
444
|
(r["type"], r["blob"], r["task_id"], r["idx"]),
|
|
315
445
|
)
|
|
316
446
|
)
|
|
317
447
|
else: # kind == "b"
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if not chain_cids:
|
|
324
|
-
return _ChannelWritesHistory(seed=DELTA_SENTINEL, writes=[])
|
|
325
|
-
|
|
326
|
-
collected: list[PendingWrite] = []
|
|
327
|
-
for cid in chain_cids:
|
|
328
|
-
for type_tag, write_blob, task_id, _idx in writes_by_cid.get(cid, []):
|
|
329
|
-
val = self.serde.loads_typed((type_tag, write_blob))
|
|
330
|
-
collected.append((task_id, channel, val))
|
|
331
|
-
|
|
332
|
-
seed: Any = DELTA_SENTINEL
|
|
333
|
-
if seed_blob is not None and seed_blob[0] != "empty":
|
|
334
|
-
seed = self.serde.loads_typed(seed_blob)
|
|
448
|
+
ver = cast(str, r["version"])
|
|
449
|
+
seed_blob_by_ver[(ch, ver)] = cast(
|
|
450
|
+
"tuple[str, bytes]", (r["type"], r["blob"])
|
|
451
|
+
)
|
|
335
452
|
|
|
336
|
-
|
|
337
|
-
|
|
453
|
+
# Sort writes per (channel, cid) newest-first by (task_id, idx)
|
|
454
|
+
for cid_map in writes_by_ch_by_cid.values():
|
|
455
|
+
for ws in cid_map.values():
|
|
456
|
+
ws.sort(key=lambda w: (w[2], w[3]), reverse=True)
|
|
457
|
+
|
|
458
|
+
result: dict[str, DeltaChannelHistory] = {}
|
|
459
|
+
for ch in channels:
|
|
460
|
+
chain_cids = chain_by_ch.get(ch, [])
|
|
461
|
+
seed_version = seed_ver_by_ch.get(ch)
|
|
462
|
+
|
|
463
|
+
collected: list[PendingWrite] = []
|
|
464
|
+
cid_writes = writes_by_ch_by_cid.get(ch, {})
|
|
465
|
+
for cid in chain_cids:
|
|
466
|
+
for type_tag, write_blob, task_id, _idx in cid_writes.get(cid, []):
|
|
467
|
+
val = self.serde.loads_typed((type_tag, write_blob))
|
|
468
|
+
collected.append((task_id, ch, val))
|
|
469
|
+
collected.reverse()
|
|
470
|
+
|
|
471
|
+
entry: DeltaChannelHistory = {"writes": collected}
|
|
472
|
+
if seed_version is not None:
|
|
473
|
+
blob = seed_blob_by_ver.get((ch, seed_version))
|
|
474
|
+
if blob is not None and blob[0] != "empty":
|
|
475
|
+
entry["seed"] = self.serde.loads_typed(blob)
|
|
476
|
+
result[ch] = entry
|
|
477
|
+
return result
|
|
338
478
|
|
|
339
479
|
def _dump_blobs(
|
|
340
480
|
self,
|
{langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/pyproject.toml
RENAMED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "langgraph-checkpoint-postgres"
|
|
7
|
-
version = "3.1.
|
|
7
|
+
version = "3.1.0a4"
|
|
8
8
|
description = "Library with a Postgres implementation of LangGraph checkpoint saver."
|
|
9
9
|
authors = []
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -12,7 +12,7 @@ readme = "README.md"
|
|
|
12
12
|
license = "MIT"
|
|
13
13
|
license-files = ['LICENSE']
|
|
14
14
|
dependencies = [
|
|
15
|
-
"langgraph-checkpoint>=4.1.
|
|
15
|
+
"langgraph-checkpoint>=4.1.0a4,<5.0.0",
|
|
16
16
|
"orjson>=3.11.5",
|
|
17
17
|
"psycopg>=3.2.0",
|
|
18
18
|
"psycopg-pool>=3.2.0",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
version = 1
|
|
2
2
|
revision = 3
|
|
3
3
|
requires-python = ">=3.10"
|
|
4
|
+
resolution-markers = [
|
|
5
|
+
"python_full_version >= '3.15'",
|
|
6
|
+
"python_full_version < '3.15'",
|
|
7
|
+
]
|
|
4
8
|
|
|
5
9
|
[[package]]
|
|
6
10
|
name = "annotated-types"
|
|
@@ -240,10 +244,11 @@ wheels = [
|
|
|
240
244
|
|
|
241
245
|
[[package]]
|
|
242
246
|
name = "langchain-core"
|
|
243
|
-
version = "1.2
|
|
247
|
+
version = "1.3.2"
|
|
244
248
|
source = { registry = "https://pypi.org/simple" }
|
|
245
249
|
dependencies = [
|
|
246
250
|
{ name = "jsonpatch" },
|
|
251
|
+
{ name = "langchain-protocol" },
|
|
247
252
|
{ name = "langsmith" },
|
|
248
253
|
{ name = "packaging" },
|
|
249
254
|
{ name = "pydantic" },
|
|
@@ -252,14 +257,26 @@ dependencies = [
|
|
|
252
257
|
{ name = "typing-extensions" },
|
|
253
258
|
{ name = "uuid-utils" },
|
|
254
259
|
]
|
|
255
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
260
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a8/03/7219502e8ca728d65eb44d7a3eb60239230742a70dbfc9241b9bfd61c4ab/langchain_core-1.3.2.tar.gz", hash = "sha256:fd7a50b2f28ba561fd9d7f5d2760bc9e06cf00cdf820a3ccafe88a94ffa8d5b7", size = 911813, upload-time = "2026-04-24T15:49:23.699Z" }
|
|
261
|
+
wheels = [
|
|
262
|
+
{ url = "https://files.pythonhosted.org/packages/7d/d5/8fa4431007cbb7cfed7590f4d6a5dea3ad724f4174d248f6642ef5ce7d05/langchain_core-1.3.2-py3-none-any.whl", hash = "sha256:d44a66127f9f8db735bdfd0ab9661bccb47a97113cfd3f2d89c74864422b7274", size = 542390, upload-time = "2026-04-24T15:49:21.991Z" },
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
[[package]]
|
|
266
|
+
name = "langchain-protocol"
|
|
267
|
+
version = "0.0.15"
|
|
268
|
+
source = { registry = "https://pypi.org/simple" }
|
|
269
|
+
dependencies = [
|
|
270
|
+
{ name = "typing-extensions" },
|
|
271
|
+
]
|
|
272
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4f/24/9777489d6fbbee64af0c8f96d4f840239c408cf694f3394672807dafc490/langchain_protocol-0.0.15.tar.gz", hash = "sha256:9ab2d11ee73944754f10e037e717098d3a6796f0e58afa9cadda6154e7655ade", size = 5862, upload-time = "2026-05-01T22:30:04.748Z" }
|
|
256
273
|
wheels = [
|
|
257
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
274
|
+
{ url = "https://files.pythonhosted.org/packages/1d/7a/9c97a7b9cbe4c5dc6a44cdb1545450c28f0c8ce89b9c1f0ee7fbad896263/langchain_protocol-0.0.15-py3-none-any.whl", hash = "sha256:461eb794358f83d5e42635a5797799ffec7b4702314e34edf73ac21e75d3ef79", size = 6982, upload-time = "2026-05-01T22:30:03.877Z" },
|
|
258
275
|
]
|
|
259
276
|
|
|
260
277
|
[[package]]
|
|
261
278
|
name = "langgraph-checkpoint"
|
|
262
|
-
version = "4.1.
|
|
279
|
+
version = "4.1.0a4"
|
|
263
280
|
source = { editable = "../checkpoint" }
|
|
264
281
|
dependencies = [
|
|
265
282
|
{ name = "langchain-core" },
|
|
@@ -307,7 +324,7 @@ test = [
|
|
|
307
324
|
|
|
308
325
|
[[package]]
|
|
309
326
|
name = "langgraph-checkpoint-postgres"
|
|
310
|
-
version = "3.1.
|
|
327
|
+
version = "3.1.0a4"
|
|
311
328
|
source = { editable = "." }
|
|
312
329
|
dependencies = [
|
|
313
330
|
{ name = "langgraph-checkpoint" },
|
|
@@ -487,7 +504,7 @@ wheels = [
|
|
|
487
504
|
|
|
488
505
|
[[package]]
|
|
489
506
|
name = "mypy"
|
|
490
|
-
version = "1.20.
|
|
507
|
+
version = "1.20.2"
|
|
491
508
|
source = { registry = "https://pypi.org/simple" }
|
|
492
509
|
dependencies = [
|
|
493
510
|
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
|
|
@@ -496,51 +513,51 @@ dependencies = [
|
|
|
496
513
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
497
514
|
{ name = "typing-extensions" },
|
|
498
515
|
]
|
|
499
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
516
|
+
sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" }
|
|
500
517
|
wheels = [
|
|
501
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
502
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
503
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
504
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
505
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
506
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
507
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
508
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
509
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
510
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
511
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
512
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
513
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
514
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
515
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
516
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
517
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
518
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
519
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
520
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
521
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
522
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
523
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
524
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
525
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
526
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
527
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
528
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
529
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
530
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
531
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
532
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
533
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
534
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
535
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
536
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
537
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
538
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
539
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
540
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
541
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
542
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
543
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
518
|
+
{ url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" },
|
|
519
|
+
{ url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" },
|
|
520
|
+
{ url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" },
|
|
521
|
+
{ url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" },
|
|
522
|
+
{ url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" },
|
|
523
|
+
{ url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" },
|
|
524
|
+
{ url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" },
|
|
525
|
+
{ url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" },
|
|
526
|
+
{ url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" },
|
|
527
|
+
{ url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" },
|
|
528
|
+
{ url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" },
|
|
529
|
+
{ url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" },
|
|
530
|
+
{ url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" },
|
|
531
|
+
{ url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" },
|
|
532
|
+
{ url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" },
|
|
533
|
+
{ url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" },
|
|
534
|
+
{ url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" },
|
|
535
|
+
{ url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" },
|
|
536
|
+
{ url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" },
|
|
537
|
+
{ url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" },
|
|
538
|
+
{ url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" },
|
|
539
|
+
{ url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" },
|
|
540
|
+
{ url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" },
|
|
541
|
+
{ url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" },
|
|
542
|
+
{ url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" },
|
|
543
|
+
{ url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" },
|
|
544
|
+
{ url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" },
|
|
545
|
+
{ url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" },
|
|
546
|
+
{ url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" },
|
|
547
|
+
{ url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" },
|
|
548
|
+
{ url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" },
|
|
549
|
+
{ url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" },
|
|
550
|
+
{ url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" },
|
|
551
|
+
{ url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" },
|
|
552
|
+
{ url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" },
|
|
553
|
+
{ url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" },
|
|
554
|
+
{ url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" },
|
|
555
|
+
{ url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" },
|
|
556
|
+
{ url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" },
|
|
557
|
+
{ url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" },
|
|
558
|
+
{ url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" },
|
|
559
|
+
{ url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" },
|
|
560
|
+
{ url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" },
|
|
544
561
|
]
|
|
545
562
|
|
|
546
563
|
[[package]]
|
|
@@ -1099,27 +1116,27 @@ wheels = [
|
|
|
1099
1116
|
|
|
1100
1117
|
[[package]]
|
|
1101
1118
|
name = "ruff"
|
|
1102
|
-
version = "0.15.
|
|
1119
|
+
version = "0.15.12"
|
|
1103
1120
|
source = { registry = "https://pypi.org/simple" }
|
|
1104
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1121
|
+
sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" }
|
|
1105
1122
|
wheels = [
|
|
1106
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1107
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1108
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1109
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1110
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1111
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1112
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1113
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1114
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1115
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1116
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1117
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1118
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1119
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1120
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1121
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1122
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1123
|
+
{ url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" },
|
|
1124
|
+
{ url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" },
|
|
1125
|
+
{ url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" },
|
|
1126
|
+
{ url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" },
|
|
1127
|
+
{ url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" },
|
|
1128
|
+
{ url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" },
|
|
1129
|
+
{ url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" },
|
|
1130
|
+
{ url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" },
|
|
1131
|
+
{ url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" },
|
|
1132
|
+
{ url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" },
|
|
1133
|
+
{ url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" },
|
|
1134
|
+
{ url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" },
|
|
1135
|
+
{ url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" },
|
|
1136
|
+
{ url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" },
|
|
1137
|
+
{ url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" },
|
|
1138
|
+
{ url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" },
|
|
1139
|
+
{ url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" },
|
|
1123
1140
|
]
|
|
1124
1141
|
|
|
1125
1142
|
[[package]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/conftest.py
RENAMED
|
File without changes
|
|
File without changes
|
{langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/test_async.py
RENAMED
|
File without changes
|
|
File without changes
|
{langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/test_store.py
RENAMED
|
File without changes
|
{langgraph_checkpoint_postgres-3.1.0a3 → langgraph_checkpoint_postgres-3.1.0a4}/tests/test_sync.py
RENAMED
|
File without changes
|