riffdb-langgraph 0.2.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.
Files changed (39) hide show
  1. riffdb_langgraph-0.2.0/.github/riffdb-revision +1 -0
  2. riffdb_langgraph-0.2.0/.github/workflows/release.yml +175 -0
  3. riffdb_langgraph-0.2.0/.github/workflows/riffdb-compatibility.yml +69 -0
  4. riffdb_langgraph-0.2.0/.gitignore +9 -0
  5. riffdb_langgraph-0.2.0/AGENTS.md +54 -0
  6. riffdb_langgraph-0.2.0/DESIGN.md +64 -0
  7. riffdb_langgraph-0.2.0/Makefile +26 -0
  8. riffdb_langgraph-0.2.0/PKG-INFO +72 -0
  9. riffdb_langgraph-0.2.0/README.md +61 -0
  10. riffdb_langgraph-0.2.0/generated/riffdb.application.exact.json +1 -0
  11. riffdb_langgraph-0.2.0/generated/riffdb.contract.bundle +0 -0
  12. riffdb_langgraph-0.2.0/pyproject.toml +55 -0
  13. riffdb_langgraph-0.2.0/riffdb/contract.riff +300 -0
  14. riffdb_langgraph-0.2.0/riffdb/queries/get_checkpoint_blobs.riffq +25 -0
  15. riffdb_langgraph-0.2.0/riffdb/queries/get_checkpoint_by_id.riffq +43 -0
  16. riffdb_langgraph-0.2.0/riffdb/queries/get_latest_checkpoint.riffq +44 -0
  17. riffdb_langgraph-0.2.0/riffdb/queries/get_parent_pending_sends.riffq +28 -0
  18. riffdb_langgraph-0.2.0/riffdb/queries/list_checkpoints_by_scope.riffq +42 -0
  19. riffdb_langgraph-0.2.0/riffdb/queries/list_checkpoints_by_scope_before.riffq +44 -0
  20. riffdb_langgraph-0.2.0/riffdb/queries/list_checkpoints_by_thread.riffq +44 -0
  21. riffdb_langgraph-0.2.0/riffdb/queries/list_checkpoints_by_thread_before.riffq +46 -0
  22. riffdb_langgraph-0.2.0/riffdb/queries/list_checkpoints_by_thread_ns.riffq +46 -0
  23. riffdb_langgraph-0.2.0/riffdb/queries/list_checkpoints_by_thread_ns_before.riffq +48 -0
  24. riffdb_langgraph-0.2.0/riffdb/queries/list_thread_blob_keys.riffq +22 -0
  25. riffdb_langgraph-0.2.0/riffdb/queries/list_thread_checkpoint_keys.riffq +21 -0
  26. riffdb_langgraph-0.2.0/riffdb/queries/list_thread_write_keys.riffq +23 -0
  27. riffdb_langgraph-0.2.0/riffdb.application.json +71 -0
  28. riffdb_langgraph-0.2.0/riffdb.application.lock.json +1 -0
  29. riffdb_langgraph-0.2.0/riffdb.toml +7 -0
  30. riffdb_langgraph-0.2.0/src/riffdb_langgraph/__init__.py +3 -0
  31. riffdb_langgraph-0.2.0/src/riffdb_langgraph/__main__.py +234 -0
  32. riffdb_langgraph-0.2.0/src/riffdb_langgraph/config.py +38 -0
  33. riffdb_langgraph-0.2.0/src/riffdb_langgraph/generated/client.py +2341 -0
  34. riffdb_langgraph-0.2.0/src/riffdb_langgraph/py.typed +0 -0
  35. riffdb_langgraph-0.2.0/src/riffdb_langgraph/saver.py +1832 -0
  36. riffdb_langgraph-0.2.0/tests/test_config.py +29 -0
  37. riffdb_langgraph-0.2.0/tests/test_saver.py +514 -0
  38. riffdb_langgraph-0.2.0/tests/test_surface.py +31 -0
  39. riffdb_langgraph-0.2.0/uv.lock +1322 -0
