langgraph-checkpoint-sqlite 1.0.3__tar.gz → 2.0.0__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_sqlite-1.0.3 → langgraph_checkpoint_sqlite-2.0.0}/PKG-INFO +2 -2
- {langgraph_checkpoint_sqlite-1.0.3 → langgraph_checkpoint_sqlite-2.0.0}/langgraph/checkpoint/sqlite/__init__.py +17 -13
- {langgraph_checkpoint_sqlite-1.0.3 → langgraph_checkpoint_sqlite-2.0.0}/langgraph/checkpoint/sqlite/aio.py +48 -6
- {langgraph_checkpoint_sqlite-1.0.3 → langgraph_checkpoint_sqlite-2.0.0}/pyproject.toml +12 -2
- {langgraph_checkpoint_sqlite-1.0.3 → langgraph_checkpoint_sqlite-2.0.0}/README.md +0 -0
- {langgraph_checkpoint_sqlite-1.0.3 → langgraph_checkpoint_sqlite-2.0.0}/langgraph/checkpoint/py.typed +0 -0
- {langgraph_checkpoint_sqlite-1.0.3 → langgraph_checkpoint_sqlite-2.0.0}/langgraph/checkpoint/sqlite/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: langgraph-checkpoint-sqlite
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Library with a SQLite implementation of LangGraph checkpoint saver.
|
|
5
5
|
Home-page: https://www.github.com/langchain-ai/langgraph
|
|
6
6
|
License: MIT
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Requires-Dist: aiosqlite (>=0.20.0,<0.21.0)
|
|
15
|
-
Requires-Dist: langgraph-checkpoint (>=
|
|
15
|
+
Requires-Dist: langgraph-checkpoint (>=2.0.0,<3.0.0)
|
|
16
16
|
Project-URL: Repository, https://www.github.com/langchain-ai/langgraph
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import random
|
|
1
2
|
import sqlite3
|
|
2
3
|
import threading
|
|
3
4
|
from contextlib import closing, contextmanager
|
|
4
|
-
from hashlib import md5
|
|
5
5
|
from typing import Any, AsyncIterator, Dict, Iterator, Optional, Sequence, Tuple
|
|
6
6
|
|
|
7
7
|
from langchain_core.runnables import RunnableConfig
|
|
@@ -13,7 +13,6 @@ from langgraph.checkpoint.base import (
|
|
|
13
13
|
Checkpoint,
|
|
14
14
|
CheckpointMetadata,
|
|
15
15
|
CheckpointTuple,
|
|
16
|
-
EmptyChannelError,
|
|
17
16
|
SerializerProtocol,
|
|
18
17
|
get_checkpoint_id,
|
|
19
18
|
)
|
|
@@ -32,7 +31,7 @@ _AIO_ERROR_MSG = (
|
|
|
32
31
|
)
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
class SqliteSaver(BaseCheckpointSaver):
|
|
34
|
+
class SqliteSaver(BaseCheckpointSaver[str]):
|
|
36
35
|
"""A checkpoint saver that stores checkpoints in a SQLite database.
|
|
37
36
|
|
|
38
37
|
Note:
|
|
@@ -40,7 +39,7 @@ class SqliteSaver(BaseCheckpointSaver):
|
|
|
40
39
|
(demos and small projects) and does not
|
|
41
40
|
scale to multiple threads.
|
|
42
41
|
For a similar sqlite saver with `async` support,
|
|
43
|
-
consider using [AsyncSqliteSaver][
|
|
42
|
+
consider using [AsyncSqliteSaver][langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver].
|
|
44
43
|
|
|
45
44
|
Args:
|
|
46
45
|
conn (sqlite3.Connection): The SQLite database connection.
|
|
@@ -435,9 +434,14 @@ class SqliteSaver(BaseCheckpointSaver):
|
|
|
435
434
|
writes (Sequence[Tuple[str, Any]]): List of writes to store, each as (channel, value) pair.
|
|
436
435
|
task_id (str): Identifier for the task creating the writes.
|
|
437
436
|
"""
|
|
437
|
+
query = (
|
|
438
|
+
"INSERT OR REPLACE INTO writes (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, value) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
439
|
+
if all(w[0] in WRITES_IDX_MAP for w in writes)
|
|
440
|
+
else "INSERT OR IGNORE INTO writes (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, value) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
441
|
+
)
|
|
438
442
|
with self.cursor() as cur:
|
|
439
443
|
cur.executemany(
|
|
440
|
-
|
|
444
|
+
query,
|
|
441
445
|
[
|
|
442
446
|
(
|
|
443
447
|
str(config["configurable"]["thread_id"]),
|
|
@@ -457,7 +461,7 @@ class SqliteSaver(BaseCheckpointSaver):
|
|
|
457
461
|
|
|
458
462
|
Note:
|
|
459
463
|
This async method is not supported by the SqliteSaver class.
|
|
460
|
-
Use get_tuple() instead, or consider using [AsyncSqliteSaver][
|
|
464
|
+
Use get_tuple() instead, or consider using [AsyncSqliteSaver][langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver].
|
|
461
465
|
"""
|
|
462
466
|
raise NotImplementedError(_AIO_ERROR_MSG)
|
|
463
467
|
|
|
@@ -473,7 +477,7 @@ class SqliteSaver(BaseCheckpointSaver):
|
|
|
473
477
|
|
|
474
478
|
Note:
|
|
475
479
|
This async method is not supported by the SqliteSaver class.
|
|
476
|
-
Use list() instead, or consider using [AsyncSqliteSaver][
|
|
480
|
+
Use list() instead, or consider using [AsyncSqliteSaver][langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver].
|
|
477
481
|
"""
|
|
478
482
|
raise NotImplementedError(_AIO_ERROR_MSG)
|
|
479
483
|
yield
|
|
@@ -483,12 +487,13 @@ class SqliteSaver(BaseCheckpointSaver):
|
|
|
483
487
|
config: RunnableConfig,
|
|
484
488
|
checkpoint: Checkpoint,
|
|
485
489
|
metadata: CheckpointMetadata,
|
|
490
|
+
new_versions: ChannelVersions,
|
|
486
491
|
) -> RunnableConfig:
|
|
487
492
|
"""Save a checkpoint to the database asynchronously.
|
|
488
493
|
|
|
489
494
|
Note:
|
|
490
495
|
This async method is not supported by the SqliteSaver class.
|
|
491
|
-
Use put() instead, or consider using [AsyncSqliteSaver][
|
|
496
|
+
Use put() instead, or consider using [AsyncSqliteSaver][langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver].
|
|
492
497
|
"""
|
|
493
498
|
raise NotImplementedError(_AIO_ERROR_MSG)
|
|
494
499
|
|
|
@@ -506,11 +511,10 @@ class SqliteSaver(BaseCheckpointSaver):
|
|
|
506
511
|
"""
|
|
507
512
|
if current is None:
|
|
508
513
|
current_v = 0
|
|
514
|
+
elif isinstance(current, int):
|
|
515
|
+
current_v = current
|
|
509
516
|
else:
|
|
510
517
|
current_v = int(current.split(".")[0])
|
|
511
518
|
next_v = current_v + 1
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
except EmptyChannelError:
|
|
515
|
-
next_h = ""
|
|
516
|
-
return f"{next_v:032}.{next_h}"
|
|
519
|
+
next_h = random.random()
|
|
520
|
+
return f"{next_v:032}.{next_h:016}"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import random
|
|
2
3
|
from contextlib import asynccontextmanager
|
|
3
4
|
from typing import (
|
|
4
5
|
Any,
|
|
5
6
|
AsyncIterator,
|
|
7
|
+
Callable,
|
|
6
8
|
Dict,
|
|
7
9
|
Iterator,
|
|
8
|
-
List,
|
|
9
10
|
Optional,
|
|
10
11
|
Sequence,
|
|
11
12
|
Tuple,
|
|
@@ -26,12 +27,13 @@ from langgraph.checkpoint.base import (
|
|
|
26
27
|
get_checkpoint_id,
|
|
27
28
|
)
|
|
28
29
|
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
|
|
30
|
+
from langgraph.checkpoint.serde.types import ChannelProtocol
|
|
29
31
|
from langgraph.checkpoint.sqlite.utils import search_where
|
|
30
32
|
|
|
31
|
-
T = TypeVar("T", bound=
|
|
33
|
+
T = TypeVar("T", bound=Callable)
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
class AsyncSqliteSaver(BaseCheckpointSaver):
|
|
36
|
+
class AsyncSqliteSaver(BaseCheckpointSaver[str]):
|
|
35
37
|
"""An asynchronous checkpoint saver that stores checkpoints in a SQLite database.
|
|
36
38
|
|
|
37
39
|
This class provides an asynchronous interface for saving and retrieving checkpoints
|
|
@@ -151,6 +153,18 @@ class AsyncSqliteSaver(BaseCheckpointSaver):
|
|
|
151
153
|
Returns:
|
|
152
154
|
Optional[CheckpointTuple]: The retrieved checkpoint tuple, or None if no matching checkpoint was found.
|
|
153
155
|
"""
|
|
156
|
+
try:
|
|
157
|
+
# check if we are in the main thread, only bg threads can block
|
|
158
|
+
# we don't check in other methods to avoid the overhead
|
|
159
|
+
if asyncio.get_running_loop() is self.loop:
|
|
160
|
+
raise asyncio.InvalidStateError(
|
|
161
|
+
"Synchronous calls to AsyncSqliteSaver are only allowed from a "
|
|
162
|
+
"different thread. From the main thread, use the async interface."
|
|
163
|
+
"For example, use `await checkpointer.aget_tuple(...)` or `await "
|
|
164
|
+
"graph.ainvoke(...)`."
|
|
165
|
+
)
|
|
166
|
+
except RuntimeError:
|
|
167
|
+
pass
|
|
154
168
|
return asyncio.run_coroutine_threadsafe(
|
|
155
169
|
self.aget_tuple(config), self.loop
|
|
156
170
|
).result()
|
|
@@ -181,7 +195,8 @@ class AsyncSqliteSaver(BaseCheckpointSaver):
|
|
|
181
195
|
while True:
|
|
182
196
|
try:
|
|
183
197
|
yield asyncio.run_coroutine_threadsafe(
|
|
184
|
-
anext(aiter_),
|
|
198
|
+
anext(aiter_),
|
|
199
|
+
self.loop,
|
|
185
200
|
).result()
|
|
186
201
|
except StopAsyncIteration:
|
|
187
202
|
break
|
|
@@ -212,7 +227,7 @@ class AsyncSqliteSaver(BaseCheckpointSaver):
|
|
|
212
227
|
).result()
|
|
213
228
|
|
|
214
229
|
def put_writes(
|
|
215
|
-
self, config: RunnableConfig, writes:
|
|
230
|
+
self, config: RunnableConfig, writes: Sequence[Tuple[str, Any]], task_id: str
|
|
216
231
|
) -> None:
|
|
217
232
|
return asyncio.run_coroutine_threadsafe(
|
|
218
233
|
self.aput_writes(config, writes, task_id), self.loop
|
|
@@ -476,10 +491,15 @@ class AsyncSqliteSaver(BaseCheckpointSaver):
|
|
|
476
491
|
writes (Sequence[Tuple[str, Any]]): List of writes to store, each as (channel, value) pair.
|
|
477
492
|
task_id (str): Identifier for the task creating the writes.
|
|
478
493
|
"""
|
|
494
|
+
query = (
|
|
495
|
+
"INSERT OR REPLACE INTO writes (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, value) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
496
|
+
if all(w[0] in WRITES_IDX_MAP for w in writes)
|
|
497
|
+
else "INSERT OR IGNORE INTO writes (thread_id, checkpoint_ns, checkpoint_id, task_id, idx, channel, type, value) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
498
|
+
)
|
|
479
499
|
await self.setup()
|
|
480
500
|
async with self.lock, self.conn.cursor() as cur:
|
|
481
501
|
await cur.executemany(
|
|
482
|
-
|
|
502
|
+
query,
|
|
483
503
|
[
|
|
484
504
|
(
|
|
485
505
|
str(config["configurable"]["thread_id"]),
|
|
@@ -493,3 +513,25 @@ class AsyncSqliteSaver(BaseCheckpointSaver):
|
|
|
493
513
|
for idx, (channel, value) in enumerate(writes)
|
|
494
514
|
],
|
|
495
515
|
)
|
|
516
|
+
|
|
517
|
+
def get_next_version(self, current: Optional[str], channel: ChannelProtocol) -> str:
|
|
518
|
+
"""Generate the next version ID for a channel.
|
|
519
|
+
|
|
520
|
+
This method creates a new version identifier for a channel based on its current version.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
current (Optional[str]): The current version identifier of the channel.
|
|
524
|
+
channel (BaseChannel): The channel being versioned.
|
|
525
|
+
|
|
526
|
+
Returns:
|
|
527
|
+
str: The next version identifier, which is guaranteed to be monotonically increasing.
|
|
528
|
+
"""
|
|
529
|
+
if current is None:
|
|
530
|
+
current_v = 0
|
|
531
|
+
elif isinstance(current, int):
|
|
532
|
+
current_v = current
|
|
533
|
+
else:
|
|
534
|
+
current_v = int(current.split(".")[0])
|
|
535
|
+
next_v = current_v + 1
|
|
536
|
+
next_h = random.random()
|
|
537
|
+
return f"{next_v:032}.{next_h:016}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "langgraph-checkpoint-sqlite"
|
|
3
|
-
version = "
|
|
3
|
+
version = "2.0.0"
|
|
4
4
|
description = "Library with a SQLite implementation of LangGraph checkpoint saver."
|
|
5
5
|
authors = []
|
|
6
6
|
license = "MIT"
|
|
@@ -10,7 +10,7 @@ packages = [{ include = "langgraph" }]
|
|
|
10
10
|
|
|
11
11
|
[tool.poetry.dependencies]
|
|
12
12
|
python = "^3.9.0"
|
|
13
|
-
langgraph-checkpoint = "^
|
|
13
|
+
langgraph-checkpoint = "^2.0.0"
|
|
14
14
|
aiosqlite = "^0.20.0"
|
|
15
15
|
|
|
16
16
|
[tool.poetry.group.dev.dependencies]
|
|
@@ -53,3 +53,13 @@ now = true
|
|
|
53
53
|
delay = 0.1
|
|
54
54
|
runner_args = ["--ff", "-v", "--tb", "short"]
|
|
55
55
|
patterns = ["*.py"]
|
|
56
|
+
|
|
57
|
+
[tool.mypy]
|
|
58
|
+
# https://mypy.readthedocs.io/en/stable/config_file.html
|
|
59
|
+
disallow_untyped_defs = "True"
|
|
60
|
+
explicit_package_bases = "True"
|
|
61
|
+
warn_no_return = "False"
|
|
62
|
+
warn_unused_ignores = "True"
|
|
63
|
+
warn_redundant_casts = "True"
|
|
64
|
+
allow_redefinition = "True"
|
|
65
|
+
disable_error_code = "typeddict-item, return-value"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|