langgraph-checkpoint-sqlite 2.0.7__tar.gz → 2.0.8__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-2.0.8/.gitignore +183 -0
- langgraph_checkpoint_sqlite-2.0.8/Makefile +37 -0
- {langgraph_checkpoint_sqlite-2.0.7 → langgraph_checkpoint_sqlite-2.0.8}/PKG-INFO +7 -14
- langgraph_checkpoint_sqlite-2.0.8/langgraph/cache/sqlite/__init__.py +120 -0
- {langgraph_checkpoint_sqlite-2.0.7 → langgraph_checkpoint_sqlite-2.0.8}/langgraph/checkpoint/sqlite/__init__.py +17 -6
- {langgraph_checkpoint_sqlite-2.0.7 → langgraph_checkpoint_sqlite-2.0.8}/langgraph/checkpoint/sqlite/aio.py +13 -3
- {langgraph_checkpoint_sqlite-2.0.7 → langgraph_checkpoint_sqlite-2.0.8}/langgraph/checkpoint/sqlite/utils.py +7 -6
- langgraph_checkpoint_sqlite-2.0.8/langgraph/store/sqlite/__init__.py +4 -0
- langgraph_checkpoint_sqlite-2.0.8/langgraph/store/sqlite/aio.py +583 -0
- langgraph_checkpoint_sqlite-2.0.8/langgraph/store/sqlite/base.py +1725 -0
- langgraph_checkpoint_sqlite-2.0.8/pyproject.toml +73 -0
- langgraph_checkpoint_sqlite-2.0.8/tests/__init__.py +0 -0
- langgraph_checkpoint_sqlite-2.0.8/tests/test_aiosqlite.py +125 -0
- langgraph_checkpoint_sqlite-2.0.8/tests/test_async_store.py +494 -0
- langgraph_checkpoint_sqlite-2.0.8/tests/test_sqlite.py +182 -0
- langgraph_checkpoint_sqlite-2.0.8/tests/test_store.py +824 -0
- langgraph_checkpoint_sqlite-2.0.8/tests/test_ttl.py +355 -0
- langgraph_checkpoint_sqlite-2.0.8/uv.lock +1136 -0
- langgraph_checkpoint_sqlite-2.0.7/pyproject.toml +0 -65
- {langgraph_checkpoint_sqlite-2.0.7 → langgraph_checkpoint_sqlite-2.0.8}/README.md +0 -0
- {langgraph_checkpoint_sqlite-2.0.7 → langgraph_checkpoint_sqlite-2.0.8}/langgraph/checkpoint/sqlite/py.typed +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
.vs/
|
|
2
|
+
.vscode/
|
|
3
|
+
.idea/
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
pip-wheel-metadata/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# PyInstaller
|
|
34
|
+
# Usually these files are written by a python script from a template
|
|
35
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
36
|
+
*.manifest
|
|
37
|
+
*.spec
|
|
38
|
+
|
|
39
|
+
# Installer logs
|
|
40
|
+
pip-log.txt
|
|
41
|
+
pip-delete-this-directory.txt
|
|
42
|
+
|
|
43
|
+
# Unit test / coverage reports
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
.cache
|
|
50
|
+
nosetests.xml
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
*.py,cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
docs/docs/_build/
|
|
77
|
+
|
|
78
|
+
# PyBuilder
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
notebooks/
|
|
84
|
+
|
|
85
|
+
# IPython
|
|
86
|
+
profile_default/
|
|
87
|
+
ipython_config.py
|
|
88
|
+
|
|
89
|
+
# pyenv
|
|
90
|
+
.python-version
|
|
91
|
+
|
|
92
|
+
# pipenv
|
|
93
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
94
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
95
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
96
|
+
# install all needed dependencies.
|
|
97
|
+
#Pipfile.lock
|
|
98
|
+
|
|
99
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
100
|
+
__pypackages__/
|
|
101
|
+
|
|
102
|
+
# Celery stuff
|
|
103
|
+
celerybeat-schedule
|
|
104
|
+
celerybeat.pid
|
|
105
|
+
|
|
106
|
+
# SageMath parsed files
|
|
107
|
+
*.sage.py
|
|
108
|
+
|
|
109
|
+
# Environments
|
|
110
|
+
.env
|
|
111
|
+
.envrc
|
|
112
|
+
.venv
|
|
113
|
+
.venvs
|
|
114
|
+
env/
|
|
115
|
+
venv/
|
|
116
|
+
ENV/
|
|
117
|
+
env.bak/
|
|
118
|
+
venv.bak/
|
|
119
|
+
|
|
120
|
+
# Spyder project settings
|
|
121
|
+
.spyderproject
|
|
122
|
+
.spyproject
|
|
123
|
+
|
|
124
|
+
# Rope project settings
|
|
125
|
+
.ropeproject
|
|
126
|
+
|
|
127
|
+
# mkdocs documentation
|
|
128
|
+
/site
|
|
129
|
+
|
|
130
|
+
# mypy
|
|
131
|
+
.mypy_cache/
|
|
132
|
+
.dmypy.json
|
|
133
|
+
dmypy.json
|
|
134
|
+
|
|
135
|
+
# Pyre type checker
|
|
136
|
+
.pyre/
|
|
137
|
+
|
|
138
|
+
# macOS display setting files
|
|
139
|
+
.DS_Store
|
|
140
|
+
|
|
141
|
+
# Wandb directory
|
|
142
|
+
wandb/
|
|
143
|
+
|
|
144
|
+
# asdf tool versions
|
|
145
|
+
.tool-versions
|
|
146
|
+
/.ruff_cache/
|
|
147
|
+
|
|
148
|
+
*.pkl
|
|
149
|
+
*.bin
|
|
150
|
+
|
|
151
|
+
# integration test artifacts
|
|
152
|
+
data_map*
|
|
153
|
+
\[('_type', 'fake'), ('stop', None)]
|
|
154
|
+
|
|
155
|
+
# Replit files
|
|
156
|
+
*replit*
|
|
157
|
+
|
|
158
|
+
node_modules
|
|
159
|
+
docs/.yarn/
|
|
160
|
+
docs/node_modules/
|
|
161
|
+
docs/.docusaurus/
|
|
162
|
+
docs/.cache-loader/
|
|
163
|
+
docs/_dist
|
|
164
|
+
docs/api_reference/api_reference.rst
|
|
165
|
+
docs/api_reference/experimental_api_reference.rst
|
|
166
|
+
docs/api_reference/_build
|
|
167
|
+
docs/api_reference/*/
|
|
168
|
+
!docs/api_reference/_static/
|
|
169
|
+
!docs/api_reference/templates/
|
|
170
|
+
!docs/api_reference/themes/
|
|
171
|
+
docs/docs_skeleton/build
|
|
172
|
+
docs/docs_skeleton/node_modules
|
|
173
|
+
docs/docs_skeleton/yarn.lock
|
|
174
|
+
|
|
175
|
+
# Any new jupyter notebooks
|
|
176
|
+
# not intended for the repo
|
|
177
|
+
Untitled*.ipynb
|
|
178
|
+
|
|
179
|
+
Chinook.db
|
|
180
|
+
|
|
181
|
+
.vercel
|
|
182
|
+
.turbo
|
|
183
|
+
.editorconfig
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.PHONY: test test_watch lint format
|
|
2
|
+
|
|
3
|
+
######################
|
|
4
|
+
# TESTING AND COVERAGE
|
|
5
|
+
######################
|
|
6
|
+
|
|
7
|
+
TEST ?= .
|
|
8
|
+
|
|
9
|
+
test:
|
|
10
|
+
uv run pytest $(TEST)
|
|
11
|
+
|
|
12
|
+
test_watch:
|
|
13
|
+
uv run ptw $(TEST)
|
|
14
|
+
|
|
15
|
+
######################
|
|
16
|
+
# LINTING AND FORMATTING
|
|
17
|
+
######################
|
|
18
|
+
|
|
19
|
+
# Define a variable for Python and notebook files.
|
|
20
|
+
PYTHON_FILES=.
|
|
21
|
+
MYPY_CACHE=.mypy_cache
|
|
22
|
+
lint format: PYTHON_FILES=.
|
|
23
|
+
lint_diff format_diff: PYTHON_FILES=$(shell git diff --name-only --relative --diff-filter=d main . | grep -E '\.py$$|\.ipynb$$')
|
|
24
|
+
lint_package: PYTHON_FILES=langgraph
|
|
25
|
+
lint_tests: PYTHON_FILES=tests
|
|
26
|
+
lint_tests: MYPY_CACHE=.mypy_cache_test
|
|
27
|
+
|
|
28
|
+
lint lint_diff lint_package lint_tests:
|
|
29
|
+
uv run ruff check .
|
|
30
|
+
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
|
31
|
+
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
|
32
|
+
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE)
|
|
33
|
+
[ "$(PYTHON_FILES)" = "" ] || uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
|
34
|
+
|
|
35
|
+
format format_diff:
|
|
36
|
+
uv run ruff format $(PYTHON_FILES)
|
|
37
|
+
uv run ruff check --select I --fix $(PYTHON_FILES)
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-checkpoint-sqlite
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.8
|
|
4
4
|
Summary: Library with a SQLite implementation of LangGraph checkpoint saver.
|
|
5
|
-
License: MIT
|
|
6
|
-
Requires-Python: >=3.9
|
|
7
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Requires-Dist: aiosqlite (>=0.20,<0.22)
|
|
15
|
-
Requires-Dist: langgraph-checkpoint (>=2.0.15,<3.0.0)
|
|
16
5
|
Project-URL: Repository, https://www.github.com/langchain-ai/langgraph
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Requires-Dist: aiosqlite>=0.20
|
|
9
|
+
Requires-Dist: langgraph-checkpoint>=2.0.21
|
|
10
|
+
Requires-Dist: sqlite-vec>=0.1.6
|
|
17
11
|
Description-Content-Type: text/markdown
|
|
18
12
|
|
|
19
13
|
# LangGraph SQLite Checkpoint
|
|
@@ -106,4 +100,3 @@ async with AsyncSqliteSaver.from_conn_string(":memory:") as checkpointer:
|
|
|
106
100
|
# list checkpoints
|
|
107
101
|
[c async for c in checkpointer.alist(read_config)]
|
|
108
102
|
```
|
|
109
|
-
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import datetime
|
|
5
|
+
import sqlite3
|
|
6
|
+
import threading
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
8
|
+
|
|
9
|
+
from langgraph.cache.base import BaseCache, FullKey, Namespace, ValueT
|
|
10
|
+
from langgraph.checkpoint.serde.base import SerializerProtocol
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SqliteCache(BaseCache[ValueT]):
|
|
14
|
+
"""File-based cache using SQLite."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
path: str,
|
|
20
|
+
serde: SerializerProtocol | None = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Initialize the cache with a file path."""
|
|
23
|
+
super().__init__(serde=serde)
|
|
24
|
+
# SQLite backing store
|
|
25
|
+
self._conn = sqlite3.connect(
|
|
26
|
+
path,
|
|
27
|
+
check_same_thread=False,
|
|
28
|
+
)
|
|
29
|
+
# Serialize access to the shared connection across threads
|
|
30
|
+
self._lock = threading.RLock()
|
|
31
|
+
# Better concurrency & atomicity
|
|
32
|
+
self._conn.execute("PRAGMA journal_mode=WAL;")
|
|
33
|
+
# Schema: key -> (expiry, encoding, value)
|
|
34
|
+
self._conn.execute(
|
|
35
|
+
"""CREATE TABLE IF NOT EXISTS cache (
|
|
36
|
+
ns TEXT,
|
|
37
|
+
key TEXT,
|
|
38
|
+
expiry REAL,
|
|
39
|
+
encoding TEXT NOT NULL,
|
|
40
|
+
val BLOB NOT NULL,
|
|
41
|
+
PRIMARY KEY (ns, key)
|
|
42
|
+
)"""
|
|
43
|
+
)
|
|
44
|
+
self._conn.commit()
|
|
45
|
+
|
|
46
|
+
def get(self, keys: Sequence[FullKey]) -> dict[FullKey, ValueT]:
|
|
47
|
+
"""Get the cached values for the given keys."""
|
|
48
|
+
with self._lock, self._conn:
|
|
49
|
+
now = datetime.datetime.now(datetime.timezone.utc).timestamp()
|
|
50
|
+
if not keys:
|
|
51
|
+
return {}
|
|
52
|
+
placeholders = ",".join("(?, ?)" for _ in keys)
|
|
53
|
+
params: list[str] = []
|
|
54
|
+
for ns_tuple, key in keys:
|
|
55
|
+
params.extend((",".join(ns_tuple), key))
|
|
56
|
+
cursor = self._conn.execute(
|
|
57
|
+
f"SELECT ns, key, expiry, encoding, val FROM cache WHERE (ns, key) IN ({placeholders})",
|
|
58
|
+
tuple(params),
|
|
59
|
+
)
|
|
60
|
+
values: dict[FullKey, ValueT] = {}
|
|
61
|
+
rows = cursor.fetchall()
|
|
62
|
+
for ns, key, expiry, encoding, raw in rows:
|
|
63
|
+
if expiry is not None and now > expiry:
|
|
64
|
+
# purge expired entry
|
|
65
|
+
self._conn.execute(
|
|
66
|
+
"DELETE FROM cache WHERE (ns, key) = (?, ?)", (ns, key)
|
|
67
|
+
)
|
|
68
|
+
continue
|
|
69
|
+
values[(tuple(ns.split(",")), key)] = self.serde.loads_typed(
|
|
70
|
+
(encoding, raw)
|
|
71
|
+
)
|
|
72
|
+
return values
|
|
73
|
+
|
|
74
|
+
async def aget(self, keys: Sequence[FullKey]) -> dict[FullKey, ValueT]:
|
|
75
|
+
"""Asynchronously get the cached values for the given keys."""
|
|
76
|
+
return await asyncio.to_thread(self.get, keys)
|
|
77
|
+
|
|
78
|
+
def set(self, mapping: Mapping[FullKey, tuple[ValueT, int | None]]) -> None:
|
|
79
|
+
"""Set the cached values for the given keys and TTLs."""
|
|
80
|
+
with self._lock, self._conn:
|
|
81
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
|
82
|
+
for key, (value, ttl) in mapping.items():
|
|
83
|
+
if ttl is not None:
|
|
84
|
+
delta = datetime.timedelta(seconds=ttl)
|
|
85
|
+
expiry: float | None = (now + delta).timestamp()
|
|
86
|
+
else:
|
|
87
|
+
expiry = None
|
|
88
|
+
encoding, raw = self.serde.dumps_typed(value)
|
|
89
|
+
self._conn.execute(
|
|
90
|
+
"INSERT OR REPLACE INTO cache (ns, key, expiry, encoding, val) VALUES (?, ?, ?, ?, ?)",
|
|
91
|
+
(",".join(key[0]), key[1], expiry, encoding, raw),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
async def aset(self, mapping: Mapping[FullKey, tuple[ValueT, int | None]]) -> None:
|
|
95
|
+
"""Asynchronously set the cached values for the given keys and TTLs."""
|
|
96
|
+
await asyncio.to_thread(self.set, mapping)
|
|
97
|
+
|
|
98
|
+
def clear(self, namespaces: Sequence[Namespace] | None = None) -> None:
|
|
99
|
+
"""Delete the cached values for the given namespaces.
|
|
100
|
+
If no namespaces are provided, clear all cached values."""
|
|
101
|
+
with self._lock, self._conn:
|
|
102
|
+
if namespaces is None:
|
|
103
|
+
self._conn.execute("DELETE FROM cache")
|
|
104
|
+
else:
|
|
105
|
+
placeholders = ",".join("?" for _ in namespaces)
|
|
106
|
+
self._conn.execute(
|
|
107
|
+
f"DELETE FROM cache WHERE (ns) IN ({placeholders})",
|
|
108
|
+
tuple(",".join(key) for key in namespaces),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
async def aclear(self, namespaces: Sequence[Namespace] | None = None) -> None:
|
|
112
|
+
"""Asynchronously delete the cached values for the given namespaces.
|
|
113
|
+
If no namespaces are provided, clear all cached values."""
|
|
114
|
+
await asyncio.to_thread(self.clear, namespaces)
|
|
115
|
+
|
|
116
|
+
def __del__(self) -> None:
|
|
117
|
+
try:
|
|
118
|
+
self._conn.close()
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import random
|
|
2
2
|
import sqlite3
|
|
3
3
|
import threading
|
|
4
|
+
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
4
5
|
from contextlib import closing, contextmanager
|
|
5
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Optional, cast
|
|
6
7
|
|
|
7
8
|
from langchain_core.runnables import RunnableConfig
|
|
8
9
|
|
|
@@ -261,7 +262,12 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
|
|
261
262
|
return CheckpointTuple(
|
|
262
263
|
config,
|
|
263
264
|
self.serde.loads_typed((type, checkpoint)),
|
|
264
|
-
|
|
265
|
+
cast(
|
|
266
|
+
CheckpointMetadata,
|
|
267
|
+
self.jsonplus_serde.loads(metadata)
|
|
268
|
+
if metadata is not None
|
|
269
|
+
else {},
|
|
270
|
+
),
|
|
265
271
|
(
|
|
266
272
|
{
|
|
267
273
|
"configurable": {
|
|
@@ -283,7 +289,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
|
|
283
289
|
self,
|
|
284
290
|
config: Optional[RunnableConfig],
|
|
285
291
|
*,
|
|
286
|
-
filter: Optional[
|
|
292
|
+
filter: Optional[dict[str, Any]] = None,
|
|
287
293
|
before: Optional[RunnableConfig] = None,
|
|
288
294
|
limit: Optional[int] = None,
|
|
289
295
|
) -> Iterator[CheckpointTuple]:
|
|
@@ -349,7 +355,12 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
|
|
349
355
|
}
|
|
350
356
|
},
|
|
351
357
|
self.serde.loads_typed((type, checkpoint)),
|
|
352
|
-
|
|
358
|
+
cast(
|
|
359
|
+
CheckpointMetadata,
|
|
360
|
+
self.jsonplus_serde.loads(metadata)
|
|
361
|
+
if metadata is not None
|
|
362
|
+
else {},
|
|
363
|
+
),
|
|
353
364
|
(
|
|
354
365
|
{
|
|
355
366
|
"configurable": {
|
|
@@ -428,7 +439,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
|
|
428
439
|
def put_writes(
|
|
429
440
|
self,
|
|
430
441
|
config: RunnableConfig,
|
|
431
|
-
writes: Sequence[
|
|
442
|
+
writes: Sequence[tuple[str, Any]],
|
|
432
443
|
task_id: str,
|
|
433
444
|
task_path: str = "",
|
|
434
445
|
) -> None:
|
|
@@ -496,7 +507,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
|
|
496
507
|
self,
|
|
497
508
|
config: Optional[RunnableConfig],
|
|
498
509
|
*,
|
|
499
|
-
filter: Optional[
|
|
510
|
+
filter: Optional[dict[str, Any]] = None,
|
|
500
511
|
before: Optional[RunnableConfig] = None,
|
|
501
512
|
limit: Optional[int] = None,
|
|
502
513
|
) -> AsyncIterator[CheckpointTuple]:
|
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
import random
|
|
3
3
|
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
4
4
|
from contextlib import asynccontextmanager
|
|
5
|
-
from typing import Any, Callable, Optional, TypeVar
|
|
5
|
+
from typing import Any, Callable, Optional, TypeVar, cast
|
|
6
6
|
|
|
7
7
|
import aiosqlite
|
|
8
8
|
from langchain_core.runnables import RunnableConfig
|
|
@@ -374,7 +374,12 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
|
|
|
374
374
|
return CheckpointTuple(
|
|
375
375
|
config,
|
|
376
376
|
self.serde.loads_typed((type, checkpoint)),
|
|
377
|
-
|
|
377
|
+
cast(
|
|
378
|
+
CheckpointMetadata,
|
|
379
|
+
self.jsonplus_serde.loads(metadata)
|
|
380
|
+
if metadata is not None
|
|
381
|
+
else {},
|
|
382
|
+
),
|
|
378
383
|
(
|
|
379
384
|
{
|
|
380
385
|
"configurable": {
|
|
@@ -449,7 +454,12 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
|
|
|
449
454
|
}
|
|
450
455
|
},
|
|
451
456
|
self.serde.loads_typed((type, checkpoint)),
|
|
452
|
-
|
|
457
|
+
cast(
|
|
458
|
+
CheckpointMetadata,
|
|
459
|
+
self.jsonplus_serde.loads(metadata)
|
|
460
|
+
if metadata is not None
|
|
461
|
+
else {},
|
|
462
|
+
),
|
|
453
463
|
(
|
|
454
464
|
{
|
|
455
465
|
"configurable": {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import Any, Optional
|
|
3
4
|
|
|
4
5
|
from langchain_core.runnables import RunnableConfig
|
|
5
6
|
|
|
@@ -7,8 +8,8 @@ from langgraph.checkpoint.base import get_checkpoint_id
|
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def _metadata_predicate(
|
|
10
|
-
metadata_filter:
|
|
11
|
-
) ->
|
|
11
|
+
metadata_filter: dict[str, Any],
|
|
12
|
+
) -> tuple[Sequence[str], Sequence[Any]]:
|
|
12
13
|
"""Return WHERE clause predicates for (a)search() given metadata filter.
|
|
13
14
|
|
|
14
15
|
This method returns a tuple of a string and a tuple of values. The string
|
|
@@ -17,7 +18,7 @@ def _metadata_predicate(
|
|
|
17
18
|
for each of the corresponding parameters.
|
|
18
19
|
"""
|
|
19
20
|
|
|
20
|
-
def _where_value(query_value: Any) ->
|
|
21
|
+
def _where_value(query_value: Any) -> tuple[str, Any]:
|
|
21
22
|
"""Return tuple of operator and value for WHERE clause predicate."""
|
|
22
23
|
if query_value is None:
|
|
23
24
|
return ("IS ?", None)
|
|
@@ -52,9 +53,9 @@ def _metadata_predicate(
|
|
|
52
53
|
|
|
53
54
|
def search_where(
|
|
54
55
|
config: Optional[RunnableConfig],
|
|
55
|
-
filter: Optional[
|
|
56
|
+
filter: Optional[dict[str, Any]],
|
|
56
57
|
before: Optional[RunnableConfig] = None,
|
|
57
|
-
) ->
|
|
58
|
+
) -> tuple[str, Sequence[Any]]:
|
|
58
59
|
"""Return WHERE clause predicates for (a)search() given metadata filter
|
|
59
60
|
and `before` config.
|
|
60
61
|
|