fix(langgraph-checkpoint-sqlite): store channel deltas - #2408
fix(langgraph-checkpoint-sqlite): store channel deltas#2408Pragnyan Ramtha (pragnyanramtha) wants to merge 5 commits into
Conversation
🦋 Changeset detectedLatest commit: 4b8214c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@langchain/langgraph-checkpoint
@langchain/langgraph-checkpoint-mongodb
@langchain/langgraph-checkpoint-postgres
@langchain/langgraph-checkpoint-redis
@langchain/langgraph-checkpoint-sqlite
@langchain/langgraph-checkpoint-validation
create-langgraph
@langchain/langgraph-api
@langchain/langgraph-cli
@langchain/langgraph
@langchain/langgraph-cua
@langchain/langgraph-supervisor
@langchain/langgraph-swarm
@langchain/langgraph-ui
@langchain/langgraph-sdk
@langchain/angular
@langchain/react
@langchain/svelte
@langchain/vue
commit: |
f3e5154 to
7ae52ca
Compare
|
After the base refresh, the only red check I see is I checked the job log: the Vue test suite itself passed ( I tried to rerun the failed job/run, but GitHub rejected reruns without repository admin rights. The branch is current with |
Christian Bromann (christian-bromann)
left a comment
There was a problem hiding this comment.
SqliteSaver serializes the whole checkpoint (including channel_values) into the single checkpoint BLOB and getTuple/list return it verbatim with no parent merge. Filtering channel_values by newVersions at write time, without a version-keyed blob table to reconstruct from on read, means reading checkpoint N returns only the channels that changed in step N — every unchanged channel from prior steps is lost.
This breaks real graph state: channelsFromCheckpoint rebuilds regular (non-Delta) channels purely from checkpoint.channel_values (emptyChannels), so a dropped channel becomes fromCheckpoint(undefined) → empty channel on resume/time-travel/getState.
The correct pattern is already in checkpoint-postgres: _dumpCheckpoint strips channel_values, blobs are stored keyed by (thread_id, ns, channel, version), and _loadCheckpoint/_loadBlobs reconstruct the full channel_values on read via channel_versions. SQLite has none of this.
CI is green because the un-skipped test only stores independent checkpoints and asserts each one's own delta — it never chains checkpoints and reads a later one back to verify full reconstructed state.
Suggested fix: add a version-keyed checkpoint_blobs table, strip channel_values from the checkpoint blob, upsert changed channels in put, reconstruct in getTuple/list from channel_versions, and handle migration of existing inline-channel_values rows. Also extend the conformance test to chain checkpoints and assert the full state on read. As a stopgap, revert to storing full channel_values and keep the test skipped rather than ship lossy persistence.
(Minor: newVersions? is optional here but required in the base class / Postgres.)
Summary
newVersionsinSqliteSaver.putso SQLite checkpoint rows only store changedchannel_valuesnewVersions@langchain/langgraph-checkpoint-sqliteFixes #594.
Validation
pnpm --filter @langchain/langgraph-checkpoint build:internalpnpm --filter @langchain/langgraph-checkpoint-sqlite build:internalpnpm --filter @langchain/langgraph-checkpoint-sqlite testpnpm --filter @langchain/langgraph-checkpoint-validation exec vitest run src/tests/sqlite.spec.tspnpm exec oxfmt --check libs/checkpoint-sqlite/src/index.ts libs/checkpoint-validation/src/spec/put.ts .changeset/swift-sqlite-deltas.mdpnpm exec oxlint libs/checkpoint-sqlite/src/index.ts libs/checkpoint-validation/src/spec/put.tsgit diff --check