@@ -0,0 +1 @@
1
+ fc72da6e3d2af6ab29be72712139e9d16bcaf233
@@ -0,0 +1,175 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v[0-9]+.[0-9]+.[0-9]+'
7
+ workflow_dispatch:
8
+ inputs:
9
+ release_tag:
10
+ description: 'Release tag to verify (for example v0.2.0)'
11
+ required: false
12
+ default: 'v0.2.0'
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ concurrency:
18
+ group: release-${{ github.workflow }}-${{ github.ref }}
19
+ cancel-in-progress: false
20
+
21
+ jobs:
22
+ verify-and-build:
23
+ name: Verify adapter and build Python distributions
24
+ runs-on: ubuntu-24.04
25
+ timeout-minutes: 20
26
+ outputs:
27
+ release_tag: ${{ steps.meta.outputs.release_tag }}
28
+ version: ${{ steps.meta.outputs.version }}
29
+ revision: ${{ steps.meta.outputs.revision }}
30
+ steps:
31
+ - name: Check out adapter
32
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
33
+ with:
34
+ fetch-depth: 0
35
+ persist-credentials: false
36
+ - name: Install Python
37
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
38
+ with:
39
+ python-version: '3.13'
40
+ - name: Install uv
41
+ run: python3 -m pip install --quiet uv
42
+ - name: Resolve release metadata and download RiffDB release bundle
43
+ id: meta
44
+ run: |
45
+ if [[ "${{ github.event_name }}" == "push" ]]; then
46
+ release_tag="${GITHUB_REF_NAME}"
47
+ else
48
+ release_tag="${{ inputs.release_tag || 'v0.2.0' }}"
49
+ fi
50
+ [[ "$release_tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)$ ]]
51
+ version="${BASH_REMATCH[1]}"
52
+ python3 - "$version" <<'PY'
53
+ from pathlib import Path
54
+ import sys
55
+ import tomllib
56
+ expected = sys.argv[1]
57
+ pyproject = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
58
+ assert pyproject["project"]["version"] == expected, pyproject["project"]["version"]
59
+ init_text = Path("src/riffdb_langgraph/__init__.py").read_text(encoding="utf-8")
60
+ assert f'__version__ = "{expected}"' in init_text, init_text
61
+ PY
62
+ riffdb_rev="$(cat .github/riffdb-revision)"
63
+ [[ "$riffdb_rev" =~ ^[0-9a-f]{40}$ ]]
64
+
65
+ bundle="riffdb-${version}-x86_64-unknown-linux-gnu.tar.gz"
66
+ base_url="https://github.com/riffdb/riffdb/releases/download/${release_tag}"
67
+ mkdir -p "$RUNNER_TEMP/riffdb-release"
68
+ curl -fsSL "$base_url/release-provenance-v1.json" -o "$RUNNER_TEMP/riffdb-release/release-provenance-v1.json"
69
+ curl -fsSL "$base_url/$bundle" -o "$RUNNER_TEMP/riffdb-release/$bundle"
70
+ expected_sha="$(jq -r '.bundle_archive.sha256' "$RUNNER_TEMP/riffdb-release/release-provenance-v1.json")"
71
+ echo "${expected_sha} $RUNNER_TEMP/riffdb-release/$bundle" | sha256sum -c -
72
+ tar -xzf "$RUNNER_TEMP/riffdb-release/$bundle" -C "$RUNNER_TEMP/riffdb-release"
73
+ bundle_dir="$RUNNER_TEMP/riffdb-release/riffdb-${version}-x86_64-unknown-linux-gnu"
74
+ [[ "$(tr -d '\n' < "$bundle_dir/REVISION")" == "$riffdb_rev" ]]
75
+ "$bundle_dir/bin/riffdb" application check
76
+
77
+ printf 'release_tag=%s\n' "$release_tag" >>"$GITHUB_OUTPUT"
78
+ printf 'version=%s\n' "$version" >>"$GITHUB_OUTPUT"
79
+ printf 'revision=%s\n' "$riffdb_rev" >>"$GITHUB_OUTPUT"
80
+ - name: Run adapter checks and build wheel and sdist
81
+ run: |
82
+ uv sync --locked
83
+ make check
84
+ mkdir -p "$RUNNER_TEMP/release-out"
85
+ uv build --out-dir "$RUNNER_TEMP/release-out"
86
+ (
87
+ cd "$RUNNER_TEMP/release-out"
88
+ sha256sum "riffdb_langgraph-${{ steps.meta.outputs.version }}"* >SHA256SUMS
89
+ )
90
+ - name: Upload verified release artifacts
91
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
92
+ with:
93
+ name: riffdb-langgraph-release
94
+ path: ${{ runner.temp }}/release-out/*
95
+ if-no-files-found: error
96
+
97
+ publish-release:
98
+ name: Publish GitHub Release and PyPI package
99
+ needs: [verify-and-build]
100
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
101
+ runs-on: ubuntu-24.04
102
+ permissions:
103
+ contents: write
104
+ id-token: write
105
+ attestations: write
106
+ steps:
107
+ - name: Check out adapter
108
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
109
+ - name: Download verified release artifacts
110
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
111
+ with:
112
+ name: riffdb-langgraph-release
113
+ path: release-out
114
+ - name: Attest build provenance
115
+ uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
116
+ with:
117
+ subject-path: |
118
+ release-out/riffdb_langgraph-${{ needs.verify-and-build.outputs.version }}-py3-none-any.whl
119
+ release-out/riffdb_langgraph-${{ needs.verify-and-build.outputs.version }}.tar.gz
120
+ release-out/SHA256SUMS
121
+ - name: Publish GitHub Release
122
+ env:
123
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
124
+ run: |
125
+ tag="${{ needs.verify-and-build.outputs.release_tag }}"
126
+ version="${{ needs.verify-and-build.outputs.version }}"
127
+ notes="Compiled RiffDB checkpoint-saver adapter for LangGraph v${version} (pinned to RiffDB \`${{ needs.verify-and-build.outputs.revision }}\` and \`riffdb==${version}\`)."
128
+ if gh release view "$tag" >/dev/null 2>&1; then
129
+ gh release upload "$tag" release-out/* --clobber
130
+ else
131
+ gh release create "$tag" release-out/* \
132
+ --title "riffdb-langgraph ${tag}" \
133
+ --notes "$notes" \
134
+ --verify-tag
135
+ fi
136
+ - name: Publish to PyPI (when token or Trusted Publishing is configured)
137
+ env:
138
+ PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
139
+ run: |
140
+ python3 -m pip install --quiet twine
141
+ if [[ -n "${PYPI_TOKEN:-}" ]]; then
142
+ python3 -m twine upload --non-interactive --skip-existing -u __token__ -p "$PYPI_TOKEN" release-out/*.whl release-out/*.tar.gz
143
+ else
144
+ python3 - <<'PY' || echo "::warning::PyPI Trusted Publishing or PYPI_TOKEN is not yet configured for riffdb-langgraph; GitHub Release wheel and sdist are published."
145
+ import json
146
+ import os
147
+ import subprocess
148
+ import sys
149
+ import urllib.request
150
+
151
+ req_url = os.environ["ACTIONS_ID_TOKEN_REQUEST_URL"]
152
+ req_tok = os.environ["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
153
+ sep = "&" if "?" in req_url else "?"
154
+ req = urllib.request.Request(
155
+ f"{req_url}{sep}audience=pypi",
156
+ headers={"Authorization": f"Bearer {req_tok}", "Accept": "application/json; api-version=2.0"},
157
+ )
158
+ with urllib.request.urlopen(req, timeout=30) as resp:
159
+ jwt = json.loads(resp.read().decode("utf-8"))["value"]
160
+ mint = urllib.request.Request(
161
+ "https://upload.pypi.org/_/oidc/mint-token",
162
+ data=json.dumps({"token": jwt}).encode("utf-8"),
163
+ headers={"Content-Type": "application/json", "Accept": "application/json"},
164
+ method="POST",
165
+ )
166
+ with urllib.request.urlopen(mint, timeout=30) as resp:
167
+ pypi_tok = json.loads(resp.read().decode("utf-8"))["token"]
168
+ import glob
169
+ pkgs = sorted(glob.glob("release-out/*.whl") + glob.glob("release-out/*.tar.gz"))
170
+ subprocess.run(
171
+ [sys.executable, "-m", "twine", "upload", "--non-interactive", "--skip-existing", "-u", "__token__", "-p", pypi_tok, *pkgs],
172
+ check=True,
173
+ )
174
+ PY
175
+ fi
@@ -0,0 +1,69 @@
1
+ name: RiffDB compatibility
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ schedule:
7
+ - cron: '23 4 * * *'
8
+ workflow_dispatch:
9
+ inputs:
10
+ riffdb_ref:
11
+ description: 'Full RiffDB commit or main; empty uses the adapter pin'
12
+ required: false
13
+ default: ''
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ concurrency:
19
+ group: riffdb-compatibility-${{ github.ref }}-${{ github.event_name }}
20
+ cancel-in-progress: true
21
+
22
+ jobs:
23
+ application-compatibility:
24
+ name: Application compatibility
25
+ runs-on: ubuntu-24.04
26
+ timeout-minutes: 30
27
+ env:
28
+ CARGO_INCREMENTAL: '0'
29
+ CARGO_TARGET_DIR: ${{ github.workspace }}/riffdb/target
30
+ steps:
31
+ - name: Check out adapter
32
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
33
+ with:
34
+ path: adapter
35
+ persist-credentials: false
36
+ - name: Select RiffDB revision
37
+ id: riffdb
38
+ env:
39
+ EVENT_NAME: ${{ github.event_name }}
40
+ REQUESTED_REF: ${{ inputs.riffdb_ref }}
41
+ run: |
42
+ revision="$(cat adapter/.github/riffdb-revision)"
43
+ [[ "$revision" =~ ^[0-9a-f]{40}$ ]]
44
+ if [[ "$EVENT_NAME" == schedule ]]; then revision=main; fi
45
+ if [[ -n "$REQUESTED_REF" ]]; then revision="$REQUESTED_REF"; fi
46
+ [[ "$revision" =~ ^([0-9a-f]{40}|main)$ ]]
47
+ printf 'revision=%s\n' "$revision" >>"$GITHUB_OUTPUT"
48
+ - name: Check out RiffDB
49
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
50
+ with:
51
+ repository: riffdb/riffdb
52
+ ref: ${{ steps.riffdb.outputs.revision }}
53
+ path: riffdb
54
+ persist-credentials: false
55
+ - name: Install Rust toolchain
56
+ run: rustup toolchain install 1.98.1 --profile minimal
57
+ - name: Cache Cargo sources
58
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
59
+ with:
60
+ path: |
61
+ ~/.cargo/registry
62
+ ~/.cargo/git
63
+ key: cargo-compatibility-${{ runner.os }}-${{ hashFiles('riffdb/Cargo.lock') }}
64
+ - name: Build selected RiffDB CLI
65
+ working-directory: riffdb
66
+ run: cargo build --locked --release -p riffdb-cli --bin riffdb
67
+ - name: Check adapter application
68
+ working-directory: adapter
69
+ run: ../riffdb/target/release/riffdb application check
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ .mypy_cache/
4
+ .ruff_cache/
5
+ __pycache__/
6
+ *.py[cod]
7
+ build/
8
+ dist/
9
+ *.egg-info/
@@ -0,0 +1,54 @@
1
+ # AGENTS.md — RiffDB LangGraph adapter
2
+
3
+ ## Mission
4
+
5
+ Implement a standalone Python checkpoint-saver adapter for LangGraph using a
6
+ compiled RiffDB application. All LangGraph-specific behavior belongs in this
7
+ repository, never in RiffDB.
8
+
9
+ ## Authoritative inputs
10
+
11
+ - `langgraph` (`>=1.1.0`), `langgraph-checkpoint` (`>=4.0.0`), and
12
+ `langgraph-checkpoint-conformance` (`>=0.1.0`), especially
13
+ `langgraph.checkpoint.base.BaseCheckpointSaver` and the upstream conformance
14
+ suite (`langgraph.checkpoint.conformance.validate`).
15
+ - This repository's `DESIGN.md`.
16
+ - The accepted RiffDB specification and ADRs at the revision recorded in the
17
+ generated application lock and `.github/riffdb-revision`.
18
+
19
+ If these inputs conflict, stop and request maintainer review. Do not weaken a
20
+ LangGraph checkpointer semantic or a RiffDB safety guarantee to make a test
21
+ pass.
22
+
23
+ ## Architecture boundaries
24
+
25
+ 1. LangGraph checkpoint mutations use generated, compiled RiffDB commands only.
26
+ 2. Reads use generated named RiffQL queries only. Do not add generic CRUD,
27
+ dynamically assembled RiffQL, or unbounded scans.
28
+ 3. Checkpoint metadata, channel version blobs, and pending task writes belong to
29
+ a single `Checkpoints` aggregate partitioned by `scope` with `conflict_key
30
+ (scope, thread_id)`.
31
+ 4. `put_writes` / `aput_writes` preserve upstream idempotency rules:
32
+ non-negative write indices (`write_idx >= 0`) are write-once per
33
+ `(thread_id, checkpoint_ns, checkpoint_id, task_id, write_idx)`, while
34
+ special negative indices from `WRITES_IDX_MAP` (`ERROR`, `SCHEDULED`,
35
+ `INTERRUPT`, `RESUME`) overwrite in place.
36
+ 5. `get_tuple` and `list` hydrate channel values from `CheckpointBlob` rows
37
+ referenced by `channel_versions`, order `pending_writes` via upstream
38
+ `writes_sort_key`, and migrate `v < 4` parent `TASKS` writes into
39
+ `channel_values[TASKS]`.
40
+ 6. Connection URIs (`riffdb://<database>`) carry only the database alias.
41
+ Credentials and TLS configuration come from protected environment variables,
42
+ never from the URI.
43
+
44
+ ## Completion checks
45
+
46
+ Before calling the adapter usable:
47
+
48
+ ```bash
49
+ make check
50
+ make conformance
51
+ ```
52
+
53
+ Unit fakes may supplement upstream tests but never replace real RiffDB loopback
54
+ and `langgraph-checkpoint-conformance` coverage.
@@ -0,0 +1,64 @@
1
+ # Design
2
+
3
+ ## Compatibility target
4
+
5
+ `riffdb-langgraph` implements `langgraph.checkpoint.base.BaseCheckpointSaver[str]`
6
+ for `langgraph>=1.1.0` and `langgraph-checkpoint>=4.0.0`, validated against all
7
+ eight capabilities of `langgraph-checkpoint-conformance`:
8
+
9
+ | Capability | Methods | Behavior |
10
+ |---|---|---|
11
+ | `put` | `put`, `aput` | Atomic checkpoint envelope + channel blob persistence, metadata normalization via `get_checkpoint_metadata` |
12
+ | `put_writes` | `put_writes`, `aput_writes` | Pending task writes with idempotent regular indices (`write_idx >= 0`) and overwritable special indices (`WRITES_IDX_MAP`) |
13
+ | `get_tuple` | `get_tuple`, `aget_tuple` | Latest or specific `checkpoint_id` lookup, channel blob hydration, `pending_writes` ordering via `writes_sort_key`, and `v < 4` `TASKS` migration |
14
+ | `list` | `list`, `alist` | Reverse-chronological (`checkpoint_id DESC`) checkpoint listing with `thread_id`, `checkpoint_ns`, `before`, `limit`, and metadata filtering |
15
+ | `delete_thread` | `delete_thread`, `adelete_thread` | Removal of all checkpoints, channel blobs, and pending writes across all namespaces for a `thread_id` |
16
+ | `delete_for_runs` | `delete_for_runs`, `adelete_for_runs` | Removal of checkpoints (and their pending writes) whose metadata `run_id` matches the target run IDs |
17
+ | `copy_thread` | `copy_thread`, `acopy_thread` | Cloning all checkpoints, channel blobs, and pending writes across all namespaces from a source `thread_id` to a target `thread_id` |
18
+ | `prune` | `prune`, `aprune` | Pruning checkpoints per `(thread_id, checkpoint_ns)` using `keep_latest` or `delete` strategies |
19
+
20
+ The Python floor is 3.13, matching the RiffDB Python application runtime.
21
+
22
+ ## Contract and aggregate model
23
+
24
+ The contract (`riffdb/contract.riff`) defines a single aggregate `Checkpoints`
25
+ partitioned by `scope` with `conflict_key (scope, thread_id)`:
26
+
27
+ - **`Checkpoint`** (root): keyed by `(scope, thread_id, checkpoint_ns, checkpoint_id)`.
28
+ Stores `parent_checkpoint_id`, typed serde payloads for the stripped checkpoint
29
+ envelope (`channel_values` removed) and normalized `CheckpointMetadata`, and a
30
+ monotonic `revision`. Indexed with `binary_utf8_v1` text keys for reverse
31
+ lexicographic `checkpoint_id` range queries (`< $before_checkpoint_id`).
32
+ - **`CheckpointBlob`** (child): keyed by
33
+ `(scope, thread_id, checkpoint_ns, checkpoint_id, blob_key)` with
34
+ `checkpoint_id = ""` and `blob_key = "{len(channel)}:{channel}:{version}"`.
35
+ Deduplicates serialized channel values across checkpoints within a
36
+ `(thread_id, checkpoint_ns)`. Empty channel versions are recorded with
37
+ `serde_type = "empty"` so removed/unwritten channels are omitted on hydration.
38
+ - **`CheckpointWrite`** (child): keyed by
39
+ `(scope, thread_id, checkpoint_ns, checkpoint_id, task_id, write_idx)`.
40
+ Stores pending task writes with `initialized: bool` so `PutCheckpointWrites`
41
+ enforces write-once semantics when `write_idx >= 0` (`allow_overwrite == false`)
42
+ and overwrites when `write_idx < 0` (`allow_overwrite == true`).
43
+
44
+ ## Configuration
45
+
46
+ `RiffDBCheckpointSaver` accepts a canonical URI `riffdb://<database>`. It
47
+ contains no credential or TLS material. Connection authority comes from
48
+ protected process configuration:
49
+
50
+ - `RIFFDB_LANGGRAPH_ENDPOINT` (or fallback `RIFFDB_ENDPOINT`)
51
+ - `RIFFDB_LANGGRAPH_CREDENTIAL_FILE` (or fallback `RIFFDB_CREDENTIAL_FILE`)
52
+ - `RIFFDB_LANGGRAPH_TRUST_ROOT` (optional, for verified TLS)
53
+ - `RIFFDB_LANGGRAPH_SERVER_NAME` (optional, for verified TLS)
54
+
55
+ The credential file is loaded via `BearerCredential.from_protected_file`.
56
+ Unknown URI schemes, userinfo, ports, paths, query parameters, and fragments
57
+ raise `InvalidCheckpointerURI` before any network activity.
58
+
59
+ ## Session consistency
60
+
61
+ `RiffDBCheckpointSaver` tracks the highest `commit_sequence` returned by any
62
+ mutation command and attaches `read_after_commit` to subsequent queries on the
63
+ same saver instance, guaranteeing monotonic read-your-writes consistency across
64
+ both sync and async execution paths.
@@ -0,0 +1,26 @@
1
+ UV ?= uv
2
+ PYTHON ?= $(UV) run python
3
+ RIFFDB ?= riffdb
4
+
5
+ .PHONY: bootstrap format lint typecheck test check conformance
6
+
7
+ bootstrap:
8
+ $(UV) sync
9
+
10
+ format:
11
+ $(UV) run ruff format --check .
12
+
13
+ lint:
14
+ $(UV) run ruff check .
15
+
16
+ typecheck:
17
+ $(UV) run mypy
18
+
19
+ test:
20
+ $(UV) run pytest
21
+
22
+ check: format lint typecheck test
23
+ git diff --exit-code
24
+
25
+ conformance:
26
+ $(RIFFDB) dev --reset --exec '$(PYTHON) -m riffdb_langgraph "$$RIFFDB_ENDPOINT" "$$RIFFDB_CREDENTIAL_FILE" "$$RIFFDB_DATABASE"'
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.5
2
+ Name: riffdb-langgraph
3
+ Version: 0.2.0
4
+ Summary: Compiled RiffDB checkpoint-saver adapter for LangGraph
5
+ Author: RiffDB contributors
6
+ License: MIT OR Apache-2.0
7
+ Requires-Python: >=3.13
8
+ Requires-Dist: langgraph-checkpoint>=3.0.0
9
+ Requires-Dist: riffdb==0.2.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # riffdb-langgraph
13
+
14
+ `riffdb-langgraph` is a compiled RiffDB checkpoint-saver adapter
15
+ (`RiffDBCheckpointSaver`) for LangGraph (`langgraph>=1.1.0`,
16
+ `langgraph-checkpoint>=4.0.0`). It is an out-of-tree integration: no
17
+ LangGraph-specific code or vocabulary belongs in the RiffDB repository.
18
+
19
+ The adapter implements both synchronous and asynchronous `BaseCheckpointSaver`
20
+ surfaces and passes all eight capability suites (81 tests) in the upstream
21
+ `langgraph-checkpoint-conformance` package:
22
+
23
+ - `put` (`put`, `aput`)
24
+ - `put_writes` (`put_writes`, `aput_writes`)
25
+ - `get_tuple` (`get_tuple`, `aget_tuple`)
26
+ - `list` (`list`, `alist`)
27
+ - `delete_thread` (`delete_thread`, `adelete_thread`)
28
+ - `delete_for_runs` (`delete_for_runs`, `adelete_for_runs`)
29
+ - `copy_thread` (`copy_thread`, `acopy_thread`)
30
+ - `prune` (`prune`, `aprune`)
31
+
32
+ ## Usage
33
+
34
+ ```python
35
+ from langgraph.graph import START, END, StateGraph
36
+ from riffdb_langgraph import RiffDBCheckpointSaver
37
+
38
+ with RiffDBCheckpointSaver("riffdb://langgraph-development") as checkpointer:
39
+ graph = builder.compile(checkpointer=checkpointer)
40
+ result = graph.invoke(
41
+ {"messages": ["hello"]},
42
+ config={"configurable": {"thread_id": "thread-1"}},
43
+ )
44
+ ```
45
+
46
+ RiffDB TLS and credential material is supplied through protected environment
47
+ configuration (`RIFFDB_LANGGRAPH_ENDPOINT` and
48
+ `RIFFDB_LANGGRAPH_CREDENTIAL_FILE`) described in [DESIGN.md](DESIGN.md); it is
49
+ never embedded in the checkpointer URI.
50
+
51
+ ## Development and Verification
52
+
53
+ ```bash
54
+ make check
55
+ make conformance
56
+ ```
57
+
58
+ `make conformance` starts a disposable `riffdb dev` instance, runs sync and
59
+ async `StateGraph` fan-out smoke tests, and executes the full
60
+ `langgraph-checkpoint-conformance` validation suite against the live server.
61
+
62
+ ## RiffDB compatibility CI
63
+
64
+ This adapter owns its RiffDB compatibility checks. Pull requests compile the
65
+ application sources and verify existing locks and generated artifacts against
66
+ the full commit in `.github/riffdb-revision`. Scheduled checks use RiffDB
67
+ `main`; manual dispatch accepts a full commit or `main`. Failures are reported
68
+ here and do not block RiffDB itself.
69
+
70
+ To upgrade, update the pin, migrate sources as needed, review the result of
71
+ `riffdb application lock --write`, and verify with `riffdb application check`.
72
+ Commit the pin and migration together in this repository.
@@ -0,0 +1,61 @@
1
+ # riffdb-langgraph
2
+
3
+ `riffdb-langgraph` is a compiled RiffDB checkpoint-saver adapter
4
+ (`RiffDBCheckpointSaver`) for LangGraph (`langgraph>=1.1.0`,
5
+ `langgraph-checkpoint>=4.0.0`). It is an out-of-tree integration: no
6
+ LangGraph-specific code or vocabulary belongs in the RiffDB repository.
7
+
8
+ The adapter implements both synchronous and asynchronous `BaseCheckpointSaver`
9
+ surfaces and passes all eight capability suites (81 tests) in the upstream
10
+ `langgraph-checkpoint-conformance` package:
11
+
12
+ - `put` (`put`, `aput`)
13
+ - `put_writes` (`put_writes`, `aput_writes`)
14
+ - `get_tuple` (`get_tuple`, `aget_tuple`)
15
+ - `list` (`list`, `alist`)
16
+ - `delete_thread` (`delete_thread`, `adelete_thread`)
17
+ - `delete_for_runs` (`delete_for_runs`, `adelete_for_runs`)
18
+ - `copy_thread` (`copy_thread`, `acopy_thread`)
19
+ - `prune` (`prune`, `aprune`)
20
+
21
+ ## Usage
22
+
23
+ ```python
24
+ from langgraph.graph import START, END, StateGraph
25
+ from riffdb_langgraph import RiffDBCheckpointSaver
26
+
27
+ with RiffDBCheckpointSaver("riffdb://langgraph-development") as checkpointer:
28
+ graph = builder.compile(checkpointer=checkpointer)
29
+ result = graph.invoke(
30
+ {"messages": ["hello"]},
31
+ config={"configurable": {"thread_id": "thread-1"}},
32
+ )
33
+ ```
34
+
35
+ RiffDB TLS and credential material is supplied through protected environment
36
+ configuration (`RIFFDB_LANGGRAPH_ENDPOINT` and
37
+ `RIFFDB_LANGGRAPH_CREDENTIAL_FILE`) described in [DESIGN.md](DESIGN.md); it is
38
+ never embedded in the checkpointer URI.
39
+
40
+ ## Development and Verification
41
+
42
+ ```bash
43
+ make check
44
+ make conformance
45
+ ```
46
+
47
+ `make conformance` starts a disposable `riffdb dev` instance, runs sync and
48
+ async `StateGraph` fan-out smoke tests, and executes the full
49
+ `langgraph-checkpoint-conformance` validation suite against the live server.
50
+
51
+ ## RiffDB compatibility CI
52
+
53
+ This adapter owns its RiffDB compatibility checks. Pull requests compile the
54
+ application sources and verify existing locks and generated artifacts against
55
+ the full commit in `.github/riffdb-revision`. Scheduled checks use RiffDB
56
+ `main`; manual dispatch accepts a full commit or `main`. Failures are reported
57
+ here and do not block RiffDB itself.
58
+
59
+ To upgrade, update the pin, migrate sources as needed, review the result of
60
+ `riffdb application lock --write`, and verify with `riffdb application check`.
61
+ Commit the pin and migration together in this repository.
@@ -0,0 +1 @@
1
+ {"application":"riffdb-langgraph","contract":{"bundle_hash":"b2da9e908d1987b893189d9d470d553edcf05ec700ac3657860ff858b580bcfe","lineage":"RiffDBLangGraph","source":"riffdb/contract.riff","version":1},"generation":{"python":"src/riffdb_langgraph/generated/client.py"},"query_modules":[{"module_hash":"031908018e6cd7507ca0e0f81b8375673f1707c57742af8e964e2b618d185e75","name":"riffdb_langgraph","queries":[{"name":"GetCheckpointBlobs","source":"riffdb/queries/get_checkpoint_blobs.riffq"},{"name":"GetCheckpointById","source":"riffdb/queries/get_checkpoint_by_id.riffq"},{"name":"GetLatestCheckpoint","source":"riffdb/queries/get_latest_checkpoint.riffq"},{"name":"GetParentPendingSends","source":"riffdb/queries/get_parent_pending_sends.riffq"},{"name":"ListCheckpointsByScope","source":"riffdb/queries/list_checkpoints_by_scope.riffq"},{"name":"ListCheckpointsByScopeBefore","source":"riffdb/queries/list_checkpoints_by_scope_before.riffq"},{"name":"ListCheckpointsByThread","source":"riffdb/queries/list_checkpoints_by_thread.riffq"},{"name":"ListCheckpointsByThreadBefore","source":"riffdb/queries/list_checkpoints_by_thread_before.riffq"},{"name":"ListCheckpointsByThreadNs","source":"riffdb/queries/list_checkpoints_by_thread_ns.riffq"},{"name":"ListCheckpointsByThreadNsBefore","source":"riffdb/queries/list_checkpoints_by_thread_ns_before.riffq"},{"name":"ListThreadBlobKeys","source":"riffdb/queries/list_thread_blob_keys.riffq"},{"name":"ListThreadCheckpointKeys","source":"riffdb/queries/list_thread_checkpoint_keys.riffq"},{"name":"ListThreadWriteKeys","source":"riffdb/queries/list_thread_write_keys.riffq"}],"version":1}],"reactive_modules":[],"roles":[{"agent_subscriptions":[],"commands":["DeleteCheckpointBlobs","DeleteCheckpointWrites","DeleteCheckpoints","PutCheckpoint","PutCheckpointBlobs","PutCheckpointWithBlobs","PutCheckpointWrites"],"environment":"development","event_streams":[],"name":"LangGraphCheckpointer","queries":["GetCheckpointBlobs","GetCheckpointById","GetLatestCheckpoint","GetParentPendingSends","ListCheckpointsByScope","ListCheckpointsByScopeBefore","ListCheckpointsByThread","ListCheckpointsByThreadBefore","ListCheckpointsByThreadNs","ListCheckpointsByThreadNsBefore","ListThreadBlobKeys","ListThreadCheckpointKeys","ListThreadWriteKeys"],"row_policies":[],"tenant_scope":"global","watch_queries":[]}],"schema":"riffdb.application-manifest/v5","seed_inputs":[]}
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27,<2"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "riffdb-langgraph"
7
+ version = "0.2.0"
8
+ description = "Compiled RiffDB checkpoint-saver adapter for LangGraph"
9
+ readme = "README.md"
10
+ requires-python = ">=3.13"
11
+ license = { text = "MIT OR Apache-2.0" }
12
+ authors = [{ name = "RiffDB contributors" }]
13
+ dependencies = [
14
+ "langgraph-checkpoint>=3.0.0",
15
+ "riffdb==0.2.0",
16
+ ]
17
+
18
+ [dependency-groups]
19
+ dev = [
20
+ "langgraph>=1.0.0",
21
+ "langgraph-checkpoint-conformance",
22
+ "mypy>=1.17,<2",
23
+ "pytest>=8.4,<9",
24
+ "pytest-asyncio>=1.0",
25
+ "ruff>=0.12,<0.13",
26
+ ]
27
+
28
+ [tool.hatch.build.targets.wheel]
29
+ packages = ["src/riffdb_langgraph"]
30
+
31
+ [tool.pytest.ini_options]
32
+ addopts = "-ra"
33
+ testpaths = ["tests"]
34
+ asyncio_mode = "auto"
35
+
36
+ [tool.ruff]
37
+ line-length = 100
38
+ target-version = "py313"
39
+ extend-exclude = ["src/riffdb_langgraph/generated/client.py"]
40
+
41
+ [tool.ruff.lint]
42
+ select = ["E", "F", "I", "B", "UP", "RUF"]
43
+
44
+ [tool.mypy]
45
+ python_version = "3.13"
46
+ strict = true
47
+ packages = ["riffdb_langgraph"]
48
+ exclude = ["src/riffdb_langgraph/generated/client.py"]
49
+
50
+ [[tool.mypy.overrides]]
51
+ module = [
52
+ "langgraph.*",
53
+ "langgraph_checkpoint_conformance.*",
54
+ ]
55
+ ignore_missing_imports = true