## Summary Adds a sqlite-specific override of `BaseCheckpointSaver.get_delta_channel_history` (and async). Before this PR, `SqliteSaver` / `AsyncSqliteSaver` inherited the default impl, which calls `get_tuple` once per ancestor — N round-trips, full pending-writes fetch per step regardless of channel relevance. The override mirrors the postgres two-stage shape (ancestor walk + per-channel UNION ALL writes fetch) but adapted for sqlite: - **No JSONB** → stage 1 streams the cursor row-by-row in `checkpoint_id` DESC order. The merged walk advances one row at a time, deserializing only on-path checkpoints and dropping each before advancing — peak in-flight is one deserialized checkpoint, no `fetchall()` materialization. - **No separate blob table** → `channel_values` lives inline in the checkpoint blob, so seeds come back from stage 1 with no second fetch. - **Single merged walk (not K independent walks)**: each visited cid is deserialized exactly once, regardless of how many channels are still seeking their seed. - **Stage 2** stays per-channel UNION ALL to avoid over-fetching writes when channels have different chain depths — same rationale as postgres. `AsyncSqliteSaver.get_delta_channel_history` bridges to its async form via `run_coroutine_threadsafe`, matching the same cross-thread guard used by `get_tuple` / `delete_thread`. ## Tests - New `tests/test_delta_channel_migration.py`: covers the `BinaryOperatorAggregate -> DeltaChannel` migration path on sqlite (sync round-trip, sync continuation with post-migration delta folding, async round-trip). Mirrors `libs/langgraph/tests/test_delta_channel_migration.py` (which covered `InMemorySaver`); without these, the override's behavior on pre-migration threads was unverified — the override has to identify a plain accumulated `channel_values[ch]` at a pre-migration ancestor as a valid `seed`, not just `_DeltaSnapshot` sentinels. - Existing `tests/test_get_delta_channel_history.py` (7 tests) continues to pass and now exercises the optimized override end-to-end (previously hit the inherited default impl). - `make format`, `make lint`, `make test`: clean. 97/97 in the non-flaky sqlite suite (the one ignored test, `test_async_asearch_refresh_ttl`, is a known TTL-store timing flake on a separate module unrelated to this PR). ## Benchmarks ### `get_delta_channel_history` micro-bench (override vs inherited default impl) 1000-turn synthetic threads with sentinel snapshots + per-step writes; `bench_sqlite_delta_history.py`. Per-call latency in microseconds. | Scenario | min | median | mean | |---|---:|---:|---:| | S1 single channel, root-only snapshot | **4.60x** | **4.90x** | **5.13x** | | S2 mixed cadence (every-50 + root-only), 2 channels | **6.08x** | **6.37x** | **6.84x** | | S3 K=8 channels, root-only snapshot | 1.23x | 1.27x | 0.90x | S2 wins biggest because per-channel UNION ALL avoids over-fetching writes for the shallow channel. S3 is the worst case for sqlite (8 channels all walking to root, 1000 deserializations either way) — the override still wins on min/median. ### Long-running thread mem/storage bench (delta vs no-delta) `bench_sqlite_delta_memory.py`. `delta` mode uses `DeltaChannel` + the override; `no_delta` uses `Annotated[list, _messages_delta_reducer]` (full state in every blob). Same workload, file-backed sqlite. Latency measured untraced (30 iterations); peak heap measured separately under tracemalloc. | Scenario | Turns | Storage Δ | Peak heap Δ | Read latency Δ | |---|---:|---|---|---| | K=1, freq=50 | 200 | **-96%** (942 KB vs 25.1 MB) | +21% (504 KB vs 418 KB) | **+13%** | | K=1, freq=50 | 500 | **-98%** (2.9 MB vs 152.3 MB) | +20% (1.2 MB vs 1.0 MB) | **-6%** (delta wins) | | K=3, freq=50 uniform | 200 | **-98%** (1.7 MB vs 73.5 MB) | +7% (1.3 MB vs 1.2 MB) | **+10%** | | K=3, freq=50 uniform | 500 | **-99%** (6.0 MB vs 452.5 MB) | +7% (3.3 MB vs 3.0 MB) | **+6%** | | K=3, freq=mixed | 200 | **-98%** (1.4 MB vs 73.5 MB) | +5% (1.3 MB vs 1.2 MB) | +190% (5.1 ms vs 1.7 ms abs) | | K=3, freq=mixed | 500 | **-99%** (4.1 MB vs 452.5 MB) | +8% (3.3 MB vs 3.0 MB) | +377% (20.9 ms vs 4.4 ms abs) | - **Storage**: -96 to -99% on long threads (a 500-turn K=3 thread shrinks from 452 MB to 6 MB on disk). This is the headline win. - **Peak heap**: within +5 to +21% of the no-delta path — the streaming cursor + merged walk + drop-after-deserialize keep peak in-flight at one checkpoint at a time. - **Read latency**: equivalent-ish (within ~15%) on uniform-cadence scenarios; at K=1/500 turns delta even wins by 6%. The mixed-cadence rows have one channel with `snapshot_frequency=1000` walking to root on a 500-turn thread — by configuration. Absolute mixed-delta latency is still 5-21 ms per read. Bench scripts (not committed; workspace-root convention matches other `bench_*.py` files): - `bench_sqlite_delta_history.py` - `bench_sqlite_delta_memory.py` ## Test plan - [x] `cd libs/checkpoint-sqlite && make format` clean - [x] `cd libs/checkpoint-sqlite && make lint` clean - [x] `cd libs/checkpoint-sqlite && make test` — 97 passed (1 known flake unrelated) - [x] `tests/test_get_delta_channel_history.py` — 7/7 (now exercises the override) - [x] `tests/test_delta_channel_migration.py` — 3/3 (new)
Low-level orchestration framework for building stateful agents.
Trusted by companies shaping the future of agents – including Klarna, Replit, Elastic, and more – LangGraph is a low-level orchestration framework for building, managing, and deploying long-running, stateful agents.
pip install -U langgraph
Tip
If you're looking to quickly build agents, check out Deep Agents — a higher-level package built on LangGraph for agents that can plan, use subagents, and leverage file systems for complex tasks.
For an equivalent JS/TS library, check out LangGraph.js and the JS docs.
Why use LangGraph?
LangGraph provides low-level supporting infrastructure for any long-running, stateful workflow or agent:
- Durable execution — Build agents that persist through failures and can run for extended periods, automatically resuming from exactly where they left off.
- Human-in-the-loop — Seamlessly incorporate human oversight by inspecting and modifying agent state at any point during execution.
- Comprehensive memory — Create truly stateful agents with both short-term working memory for ongoing reasoning and long-term persistent memory across sessions.
- Debugging with LangSmith — Gain deep visibility into complex agent behavior with visualization tools that trace execution paths, capture state transitions, and provide detailed runtime metrics.
- Production-ready deployment — Deploy sophisticated agent systems confidently with scalable infrastructure designed to handle the unique challenges of stateful, long-running workflows.
Tip
For developing, debugging, and deploying AI agents and LLM applications, see LangSmith.
LangGraph ecosystem
While LangGraph can be used standalone, it also integrates seamlessly with any LangChain product, giving developers a full suite of tools for building agents.
To improve your LLM application development, pair LangGraph with:
- Deep Agents – Build agents that can plan, use subagents, and leverage file systems for complex tasks.
- LangChain – Provides integrations and composable components to streamline LLM application development.
- LangSmith – Helpful for agent evals and observability. Debug poor-performing LLM app runs, evaluate agent trajectories, gain visibility in production, and improve performance over time.
- LangSmith Deployment – Deploy and scale agents effortlessly with a purpose-built deployment platform for long-running, stateful workflows. Discover, reuse, configure, and share agents across teams – and iterate quickly with visual prototyping in LangSmith Studio.
Documentation
- docs.langchain.com – Comprehensive documentation, including conceptual overviews and guides
- reference.langchain.com/python/langgraph – API reference docs for LangGraph packages
- LangGraph Quickstart – Get started building with LangGraph
- Chat LangChain – Chat with the LangChain documentation and get answers to your questions
Discussions: Visit the LangChain Forum to connect with the community and share all of your technical questions, ideas, and feedback.
Additional resources
- Guides – Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
- LangChain Academy – Learn the basics of LangGraph in our free, structured course.
- Case studies – Hear how industry leaders use LangGraph to ship AI applications at scale.
- Contributing Guide – Learn how to contribute to LangChain projects and find good first issues.
- Code of Conduct – Our community guidelines and standards for participation.
Acknowledgements
LangGraph is inspired by Pregel and Apache Beam. The public interface draws inspiration from NetworkX. LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.