mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-26 11:35:02 +02:00
a79c8740f95e092535ef78a9f41f957d79d17d13
217
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
eae02c622d |
chore(deps): bump the minor-and-patch group in /libs/checkpoint with 4 updates (#8777)
Bumps the minor-and-patch group in /libs/checkpoint with 4 updates: [langchain-core](https://github.com/langchain-ai/langchain), [redis](https://github.com/redis/redis-py), [ruff](https://github.com/astral-sh/ruff) and [ty](https://github.com/astral-sh/ty). Updates `langchain-core` from 1.5.2 to 1.6.1 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/langchain-ai/langchain/releases">langchain-core's releases</a>.</em></p> <blockquote> <h2>langchain-core==1.6.1</h2> <p>Changes since langchain-core==1.6.0</p> <p>revert: release(core): 1.6.2 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39971">#39971</a>) release(core): 1.6.2 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39967">#39967</a>) fix(core): shore up indexing in genai v1 streaming content (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39964">#39964</a>) fix(core): make <code>StructuredTool</code> JSON-serializable (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39631">#39631</a>) chore(deps): bump minor and patch dependencies (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39869">#39869</a>) release(core): 1.6.1 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39832">#39832</a>) feat(core): propagate gateway information on error path (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39829">#39829</a>)</p> <h2>langchain-core==1.6.0</h2> <p>Changes since langchain-core==1.5.6</p> <p>release(core): 1.6.0 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39760">#39760</a>) fix(core): resolve postponed annotations in <code>StructuredTool._injected_args_keys</code> (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39602">#39602</a>) feat(core): add standard model exception types (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39538">#39538</a>) fix(core): allow deserializing <code>RunnablePick</code> (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39753">#39753</a>) fix(core): make <code>convert_to_openai_function</code> handle callables and non-dict mappings (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39750">#39750</a>) fix(core): make subprocess and temporary file tests portable on Windows (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39664">#39664</a>) fix(core): fail fast when tool schemas can't resolve forward refs during serialization (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39570">#39570</a>) test(core): avoid version-dependent runnable snapshots (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39705">#39705</a>) fix(core): require all nested properties for strict tool schemas (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39306">#39306</a>) fix(core): remove stale sync-stream <code>xfail</code> [closes <a href="https://redirect.github.com/langchain-ai/langchain/issues/39720">#39720</a>] (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39723">#39723</a>) perf(core): Lazily import transformers (<a href="https://redirect.github.com/langchain-ai/langchain/issues/38037">#38037</a>) fix(core): accept non-dict Mapping values in mustache templates (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39680">#39680</a>) docs(core): clarify <code>Runnable</code> pipe coercion [closes <a href="https://redirect.github.com/langchain-ai/langchain/issues/39075">#39075</a>] (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39707">#39707</a>) fix(core): finalize chain-group runs on <code>BaseException</code> (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39699">#39699</a>)</p> <h2>langchain-core==1.5.6</h2> <p>Changes since langchain-core==1.5.5</p> <p>chore(core): release 1.5.6 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39704">#39704</a>) feat(core): incorporate gateway metadata to traces (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39703">#39703</a>)</p> <h2>langchain-core==1.5.5</h2> <p>Changes since langchain-core==1.5.4</p> <p>release(core): 1.5.5 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39655">#39655</a>) fix(core): make abatch_iterate consistent with batch_iterate for None and zero size (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39367">#39367</a>) fix(core): respect pydantic aliases when validating tool inputs (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39572">#39572</a>) fix(core): issues in merging chunks (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39535">#39535</a>) fix(core): handle v1 base model validation in async path (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39576">#39576</a>) fix(core): handle tool descriptions for infer_schema=False (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39573">#39573</a>) fix(core): clear usage metadata callback on exceptions in context manager (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39616">#39616</a>) fix(core): handle falsy LLM and chat model caches (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39283">#39283</a>) chore(core): add httpx as an explicit dep (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39612">#39612</a>) fix(core): preserve non-str/non-dict items in <code>DictPromptTemplate</code> list values (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39588">#39588</a>) fix(core): raise ValueError when explicit tool_outputs length mismatches tool_calls in tool_example_to_messages (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39142">#39142</a>) fix(core): guard malformed Anthropic content blocks (<a href="https://redirect.github.com/langchain-ai/langchain/issues/38670">#38670</a>)</p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/langchain-ai/langchain/commit/4fe9d3062f4b68e2e472eb92decf369c93aebb46"><code>4fe9d30</code></a> chore(openai): fix tests (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39972">#39972</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/8fa38dc143faecec0287395bce4484af4b82254c"><code>8fa38dc</code></a> revert: release(core): 1.6.2 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39971">#39971</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/122030d79e944a5b55c33847d29bcfbe64488372"><code>122030d</code></a> release(core): 1.6.2 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39967">#39967</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/7d4b42b57235020e6f496fdfebab44c3ca1b1f5b"><code>7d4b42b</code></a> release(langchain): 1.3.18 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39966">#39966</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/4a66e355da07b8fad7d6cb8db99287fb4d7e6d83"><code>4a66e35</code></a> fix(langchain): preserve content-block shape in PIIMiddleware redaction (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39894">#39894</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/38e211359f0b65b480d9ee4c53f2385f8c249bb3"><code>38e2113</code></a> fix(core): shore up indexing in genai v1 streaming content (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39964">#39964</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/13b1b2feae476fdcebc0a285a723d5cbfed9df2e"><code>13b1b2f</code></a> fix(core): make <code>StructuredTool</code> JSON-serializable (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39631">#39631</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/3b3b308e6d956c9a670e41e66a468466fe6f8c26"><code>3b3b308</code></a> release(anthropic): 1.7.0 (<a href="https://redirect.github.com/langchain-ai/langchain/issues/39963">#39963</a>)</li> <li><a href="https://github.com/langchain-ai/langchain/commit/e3f6adfe1976a773704a20fef7bb03ac0309e0e4"><code>e3f6adf</code></a> feat(anthropic): support top-level param for skills via <code>container</code>; `updates...</li> <li><a href="https://github.com/langchain-ai/langchain/commit/c253f54b905f9648c50b723f01945f39a256d8aa"><code>c253f54</code></a> fix(openai): correct <code>reasoning_effort_levels</code> for gpt-5 and gpt-5.1 profiles...</li> <li>Additional commits viewable in <a href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.5.2...langchain-core==1.6.1">compare view</a></li> </ul> </details> <br /> Updates `redis` from 8.0.1 to 8.1.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/redis/redis-py/releases">redis's releases</a>.</em></p> <blockquote> <h2>8.1.0</h2> <h1>Changes</h1> <h2>✨ Highlights</h2> <h3>Async maintenance notifications</h3> <p>redis-py now supports server-pushed <strong>maintenance notifications</strong> in the asyncio stack for both standalone and cluster clients, bringing the async client to parity with the sync implementation. When a Redis deployment signals maintenance events (such as node migration or failover windows), the async client reacts through the maintenance-notifications handler (<code>redis/maint_notifications.py</code> and its async integration under <code>redis/asyncio/</code>), allowing applications to adapt connection handling during maintenance without downtime. (<a href="https://redirect.github.com/redis/redis-py/issues/4177">#4177</a>)</p> <h3>Expanded command coverage</h3> <p>This release adds a batch of new command surfaces across core data types and modules:</p> <ul> <li><strong>Lists</strong> — <code>LMOVEM</code> / <code>BLMOVEM</code> for moving multiple elements between lists (<a href="https://redirect.github.com/redis/redis-py/issues/4174">#4174</a>).</li> <li><strong>Sets</strong> — <code>SDIFFCARD</code> and <code>SUNIONCARD</code> cardinality commands (<a href="https://redirect.github.com/redis/redis-py/issues/4171">#4171</a>).</li> <li><strong>Streams</strong> — <code>MAXCOUNT</code> / <code>MAXSIZE</code> options for <code>XREAD</code> and <code>XREADGROUP</code> (<a href="https://redirect.github.com/redis/redis-py/issues/4173">#4173</a>).</li> <li><strong>Time Series</strong> — <code>TS.READ</code> (<a href="https://redirect.github.com/redis/redis-py/issues/4170">#4170</a>), <code>TS.QUERYLABELS</code> (<a href="https://redirect.github.com/redis/redis-py/issues/4197">#4197</a>), <code>TS.NRANGE</code> / <code>TS.NREVRANGE</code> (<a href="https://redirect.github.com/redis/redis-py/issues/4163">#4163</a>), and an <code>exclude_empty</code> (<code>EXCLUDEEMPTY</code>) option for <code>TS.MRANGE</code> / <code>TS.MREVRANGE</code> (<a href="https://redirect.github.com/redis/redis-py/issues/4188">#4188</a>).</li> <li><strong>Search</strong> — <code>FT.ALIASLIST</code> (<a href="https://redirect.github.com/redis/redis-py/issues/4198">#4198</a>) and a <code>COLLECT</code> reducer for aggregations (<a href="https://redirect.github.com/redis/redis-py/issues/4179">#4179</a>).</li> <li><strong>Sentinel</strong> — replica sentinel aliases (<a href="https://redirect.github.com/redis/redis-py/issues/4127">#4127</a>).</li> </ul> <h2>🚀 New Features</h2> <ul> <li>Add replica sentinel aliases (<a href="https://redirect.github.com/redis/redis-py/issues/4127">#4127</a>)</li> <li>Add TS.NRANGE and TS.NREVRANGE support to the timeseries command surface (<a href="https://redirect.github.com/redis/redis-py/issues/4163">#4163</a>)</li> <li>feat: add TS.READ command support to the timeseries module (<a href="https://redirect.github.com/redis/redis-py/issues/4170">#4170</a>)</li> <li>feat: add SDIFFCARD and SUNIONCARD command support (<a href="https://redirect.github.com/redis/redis-py/issues/4171">#4171</a>)</li> <li>feat: add MAXCOUNT/MAXSIZE support to XREAD and XREADGROUP (<a href="https://redirect.github.com/redis/redis-py/issues/4173">#4173</a>)</li> <li>feat: add LMOVEM and BLMOVEM commands for moving multiple list elements (<a href="https://redirect.github.com/redis/redis-py/issues/4174">#4174</a>)</li> <li>feat: add async maintenance-notifications support for standalone and cluster clients (<a href="https://redirect.github.com/redis/redis-py/issues/4177">#4177</a>)</li> <li>feat: add COLLECT reducer support to search aggregations (<a href="https://redirect.github.com/redis/redis-py/issues/4179">#4179</a>)</li> <li>feat: add exclude_empty (EXCLUDEEMPTY) option to TS.MRANGE and TS.MREVRANGE (<a href="https://redirect.github.com/redis/redis-py/issues/4188">#4188</a>)</li> <li>feat: add TS.QUERYLABELS support to the timeseries command surface (<a href="https://redirect.github.com/redis/redis-py/issues/4197">#4197</a>)</li> <li>feat: add FT.ALIASLIST support (<a href="https://redirect.github.com/redis/redis-py/issues/4198">#4198</a>)</li> </ul> <h2>⚠️ Experimental</h2> <ul> <li>feat: add client-side HIMPORT fieldset support for standalone and cluster clients (<a href="https://redirect.github.com/redis/redis-py/issues/4205">#4205</a>)</li> </ul> <p>The feature considered unstable and <strong>public API might be changed in the future minor version</strong></p> <h2>🐛 Bug Fixes</h2> <ul> <li>fix: detect closed pooled connection without consuming pending push data (RESP3 + hiredis) (<a href="https://redirect.github.com/redis/redis-py/issues/4156">#4156</a>)</li> <li>Fix Sentinel pool capacity loss after failover (<a href="https://redirect.github.com/redis/redis-py/issues/4193">#4193</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/redis/redis-py/commit/e013126df18e476fbb8b8215f040f39bd3242683"><code>e013126</code></a> Testing with 8.10 GA (<a href="https://redirect.github.com/redis/redis-py/issues/4227">#4227</a>)</li> <li><a href="https://github.com/redis/redis-py/commit/059d1e97d88dd7ed516628e0a9765ab8e0e299f1"><code>059d1e9</code></a> Fixed TS.NRANGE commands to correctly apply aggregators (<a href="https://redirect.github.com/redis/redis-py/issues/4225">#4225</a>)</li> <li><a href="https://github.com/redis/redis-py/commit/d486a0a982dcdf316006474dc286ca65cbec5a9f"><code>d486a0a</code></a> Fix FIELDNAME alias dropping first character of un-prefixed fields (<a href="https://redirect.github.com/redis/redis-py/issues/4224">#4224</a>)</li> <li><a href="https://github.com/redis/redis-py/commit/fb931042420baaa5fbb9021955e444f7afa66dc2"><code>fb93104</code></a> feat: add client-side HIMPORT fieldset support for standalone and cluster cli...</li> <li><a href="https://github.com/redis/redis-py/commit/9197609f48eb9713411a50534ca48210486a4bf9"><code>9197609</code></a> Fix lat/lon swap in search querystring geo() helper (<a href="https://redirect.github.com/redis/redis-py/issues/4223">#4223</a>)</li> <li><a href="https://github.com/redis/redis-py/commit/14714a0812d3ff03ad34c89ce6f79e287a04e9fb"><code>14714a0</code></a> fix: Fixed double decoding issue with unquote() (<a href="https://redirect.github.com/redis/redis-py/issues/4222">#4222</a>)</li> <li><a href="https://github.com/redis/redis-py/commit/227280a939718d09ff9d7c41f9785ffdc21bdc2a"><code>227280a</code></a> Update lib version to 8.1.0</li> <li><a href="https://github.com/redis/redis-py/commit/88d16d0db10c852d1d292628b2c832f1ed117056"><code>88d16d0</code></a> Decode ACL LOG string values on the default RESP3 legacy callback (<a href="https://redirect.github.com/redis/redis-py/issues/4201">#4201</a>)</li> <li><a href="https://github.com/redis/redis-py/commit/599fd75ca7d0745928280ff3a64ccd224dbf2b76"><code>599fd75</code></a> feat: add FT.ALIASLIST support (<a href="https://redirect.github.com/redis/redis-py/issues/4198">#4198</a>)</li> <li><a href="https://github.com/redis/redis-py/commit/71e275e6eae94d202b49bccce10ef0d2a59050e7"><code>71e275e</code></a> test: add VectorField RERANK serialization tests for sync and async search (#...</li> <li>Additional commits viewable in <a href="https://github.com/redis/redis-py/compare/v8.0.1...v8.1.0">compare view</a></li> </ul> </details> <br /> Updates `ruff` from 0.16.0 to 0.16.5 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ruff/releases">ruff's releases</a>.</em></p> <blockquote> <h2>0.16.5</h2> <h2>Release Notes</h2> <p>Released on 2026-08-27.</p> <h3>Preview features</h3> <ul> <li>Allow rules without codes (<a href="https://redirect.github.com/astral-sh/ruff/pull/28049">#28049</a>)</li> <li>Introduce category selectors (<a href="https://redirect.github.com/astral-sh/ruff/pull/27666">#27666</a>)</li> <li>Update preview default rules and categories (<a href="https://redirect.github.com/astral-sh/ruff/pull/27877">#27877</a>)</li> </ul> <h3>Bug fixes</h3> <ul> <li>[<code>flake8-async</code>] Detect blocking generic HTTP requests (<code>ASYNC210</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/28024">#28024</a>)</li> <li>[<code>flake8-datetimez</code>] Allow timezone-safe <code>strptime</code> chains (<code>DTZ007</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/28023">#28023</a>)</li> <li>[<code>flake8-simplify</code>] Respect side effects in <code>lambda</code> defaults (<code>SIM401</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/28000">#28000</a>)</li> </ul> <h3>Server</h3> <ul> <li>Fix duplicated "of" in <code>ClientOptions</code> doc comment (<a href="https://redirect.github.com/astral-sh/ruff/pull/27978">#27978</a>)</li> </ul> <h3>Documentation</h3> <ul> <li>Document rule acceptance guidelines (<a href="https://redirect.github.com/astral-sh/ruff/pull/27910">#27910</a>)</li> <li>Document the new category selectors (<a href="https://redirect.github.com/astral-sh/ruff/pull/27906">#27906</a>)</li> </ul> <h3>Contributors</h3> <ul> <li><a href="https://github.com/AlexWaygood"><code>@AlexWaygood</code></a></li> <li><a href="https://github.com/sharkdp"><code>@sharkdp</code></a></li> <li><a href="https://github.com/jelle-openai"><code>@jelle-openai</code></a></li> <li><a href="https://github.com/charliermarsh"><code>@charliermarsh</code></a></li> <li><a href="https://github.com/ntBre"><code>@ntBre</code></a></li> <li><a href="https://github.com/aarushkandukoori"><code>@aarushkandukoori</code></a></li> </ul> <h2>Install ruff 0.16.5</h2> <h3>Install prebuilt binaries via shell script</h3> <pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf https://releases.astral.sh/github/ruff/releases/download/0.16.5/ruff-installer.sh | sh </code></pre> <h3>Install prebuilt binaries via powershell script</h3> <pre lang="sh"><code>powershell -ExecutionPolicy Bypass -c "irm https://releases.astral.sh/github/ruff/releases/download/0.16.5/ruff-installer.ps1 | iex" </code></pre> <h2>Download ruff 0.16.5</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md">ruff's changelog</a>.</em></p> <blockquote> <h2>0.16.5</h2> <p>Released on 2026-08-27.</p> <h3>Preview features</h3> <ul> <li>Allow rules without codes (<a href="https://redirect.github.com/astral-sh/ruff/pull/28049">#28049</a>)</li> <li>Introduce category selectors (<a href="https://redirect.github.com/astral-sh/ruff/pull/27666">#27666</a>)</li> <li>Update preview default rules and categories (<a href="https://redirect.github.com/astral-sh/ruff/pull/27877">#27877</a>)</li> </ul> <h3>Bug fixes</h3> <ul> <li>[<code>flake8-async</code>] Detect blocking generic HTTP requests (<code>ASYNC210</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/28024">#28024</a>)</li> <li>[<code>flake8-datetimez</code>] Allow timezone-safe <code>strptime</code> chains (<code>DTZ007</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/28023">#28023</a>)</li> <li>[<code>flake8-simplify</code>] Respect side effects in <code>lambda</code> defaults (<code>SIM401</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/28000">#28000</a>)</li> </ul> <h3>Server</h3> <ul> <li>Fix duplicated "of" in <code>ClientOptions</code> doc comment (<a href="https://redirect.github.com/astral-sh/ruff/pull/27978">#27978</a>)</li> </ul> <h3>Documentation</h3> <ul> <li>Document rule acceptance guidelines (<a href="https://redirect.github.com/astral-sh/ruff/pull/27910">#27910</a>)</li> <li>Document the new category selectors (<a href="https://redirect.github.com/astral-sh/ruff/pull/27906">#27906</a>)</li> </ul> <h3>Contributors</h3> <ul> <li><a href="https://github.com/AlexWaygood"><code>@AlexWaygood</code></a></li> <li><a href="https://github.com/sharkdp"><code>@sharkdp</code></a></li> <li><a href="https://github.com/jelle-openai"><code>@jelle-openai</code></a></li> <li><a href="https://github.com/charliermarsh"><code>@charliermarsh</code></a></li> <li><a href="https://github.com/ntBre"><code>@ntBre</code></a></li> <li><a href="https://github.com/aarushkandukoori"><code>@aarushkandukoori</code></a></li> </ul> <h2>0.16.4</h2> <p>Released on 2026-08-20.</p> <h3>Preview features</h3> <ul> <li>[<code>flake8-use-pathlib</code>] Add autofix for <code>PTH116</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/26460">#26460</a>)</li> <li>[<code>refurb</code>] Restrict <code>delete-full-slice</code> to lists (<code>FURB131</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27711">#27711</a>)</li> <li>[<code>refurb</code>] Skip <code>FURB101</code> and <code>FURB103</code> when the <code>open</code> argument is a file descriptor (<a href="https://redirect.github.com/astral-sh/ruff/pull/27643">#27643</a>)</li> </ul> <h3>Bug fixes</h3> <ul> <li>Fix <code>InvalidInstruction</code> on Windows CPUs that do not support <code>POPCNT</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/27803">#27803</a>)</li> <li>[<code>pyflakes</code>] Emit semantic syntax errors in string type definitions as <code>F722</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/27835">#27835</a>)</li> <li>[<code>pylint</code>] Allow <code>os._exit</code> imports in <code>import-private-name</code> (<code>PLC2701</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27738">#27738</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/astral-sh/ruff/commit/9e4938c4a60bed3e87a11ee1e1db1bd23f4d964a"><code>9e4938c</code></a> Bump 0.16.5 (<a href="https://redirect.github.com/astral-sh/ruff/issues/28110">#28110</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/aad0e909ef1390f4b2a3ba8aa0a67fb8ea5cbacd"><code>aad0e90</code></a> Allow rules without codes (<a href="https://redirect.github.com/astral-sh/ruff/issues/28049">#28049</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/5fdab73c5052350400c36b08c5d7710210343bc4"><code>5fdab73</code></a> Update preview default rules and categories (<a href="https://redirect.github.com/astral-sh/ruff/issues/27877">#27877</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/29c8e5b2d0a46eb7dc4ff11c1b0a0dc5ccea52e4"><code>29c8e5b</code></a> Document rule acceptance guidelines (<a href="https://redirect.github.com/astral-sh/ruff/issues/27910">#27910</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/50a4d7fd106603a5616b01ac3bef3306252b248f"><code>50a4d7f</code></a> Document the new category selectors (<a href="https://redirect.github.com/astral-sh/ruff/issues/27906">#27906</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/ada87950ea188f882f69b7bd6e2213a9696e3ee2"><code>ada8795</code></a> Introduce category selectors (<a href="https://redirect.github.com/astral-sh/ruff/issues/27666">#27666</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/d8947238863b61922bfc83f07edcc697c1cc07c0"><code>d894723</code></a> [ty] Infer lambda parameters through callable type aliases (<a href="https://redirect.github.com/astral-sh/ruff/issues/28109">#28109</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/2685fdebbcf9938736fed8c45886a629f9c99a06"><code>2685fde</code></a> [ty] Narrow functional enum members in <code>==</code> and <code>match</code> (<a href="https://redirect.github.com/astral-sh/ruff/issues/28103">#28103</a>)</li> <li><a href="https://github.com/astral-sh/ruff/commit/efcffd2178ce62e9951a53d4c50cadc225a0cfec"><code>efcffd2</code></a> [ty] Intersection simplifications with subtype-related generic specialization...</li> <li><a href="https://github.com/astral-sh/ruff/commit/eb780488037504e11f145ed778654fd8a825028b"><code>eb78048</code></a> [ty] Bump ecosystem-analyzer for HTML escaping (<a href="https://redirect.github.com/astral-sh/ruff/issues/28104">#28104</a>)</li> <li>Additional commits viewable in <a href="https://github.com/astral-sh/ruff/compare/0.16.0...0.16.5">compare view</a></li> </ul> </details> <br /> Updates `ty` from 0.0.64 to 0.0.75 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ty/releases">ty's releases</a>.</em></p> <blockquote> <h2>0.0.75</h2> <h2>Release Notes</h2> <p>Released on 2026-08-26.</p> <h3>Preview features</h3> <ul> <li>Initialize PEP 723 script environments in the CLI (<a href="https://redirect.github.com/astral-sh/ruff/pull/27544">#27544</a>)</li> <li>Refresh PEP 723 script environments in watch mode (<a href="https://redirect.github.com/astral-sh/ruff/pull/27617">#27617</a>)</li> <li>Run PEP 723 script synchronization on bounded workers (<a href="https://redirect.github.com/astral-sh/ruff/pull/27615">#27615</a>)</li> </ul> <h3>Bug fixes</h3> <ul> <li>Specialize <code>Self</code> bounds of inherited methods (<a href="https://redirect.github.com/astral-sh/ruff/pull/27990">#27990</a>)</li> </ul> <h3>LSP server</h3> <ul> <li>Add "Go to Definition" support for pytest fixtures (<a href="https://redirect.github.com/astral-sh/ruff/pull/27444">#27444</a>)</li> </ul> <h3>Documentation</h3> <ul> <li>Fix documented Python package build command (<a href="https://redirect.github.com/astral-sh/ty/pull/4384">#4384</a>)</li> <li>Link untyped-call tracking issue in migration guide (<a href="https://redirect.github.com/astral-sh/ty/pull/4382">#4382</a>)</li> </ul> <h3>Library support</h3> <ul> <li>Resolve imported pytest fixture exposures (<a href="https://redirect.github.com/astral-sh/ruff/pull/27539">#27539</a>)</li> <li>Resolve installed core pytest fixture providers (<a href="https://redirect.github.com/astral-sh/ruff/pull/27770">#27770</a>)</li> <li>Resolve pytest fixtures through conftest (<a href="https://redirect.github.com/astral-sh/ruff/pull/27540">#27540</a>)</li> </ul> <h3>Diagnostics</h3> <ul> <li>Add more autofixes (<a href="https://redirect.github.com/astral-sh/ruff/pull/28029">#28029</a>)</li> <li>Add a dedicated missing-slot diagnostic (<a href="https://redirect.github.com/astral-sh/ruff/pull/28039">#28039</a>)</li> <li>Explain missing storage for declared slotted attributes (<a href="https://redirect.github.com/astral-sh/ruff/pull/27969">#27969</a>)</li> <li>Improve diagnostic spans for unpacked variable assignments (<a href="https://redirect.github.com/astral-sh/ruff/pull/28041">#28041</a>)</li> </ul> <h3>Core type checking</h3> <ul> <li>Account for known subclasses in equality inference (<a href="https://redirect.github.com/astral-sh/ruff/pull/28005">#28005</a>)</li> <li>Expand <code>ParamSpec</code> signatures inferred from bound receivers (<a href="https://redirect.github.com/astral-sh/ruff/pull/28020">#28020</a>)</li> <li>Fix <code>Self</code> binding in <code>ParamSpec</code> protocols (<a href="https://redirect.github.com/astral-sh/ruff/pull/28016">#28016</a>)</li> <li>Fix <code>TypedDict</code> variance inference (<a href="https://redirect.github.com/astral-sh/ruff/pull/28052">#28052</a>)</li> <li>Fix unsound narrowing through branch-assigned conditions (<a href="https://redirect.github.com/astral-sh/ruff/pull/28006">#28006</a>)</li> <li>Ignore inconsistent binding decorators on overloads (<a href="https://redirect.github.com/astral-sh/ruff/pull/28036">#28036</a>)</li> <li>Infer <code>yield from</code> send/return types from the iterator returned by <code>__iter__</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/27987">#27987</a>)</li> <li>Infer tuple type parameters from union arguments (<a href="https://redirect.github.com/astral-sh/ruff/pull/28062">#28062</a>)</li> <li>Infer variance through nonrecursive protocol references (<a href="https://redirect.github.com/astral-sh/ruff/pull/28065">#28065</a>)</li> <li>Preserve bounds of non-literal metaclasses (<a href="https://redirect.github.com/astral-sh/ruff/pull/28046">#28046</a>)</li> <li>Preserve correlated generic-call inference (<a href="https://redirect.github.com/astral-sh/ruff/pull/28043">#28043</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ty/blob/main/CHANGELOG.md">ty's changelog</a>.</em></p> <blockquote> <h2>0.0.75</h2> <p>Released on 2026-08-26.</p> <h3>Preview features</h3> <ul> <li>Initialize PEP 723 script environments in the CLI (<a href="https://redirect.github.com/astral-sh/ruff/pull/27544">#27544</a>)</li> <li>Refresh PEP 723 script environments in watch mode (<a href="https://redirect.github.com/astral-sh/ruff/pull/27617">#27617</a>)</li> <li>Run PEP 723 script synchronization on bounded workers (<a href="https://redirect.github.com/astral-sh/ruff/pull/27615">#27615</a>)</li> </ul> <h3>Bug fixes</h3> <ul> <li>Specialize <code>Self</code> bounds of inherited methods (<a href="https://redirect.github.com/astral-sh/ruff/pull/27990">#27990</a>)</li> </ul> <h3>LSP server</h3> <ul> <li>Add "Go to Definition" support for pytest fixtures (<a href="https://redirect.github.com/astral-sh/ruff/pull/27444">#27444</a>)</li> </ul> <h3>Documentation</h3> <ul> <li>Fix documented Python package build command (<a href="https://redirect.github.com/astral-sh/ty/pull/4384">#4384</a>)</li> <li>Link untyped-call tracking issue in migration guide (<a href="https://redirect.github.com/astral-sh/ty/pull/4382">#4382</a>)</li> </ul> <h3>Library support</h3> <ul> <li>Resolve imported pytest fixture exposures (<a href="https://redirect.github.com/astral-sh/ruff/pull/27539">#27539</a>)</li> <li>Resolve installed core pytest fixture providers (<a href="https://redirect.github.com/astral-sh/ruff/pull/27770">#27770</a>)</li> <li>Resolve pytest fixtures through conftest (<a href="https://redirect.github.com/astral-sh/ruff/pull/27540">#27540</a>)</li> </ul> <h3>Diagnostics</h3> <ul> <li>Add more autofixes (<a href="https://redirect.github.com/astral-sh/ruff/pull/28029">#28029</a>)</li> <li>Add a dedicated missing-slot diagnostic (<a href="https://redirect.github.com/astral-sh/ruff/pull/28039">#28039</a>)</li> <li>Explain missing storage for declared slotted attributes (<a href="https://redirect.github.com/astral-sh/ruff/pull/27969">#27969</a>)</li> <li>Improve diagnostic spans for unpacked variable assignments (<a href="https://redirect.github.com/astral-sh/ruff/pull/28041">#28041</a>)</li> </ul> <h3>Core type checking</h3> <ul> <li>Account for known subclasses in equality inference (<a href="https://redirect.github.com/astral-sh/ruff/pull/28005">#28005</a>)</li> <li>Expand <code>ParamSpec</code> signatures inferred from bound receivers (<a href="https://redirect.github.com/astral-sh/ruff/pull/28020">#28020</a>)</li> <li>Fix <code>Self</code> binding in <code>ParamSpec</code> protocols (<a href="https://redirect.github.com/astral-sh/ruff/pull/28016">#28016</a>)</li> <li>Fix <code>TypedDict</code> variance inference (<a href="https://redirect.github.com/astral-sh/ruff/pull/28052">#28052</a>)</li> <li>Fix unsound narrowing through branch-assigned conditions (<a href="https://redirect.github.com/astral-sh/ruff/pull/28006">#28006</a>)</li> <li>Ignore inconsistent binding decorators on overloads (<a href="https://redirect.github.com/astral-sh/ruff/pull/28036">#28036</a>)</li> <li>Infer <code>yield from</code> send/return types from the iterator returned by <code>__iter__</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/27987">#27987</a>)</li> <li>Infer tuple type parameters from union arguments (<a href="https://redirect.github.com/astral-sh/ruff/pull/28062">#28062</a>)</li> <li>Infer variance through nonrecursive protocol references (<a href="https://redirect.github.com/astral-sh/ruff/pull/28065">#28065</a>)</li> <li>Preserve bounds of non-literal metaclasses (<a href="https://redirect.github.com/astral-sh/ruff/pull/28046">#28046</a>)</li> <li>Preserve correlated generic-call inference (<a href="https://redirect.github.com/astral-sh/ruff/pull/28043">#28043</a>)</li> <li>Preserve invariant materialization constraints (<a href="https://redirect.github.com/astral-sh/ruff/pull/28047">#28047</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/astral-sh/ty/commit/a553dcc1a4c464b22d6fff8c46d444e326e0ae2d"><code>a553dcc</code></a> Bump version to 0.0.75 (<a href="https://redirect.github.com/astral-sh/ty/issues/4396">#4396</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/26b1b50998e92a5f50c342dc4a1fed84db2f8ee4"><code>26b1b50</code></a> Update prek dependencies (<a href="https://redirect.github.com/astral-sh/ty/issues/4392">#4392</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/73d226ee4d5a9274bc559c50a244159b61dfa315"><code>73d226e</code></a> Fix documented Python package build command (<a href="https://redirect.github.com/astral-sh/ty/issues/4384">#4384</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/5e264dac66970648d461a3fc8f2ebebd30a44dc9"><code>5e264da</code></a> Link untyped-call tracking issue in migration guide (<a href="https://redirect.github.com/astral-sh/ty/issues/4382">#4382</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/00199f0aaa6a8cd264fea08eae4f3c3fe4451c17"><code>00199f0</code></a> Bump version to 0.0.74 (<a href="https://redirect.github.com/astral-sh/ty/issues/4351">#4351</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/5769062fa10db84f6c03145bdf42d0b8da9aeb63"><code>5769062</code></a> Update mypy/pyright comparison table (<a href="https://redirect.github.com/astral-sh/ty/issues/4165">#4165</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/4fcb06010f2219b80adf3e688964abd9b0d72866"><code>4fcb060</code></a> Remove Zulip from the PGO training (<a href="https://redirect.github.com/astral-sh/ty/issues/4336">#4336</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/a7a706442a710db4a93c1a79779dff5ec5824cf6"><code>a7a7064</code></a> Use the versions bot's ruleset bypass when merging (<a href="https://redirect.github.com/astral-sh/ty/issues/4333">#4333</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/01b0fab0093c64535a401bc2731f80d7f5b6a7a8"><code>01b0fab</code></a> Update prek dependencies (<a href="https://redirect.github.com/astral-sh/ty/issues/4325">#4325</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/deef42f517bfbacb2ffe673046bef1e206c6b8ab"><code>deef42f</code></a> Update dependency python to 3.14 (<a href="https://redirect.github.com/astral-sh/ty/issues/4326">#4326</a>)</li> <li>Additional commits viewable in <a href="https://github.com/astral-sh/ty/compare/0.0.64...0.0.75">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Made by [Open SWE](https://openswe.vercel.app/agents/0758e158-6746-5095-b8a0-7f69acf36b73) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Kennedy <65985482+jkennedyvz@users.noreply.github.com> Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com> |
||
|
|
11ee185999 |
fix(checkpoint): widen Store put value type to Mapping[str, Any] (#8617)
TypedDict values don't structurally satisfy dict[str, Any] since dict implies full mutability. Mapping[str, Any] accepts both plain dicts and TypedDicts while still requiring string keys, matching what put() actually needs from callers. Fixes #8616 Verified by running lint/type/test locally across checkpoint, checkpoint-sqlite, checkpoint-postgres, prebuilt, sdk-py, and a scoped langgraph subset. LinkedIn: https://linkedin.com/in/lisandro-navarra --------- Co-authored-by: Mason Daugherty <github@mdrxy.com> |
||
|
|
a90ab44358 |
fix(checkpoint): collect writes at plain-value seed in delta channel history (#8526)
Fixes langchain-ai/langgraph#8384 `InMemorySaver.get_delta_channel_history` skipped the writes stored at the ancestor it seeded from whenever that ancestor's blob was a plain value rather than a `_DeltaSnapshot`, silently dropping the first write made after migrating a thread to `DeltaChannel`. ### Why the old rule was wrong A stored blob is the value *entering* its checkpoint; the writes stored under that same checkpoint are what produce its child. That's true for `_DeltaSnapshot` blobs and pre-delta plain values alike, so there was never a reason to treat them differently. Writes at ancestors *older* than the seed genuinely are subsumed by the seed value — but that's already guaranteed by terminating the walk, since the channel leaves `remaining` once its seed is found. The removed check re-solved that and overreached by one checkpoint. `BaseCheckpointSaver`, `SqliteSaver` and `PostgresSaver` never had this check. `InMemorySaver` was the only outlier. ### How I verified it Built a differential harness running the same migration scenarios through `InMemorySaver`, the `BaseCheckpointSaver` reference walk, and `SqliteSaver`. **4 of 11 scenarios agreed before this change; 11 of 11 after.** The loss is wider than one write — on the `add_messages` → `DeltaChannel` path it drops a real user message. Suites: `libs/checkpoint` 156 passed, `libs/langgraph` 1972 passed, `libs/checkpoint-sqlite` 117 passed, `libs/checkpoint-postgres` passed against PG 16. `make format`, `make lint` clean in each. ### Two things worth a closer look in review **1. I inverted two existing assertions** in `TestPreDeltaBlobTerminator` (`libs/checkpoint/tests/test_memory.py`). They encoded the old rule. Their fixture is the real migration shape — a plain-value blob carrying pending writes, with a delta-era child — which I confirmed against a dumped checkpoint chain from the issue's repro, so the assertions were wrong rather than the fixture being unrealistic. I added an ancestor *older* than the seed so the terminator still guards what it legitimately should: older writes stay excluded, the seed's own writes replay. **2. The new conformance test fails against Postgres**, for a reason unrelated to this change. Postgres `aput` leaves an inline `True` marker in `channel_values` only for `_DeltaSnapshot`; plain non-primitive values are popped with no marker, and seed detection is `(checkpoint -> 'channel_values' -> ch) IS NOT NULL`. So Postgres can't locate a plain-value seed at all: ``` seed stored as plain list: InMemorySaver -> [10, 20] AsyncPostgresSaver -> no seed key seed stored as _DeltaSnapshot: InMemorySaver -> found AsyncPostgresSaver -> found ``` The pre-existing `test_history_migration_plain_value_as_seed` already fails there too — conformance CI only validates `InMemorySaver`, so nobody was watching. Values still come out correct today (with no seed the walk runs to the root and replays everything), but early termination is lost: 1 write replayed on `InMemorySaver` vs 7 on Postgres for the same 6-turn thread. Filing separately rather than folding a write-path/format decision into this PR. ### Note on scope This touches three packages: the fix in `libs/checkpoint`, graph-level regression tests in `libs/langgraph` (the bug is only observable through a graph read), and the contract test in `libs/checkpoint-conformance` so third-party savers are covered too. --------- Co-authored-by: PiedPiper911 <32931126+PiedPiper911@users.noreply.github.com> |
||
|
|
9578140336 |
feat(checkpoint,checkpoint-postgres): add opt-in omit_expired to skip expired rows on read (#8354)
The Postgres store removes expired items only via the background TTL sweeper, so between sweeps a read can still return a logically expired row. Adds an opt-in flag so reads can filter expired rows at query time, closing the window without depending on sweep cadence. ## Changes - Add `omit_expired: bool` to `TTLConfig` (default `False`). When unset or `False`, behavior is unchanged - When enabled, inject `(expires_at IS NULL OR expires_at > NOW())` into the read query builders on `BasePostgresStore`, so `get`/`search`/`list_namespaces` do not surface an expired row: - **GET** (`_get_batch_GET_ops_queries`): predicate on both the final SELECT *and* the refresh `UPDATE` — the update is driven from an unfiltered key list, so gating only the SELECT would hide an expired row yet still refresh it back to life. - **SEARCH** (`_prepare_batch_search_queries`): inside the inner scans (before `LIMIT`/`OFFSET`), which also gates the refresh `UPDATE` fed by `search_results` and keeps pagination correct - **list_namespaces** (`_get_batch_list_namespaces_queries`): predicate appended to the existing scan conditions. ## Testing - Four behaviors covered sync + async, across the `default`/`pipe`/`pool` fixtures: expired-unswept row omitted from all read paths (with a raw SQL check proving the row is still physically present), default/explicit-`False` still returns it, `refresh_ttl=True` doesn't resurrect an expired row while still extending live ones, and search pagination stays correct when an expired row falls inside the page window. - Full `checkpoint-postgres` suite green on PG16 (210 passed); `ruff format`/`check` clean on both packages. |
||
|
|
346aa97425 |
fix(checkpoint): restrict lc:2 envelope revival to default constructor (#7892)
## Summary Restricts lc:2 JSON envelope revival in `JsonPlusSerializer` to the default constructor; the `method` field is now ignored. Adds a `logger.warning` when the default constructor raises so legacy payloads that previously fell back to `construct(**kwargs)` are observable instead of silently degrading. ## Test plan - [x] `make format` / `make lint` / `make test` from `libs/checkpoint` |
||
|
|
2e5025ec1a |
feat(checkpoint): force delta channel snapshot after max supersteps since last snapshot (#7746)
## Summary Add a system-wide upper bound on supersteps-since-last-snapshot for `DeltaChannel`, preventing unbounded ancestor walks on long-lived threads where a delta channel stops receiving writes. **Problem:** If a delta channel is written a few times (below `snapshot_frequency`) and then never written again, it is never snapshotted. Every subsequent run triggers an ancestor walk that grows linearly with thread length — on long threads this becomes catastrophic. **Solution:** Track a second counter (total supersteps) per delta channel alongside the existing update count. Force a snapshot when EITHER `updates >= snapshot_frequency` OR `supersteps >= DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT` (default 5000, overridable via env `LANGGRAPH_DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`). ### Changes - **`checkpoint` lib**: Rename metadata field `delta_updates_since_snapshot: dict[str, int]` -> `counters_since_last_snapshot: dict[str, tuple[int, int]]` where index 0 = updates, index 1 = supersteps. - **`langgraph/_internal/_config.py`**: Add `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT` constant with env override. - **`langgraph/pregel/_checkpoint.py`**: Update `delta_channels_to_snapshot()` predicate to fire on either threshold. Rename reader helper to `read_counters_since_last_snapshot()`. - **`langgraph/pregel/_loop.py`**: Iterate all delta channels each superstep (not just updated ones) to bump the supersteps counter. Reset both counters to `(0, 0)` on snapshot. - **Tests**: Updated existing exit-mode tests for new field shape. Added 4 new tests covering forced snapshot (single run + multi-run accumulation), predicate unit test, and counter reset. ## Test plan - [x] `test_delta_channel_supersteps_bound.py` — 4 new tests all pass - [x] `test_delta_channel_exit_mode.py` — 11 existing tests updated and pass - [x] `test_delta_channel_migration.py` — 11 tests pass - [x] `test_channels.py` — 29 tests pass - [x] `test_pregel.py` — 457 tests pass - [x] `libs/checkpoint` test suite — 151 pass, 16 skipped - [x] `make lint` clean (langgraph + checkpoint) |
||
|
|
dc0d992b90 | fix(checkpoint): specify allowed_objects in Reviver (#7743) | ||
|
|
fb6e5c2bce |
chore: remove keepset helper (#7745)
Comment follow up |
||
|
|
398d6cc59d |
chore(langgraph): add guide/conformance for delta channel checkpointer (#7736)
## Summary Add a user-facing design doc and `get_delta_channel_keepset` helper for third-party `BaseCheckpointSaver` authors who need to support graphs using `DeltaChannel`. **Deliverables:** 1. ~~**`docs/delta-channel-checkpointer-guide.md`** — comprehensive guide covering~~: moved to docs repo 2. **`BaseCheckpointSaver.get_delta_channel_keepset` / `aget_delta_channel_keepset`** — returns the minimum set of ancestor `checkpoint_id`s that must survive deletion for a given head's `DeltaChannel` reconstruction to remain intact. Enables safe `prune` implementations without silently corrupting delta history. 3. **Docstring warnings** on `prune`, `aprune`, `delete_for_runs`, `adelete_for_runs`, `copy_thread`, `acopy_thread` explaining the DeltaChannel pitfall (silent data loss if ancestor writes/snapshots are deleted). 4. **Three new conformance capabilities** in `libs/checkpoint-conformance`: - `delta_channel_history` — validates the `aget_delta_channel_history` walk contract - `delta_channel_keepset` — validates the keep-set contract - `delta_channel_reconstruction` — end-to-end round-trip (aput + aput_writes + history + reconstruct) ## Test plan - [x] `make format lint` passes in `libs/checkpoint`, `libs/checkpoint-conformance` - [x] All three new conformance capabilities pass against `InMemorySaver` - [x] Run conformance against SQLite saver - [x] Run conformance against Postgres saver --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
9032a3f90a |
docs(checkpoint): mark DeltaChannel and delta-history APIs as beta (#7732)
## Summary Adds a Beta admonition to the `DeltaChannel` surface area so users can distinguish stable from in-progress contracts. Single-sourced on the base class; subclass overrides in `checkpoint-postgres` / `checkpoint-sqlite` / memory inherit the marker via their existing references back to the base. Touched: - `DeltaChannel` (`libs/langgraph/langgraph/channels/delta.py`) - `BaseCheckpointSaver.get_delta_channel_history` and `aget_delta_channel_history` (`libs/checkpoint/.../base/__init__.py`) - `DeltaChannelHistory` TypedDict - `CheckpointMetadata.delta_updates_since_snapshot` ## Why docstring admonitions, not `@beta` The base `get_delta_channel_history` methods are designed to be overridden by savers. Wrapping with `@beta` (from `langchain_core._api`) would emit warnings whenever a subclass called `super().get_delta_channel_history(...)` or whenever the default ancestor walk fired. Docstring-only keeps the signal advisory and noise-free. `_DeltaSnapshot` is already leading-underscore-private, so it implicitly signals "internal." ## Versioning note This intentionally stays a **minor** bump for the checkpoint releases (4.0 → 4.1, 3.0 → 3.1): - The new methods are strictly additive — defaults provided on the base, no signatures changed, no removed APIs. Third-party savers keep working without overriding anything. - The beta marker and the version bump do orthogonal jobs: semver answers "is this a breaking change?" (no), the marker answers "is this contract stable?" (no). - Bumping major now would consume the lever you want available for when the delta contract actually changes in a breaking way. ## Test plan - [ ] Docstring-only — no behavioral change - [x] `make format && make lint` clean in `libs/checkpoint` and `libs/langgraph` |
||
|
|
127c6c2c7b |
chore: "chore: minor clean up around checkpoint and delta channel" (#7706)
Reverts langchain-ai/langgraph#7705 |
||
|
|
0ae8870432 | chore: minor clean up around checkpoint and delta channel (#7705) | ||
|
|
0a53c385b2 |
feat: public get_writes_history saver API + delta cadence rework (#7699)
## Summary
- Promotes the private K-channel batched ancestor-walk to a stable
public `get_delta_channel_history` / `aget_delta_channel_history` API on
`BaseCheckpointSaver` (returns `Mapping[str, DeltaChannelHistory]`, a
TypedDict with `writes` always present and `seed` `NotRequired`)
- Removes `DELTA_SENTINEL` / `_DeltaSentinel` entirely — the saver layer
is now delta-agnostic on both write and read paths
- Reworks `DeltaChannel` snapshot cadence from "every Nth superstep" to
"every N updates to this channel," persisted in
`CheckpointMetadata.delta_updates_since_snapshot`
- Adds Postgres optimizations: paged stage-1 with cursor (1024-row
pages) and per-channel UNION ALL stage-2 (no over-fetch when channels
have different chain depths)
- Default `snapshot_frequency` becomes a positive int (default `1000`);
the previous `None` opt-out is removed
## Public API
```python
class DeltaChannelHistory(TypedDict):
writes: list[PendingWrite] # always present, possibly empty
seed: NotRequired[Any] # absent if walk reached root
def get_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]: ...
async def aget_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]: ...
```
`config` and `channels` are keyword-only so later additions (e.g.
`page_size`) don't shift the positional API.
The TypedDict-with-`NotRequired[seed]` shape matches the existing
checkpoint-package convention (`CheckpointMetadata` is
`TypedDict(total=False)`) — absence-via-key-omission rather than
introducing a new sentinel. Pregel translates `"seed" not in hist` to
`MISSING` on its side at consume time.
The default impl walks `get_tuple` + `parent_config` correctly but is
slow on long chains; savers that care override (`InMemorySaver`,
`PostgresSaver`).
## Sentinel removal
`DELTA_SENTINEL` and `_DeltaSentinel` are deleted entirely. The saver
layer becomes delta-agnostic:
- `DeltaChannel.checkpoint()` returns `MISSING` for non-snapshot steps;
pregel's `create_checkpoint` skips MISSING so delta channels without a
snapshot simply don't appear in `channel_values`
- `InMemorySaver.put` and Postgres `put` no longer filter sentinels
(they have nothing to filter)
- `_needs_replay` becomes `stored is MISSING`
- `DeltaChannel.from_checkpoint` accepts: `MISSING` → empty,
`_DeltaSnapshot(value)` → snapshot value, plain value → pre-migration
legacy
## Snapshot cadence
`DeltaChannel.snapshot_frequency: int` (default `1000`, positive). The
previous `None` opt-out is gone.
```python
def should_snapshot(ch_name, ch):
if force_delta_snapshot: # durability="exit"
return True
return updates_since_snapshot.get(ch_name, 0) >= ch.snapshot_frequency
```
Per-channel update counters are persisted in
`CheckpointMetadata.delta_updates_since_snapshot` (`NotRequired`,
`total=False`). The counter is incremented by `_put_checkpoint` for any
delta channel in `updated_channels` and reset to `0` by
`create_checkpoint` for channels that fire a snapshot this step.
Version-format-independent — works for `int`, `float`, and `str`
versioning schemes alike.
## Postgres optimization
Two improvements internal to the override:
**Stage-1 paged with cursor** (`LIMIT 1024` internal const, `AND
checkpoint_id < ?` for subsequent pages). The previous unpaged form
scanned every checkpoint in `(thread_id, ns)` and was pathological at
high thread depths.
**Stage-2 per-channel UNION ALL**: one `WHERE channel='X' AND
checkpoint_id = ANY(chain_X)` branch per channel plus one seed-blob
branch per channel with a seed. The previous form filtered by `channel =
ANY(channels) AND checkpoint_id = ANY(union_chain_cids)`, over-fetching
writes when channels had different chain depths (`K ×
max(chain_lengths)` vs the correct `sum(chain_lengths)`).
Both improvements stay internal to `PostgresSaver`/`AsyncPostgresSaver`;
the public contract returns a single `Mapping`.
## Benchmarks
`libs/langgraph/tests/test_delta_channel_benchmark.py`. Run via `python
libs/langgraph/tests/test_delta_channel_benchmark.py`. Postgres against
local pg:5441.
Results below trimmed to the high-signal cells. Sub-millisecond /
sub-100-turn rows omitted as warmup-bound; freq=1 omitted (chain depth =
1, nothing to optimize); peak read-time memory and Postgres storage are
flat between branches and omitted. Deep-thread reads and the
cadence-rework storage win are the load-bearing numbers.
### Postgres reads, 500 turns
| Scenario | main | branch | Δ |
|---|---:|---:|---:|
| Single-channel deep read | 17.7 ms | **6.1 ms** | **-66%** |
| Single-channel, 1000 turns | 35.0 ms | **14.3 ms** | **-59%** |
| K=3 channels, freq=50 uniform | 70.5 ms | **41.4 ms** | **-41%** |
| K=8 channels, freq=50 uniform | 214.2 ms | **139.4 ms** | **-35%** |
| K=8 channels, mixed freq (25/50/100/.../1000) | 295.6 ms | **214.4
ms** | **-27%** |
K-channel batching + paged stage-1 + per-channel UNION ALL stage-2 doing
exactly what they should at depth.
### InMemory reads, 500 turns
| Scenario | main | branch | Δ |
|---|---:|---:|---:|
| Single-channel deep read | 7.9 ms | **3.8 ms** | **-52%** |
| Single-channel, 1000 turns | 15.6 ms | **7.2 ms** | **-54%** |
| K=8 channels, freq=50 uniform | 112.3 ms | 94.6 ms | -16% |
| K=8 channels, mixed freq | 184.9 ms | **134.5 ms** | **-27%** |
### InMemory storage, 500 turns (cadence-rework win)
| Scenario | main | branch | Δ |
|---|---:|---:|---:|
| K=3, freq=50 uniform | 8.7 MB | **3.3 MB** | **-62%** |
| K=3 mixed freq | 3.8 MB | **1.3 MB** | **-66%** |
| K=8, freq=50 uniform | 23.1 MB | **8.7 MB** | **-62%** |
| K=8 mixed freq | 11.5 MB | **4.2 MB** | **-64%** |
Snapshot frequency now counts **channel updates** instead of
**supersteps**. On graphs where supersteps outpace per-channel updates
(e.g., input/end steps that don't write to channels), branch stores ~3×
fewer snapshot blobs.
### Tradeoff worth flagging
InMemory K=3 with mixed frequencies (50/200/1000) at 500 turns: **+64%
read latency** (46.6 → 76.5 ms). The mixed scenario has a channel with
`freq=1000` that goes the entire 500-turn run with no snapshot. On main,
the old superstep-counted cadence happened to fire at step=500 anyway.
New cadence gives users explicit control over walk depth via
`snapshot_frequency`. The K=8 mixed case still wins overall (-27%); this
regression is specific to the K=3 mixed shape.
Default `snapshot_frequency=1000` is the upper bound on walk depth —
it's a tunable knob.
## Tests
- New sqlite smoke test (`test_get_delta_channel_history.py`) exercises
the inherited default `BaseCheckpointSaver` impl via `SqliteSaver` /
`AsyncSqliteSaver` end-to-end with a real `DeltaChannel`-backed graph.
Sqlite uses the default unchanged — this validates the default path
actually works on a real second saver, not just on the optimized
override.
- Module-level `pytest.importorskip("langgraph.channels.delta")` guards
the test for sqlite's standalone CI environment (matches the postgres
pattern).
## Test plan
- [x] `libs/checkpoint`: 150 passed, 16 skipped
- [x] `libs/langgraph` (channels + delta migration): 41/41 (post-merge)
- [x] `libs/langgraph` (full pregel suite): 1784 passing — 6 "failures"
verified via `env -i` clean shell are local LangSmith env vars + `git
describe revision_id` polluting LangChain metadata fixtures; CI is
unaffected
- [x] `libs/checkpoint-postgres`: 40/40 saver tests + 3/3 delta channel
reconstruction tests against local Postgres
- [x] `libs/checkpoint-sqlite`: 105/105 (incl. retry-passed flake
`test_ttl_refresh`, unrelated to this PR)
- [x] Lint clean across all four libs (`ruff format`, `ruff check`,
`mypy`)
- [x] Branch-vs-main benchmarks — see results above
---------
Co-authored-by: Quanzheng Long <long@langchain.dev>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
d8b7800183 |
chore(langgraph): use two phase read to avoid unnecessary data transport (#7660)
## Summary Replaces the single-roundtrip `UNION ALL` DeltaChannel read with a two-stage query that avoids fetching unused snapshot blobs, then removes the old combined path entirely. ### Problem `_get_channel_writes_history` used a single `UNION ALL` query that fetched **all** checkpoint metadata, writes, and blobs for a `(thread_id, channel)` in one shot. With `snapshot_frequency=N`, this pulled back O(N/freq) full-size snapshot blobs even though only the nearest one is needed to seed reconstruction. At 500 turns with `snapshot_frequency=10`, this meant fetching ~100 complete message-history snapshots per read. ### Solution Two-stage read: - **Stage 1** — lightweight scan of `checkpoints` only (no blob bytes): walks the parent chain from the target checkpoint and stops at the first ancestor with a snapshot, returning `chain_cids` and `seed_version` - **Stage 2** — targeted fetch: only the writes for `chain_cids` and the single seed blob at `seed_version` The two-stage path is now unconditional — the old combined query and `LG_DELTA_TWO_STAGE_QUERY` env-var gate have been removed. ### Sentinel cleanup `DELTA_SENTINEL` is now a pure in-memory signal and is never written to storage: - Postgres `put()` already stripped it from `channel_values` before writing blobs - Memory saver `put()` now stores `"empty"` instead of serializing the sentinel - `EXT_DELTA_SENTINEL` (msgpack ext code 8) removed from `JsonPlusSerializer` - `DELTA_SENTINEL` is kept as an in-memory marker: `DeltaChannel.checkpoint()` returns it so savers know to skip it, and `_ChannelWritesHistory.seed` uses it to mean "no snapshot found, start from empty" ## Performance Benchmarked at `snapshot_frequency=10` on Postgres (`~100 tok/msg`): | turns | old combined query | two-stage | |------:|-------------------:|----------:| | 50 | 6.0ms | 2.8ms (2.1x faster) | | 100 | 10.1ms | 5.6ms (1.8x faster) | | 500 | **216.1ms** | 15.3ms (**14x faster**) | The old query's read time grew super-linearly with turn count because each read fetched O(N/freq) full snapshot blobs. Two-stage keeps read depth bounded by `snapshot_frequency` regardless of thread length. ## Test plan - `make test` in `libs/checkpoint`, `libs/checkpoint-postgres`, `libs/langgraph` - Removed `test_delta_sentinel_serde_round_trip` (sentinel no longer serializable) - Updated `test_memory.py` — delta channel blobs stored as `"empty"`, not serialized sentinel - Updated `test_channels.py` — `channel_values` no longer contains sentinel key for DeltaChannels - Deleted `test_delta_channel_two_stage_benchmark.py` (one-stage vs two-stage comparison; path no longer exists) --------- Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
5c18bde0f8 |
feat(langgraph): DeltaChannel: store sentinel in blobs, reconstruct from checkpoint_writes (#7586)
# DeltaChannel: sentinel-based checkpoint blobs + write-replay
reconstruction
## Summary
`DeltaChannel` is a new fold-reducer channel that stores only a
zero-byte sentinel in checkpoint blobs instead of the full accumulated
value. On restore, the runtime replays ancestor writes through the
reducer to reconstruct state. For long-running threads with large
accumulating state (e.g. message histories), this delivers dramatically
smaller checkpoint blobs with configurable read-depth bounds.
```python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.channels.delta import DeltaChannel
from langgraph.graph.message import _messages_delta_reducer
class State(TypedDict):
# blob per step: ~60 bytes (sentinel) instead of growing full list
messages: Annotated[list, DeltaChannel(_messages_delta_reducer)]
# bound read depth to 10 steps via periodic snapshots
messages_bounded: Annotated[list, DeltaChannel(_messages_delta_reducer, snapshot_frequency=10)]
```
---
## Storage benchmarks (InMemory, ~400 char/msg)
**Messages blob storage** (`checkpoint_blobs` bytes for the messages
channel):
| turns | add\_messages | delta(inf) | delta(freq=50) | delta(freq=10) |
delta(freq=5) |
|------:|-------------:|-----------:|---------------:|---------------:|--------------:|
| 10 | 91.0 KB | 60 B (1517x) | 60 B (1517x) | 14.4 KB (6x) | 32.6 KB
(3x) |
| 50 | 2.20 MB | 300 B (7347x) | 67.1 KB (33x) | 423 KB (5x) | 864 KB
(3x) |
| 100 | 8.78 MB | 600 B (14636x) | 310 KB (28x) | 1.72 MB (5x) | 3.48 MB
(3x) |
| 250 | 54.80 MB | 1.5 KB (36536x) | 2.09 MB (26x) | 10.87 MB (5x) |
21.84 MB (3x) |
| 500 | 219.19 MB | 3.0 KB (73063x) | 8.56 MB (26x) | 43.67 MB (5x) |
87.50 MB (3x) |
**Total checkpoint storage** (blobs + writes + metadata):
| turns | add\_messages | delta(inf) | delta(freq=50) | delta(freq=10) |
delta(freq=5) |
|------:|-------------:|-----------:|---------------:|---------------:|--------------:|
| 10 | 129.7 KB | 38.7 KB (3.4x) | 38.7 KB (3.4x) | 53.1 KB (2.4x) |
71.2 KB (1.8x) |
| 50 | 2.40 MB | 196 KB (12x) | 263 KB (9x) | 620 KB (3.9x) | 1.06 MB
(2.3x) |
| 100 | 9.18 MB | 394 KB (23x) | 703 KB (13x) | 2.12 MB (4.3x) | 3.87 MB
(2.4x) |
| 250 | 55.79 MB | 987 KB (57x) | 3.07 MB (18x) | 11.86 MB (4.7x) |
22.82 MB (2.4x) |
| 500 | 221.16 MB | 1.98 MB (112x) | 10.53 MB (21x) | 45.64 MB (4.9x) |
89.48 MB (2.5x) |
**Write-phase peak heap**:
| turns | add\_messages | delta(inf) | delta(freq=50) | delta(freq=10) |
delta(freq=5) |
|------:|-------------:|-----------:|---------------:|---------------:|--------------:|
| 10 | 456 KB | 199 KB (2.3x) | 199 KB (2.3x) | 212 KB (2.2x) | 232 KB
(2.0x) |
| 50 | 3.04 MB | 742 KB (4.1x) | 805 KB (3.8x) | 1.21 MB (2.5x) | 1.67
MB (1.8x) |
| 100 | 10.70 MB | 1.41 MB (7.6x) | 1.82 MB (5.9x) | 3.42 MB (3.1x) |
5.25 MB (2.0x) |
| 250 | 60.44 MB | 3.36 MB (18x) | 5.67 MB (11x) | 14.87 MB (4.1x) |
26.31 MB (2.3x) |
**Read-phase avg `get_state` latency** (5 calls, InMemory):
| turns | add\_messages | delta(inf) | delta(freq=50) | delta(freq=10) |
delta(freq=5) |
|------:|-------------:|-----------:|---------------:|---------------:|--------------:|
| 10 | 0.7 ms | 1.1 ms (0.6x) | 1.1 ms (0.6x) | 0.8 ms (0.9x) | 0.6 ms
(1.1x) |
| 50 | 2.7 ms | 5.3 ms (0.5x) | 3.5 ms (0.8x) | 2.7 ms (1.0x) | 2.7 ms
(1.0x) |
| 100 | 5.5 ms | 11.1 ms (0.5x) | 6.0 ms (0.9x) | 5.2 ms (1.1x) | 5.4 ms
(1.0x) |
| 250 | 12.9 ms | 27.2 ms (0.5x) | 13.6 ms (0.9x) | 12.9 ms (1.0x) |
13.0 ms (1.0x) |
**Postgres `get_tuple` read latency** (~100 tok/msg per step):
| steps | full-list | delta(inf) | delta(freq=50) | delta(freq=10) |
delta(freq=5) |
|------:|----------:|-----------:|---------------:|---------------:|--------------:|
| 10 | 0.29 ms | 0.21 ms (1.4x) | 0.19 ms (1.6x) | 0.19 ms (1.5x) | 0.19
ms (1.6x) |
| 50 | 0.19 ms | 0.15 ms (1.3x) | 0.19 ms (1.0x) | 0.22 ms (0.8x) | 0.29
ms (0.7x) |
| 100 | 0.27 ms | 0.17 ms (1.6x) | 0.22 ms (1.2x) | 0.23 ms (1.2x) |
0.21 ms (1.3x) |
| 500 | 0.60 ms | 0.30 ms (2.0x) | 0.66 ms (0.9x) | 0.56 ms (1.1x) |
0.69 ms (0.9x) |
**Takeaway:** `snapshot_frequency=10` matches full-list read latency
while still saving 5x on blob storage and ~4x on total storage.
---
## How it works
### Checkpoint blobs
`checkpoint()` always returns `DELTA_SENTINEL` (a zero-byte msgpack ext
marker) instead of the accumulated value. On restore, the saver's
`_get_channel_writes_history` walks the ancestor chain collecting
`checkpoint_writes` entries and replays them through the reducer:
```python
# blob stored per step: ~1 byte (sentinel)
# vs. full list growing O(N) every step with BinaryOperatorAggregate
```
### Reducer interface
`DeltaChannel` takes a **batch reducer** `(state, list[writes]) ->
state` — all writes for a step arrive in one call, enabling single-pass
implementations:
```python
# ❌ Don't use add_messages directly — it's a binary operator, not a batch reducer
messages: Annotated[list, DeltaChannel(add_messages)] # wrong
# ✅ Use _messages_delta_reducer — single pass, dedup by ID, RemoveMessage support
messages: Annotated[list, DeltaChannel(_messages_delta_reducer)]
# ✅ Or write your own batch reducer for custom types
def my_dict_reducer(state: dict, writes: list[dict]) -> dict:
result = dict(state)
for w in writes:
result.update(w)
return result
files: Annotated[dict, DeltaChannel(my_dict_reducer)]
```
### Snapshot frequency
`snapshot_frequency=N` writes a full `_DeltaSnapshot` blob every N
pregel steps, bounding replay depth regardless of thread length.
Snapshots are eager — written even if the channel had no update that
step, so the depth bound always holds:
```python
# Replay walks at most 10 ancestors before hitting a snapshot
messages: Annotated[list, DeltaChannel(_messages_delta_reducer, snapshot_frequency=10)]
```
### Migration from `BinaryOperatorAggregate`
Pre-existing threads written under `BinaryOperatorAggregate` work
transparently after swapping the annotation — the saver detects a
plain-value ancestor blob and uses it as the reconstruction seed:
```python
# Before: BinaryOperatorAggregate stores full list every step
items: Annotated[list, add_messages]
# After: DeltaChannel — existing checkpoints still readable, new steps use sentinel
items: Annotated[list, DeltaChannel(_messages_delta_reducer)]
```
### Async write-ordering safety
In `durability="async"` mode (default), `put_writes` calls are
fire-and-forget. `AsyncPregelLoop` tracks in-flight `aput_writes`
futures for DeltaChannel channels in `_delta_write_futs` and drains them
via `await asyncio.gather()` in `_checkpointer_put_after_previous`
before `aput()` — ensuring `checkpoint_writes` are durable before the
sentinel blob is committed.
---
## What's in scope
- **`libs/langgraph/langgraph/channels/delta.py`** — `DeltaChannel`
implementation
- **`libs/langgraph/langgraph/graph/message.py`** —
`_messages_delta_reducer` (experimental)
- **`libs/checkpoint/`** — `_get_channel_writes_history` ancestor-walk
API on `BaseCheckpointSaver`, `InMemorySaver` optimized override
- **`libs/checkpoint-postgres/`** — `PostgresSaver` /
`AsyncPostgresSaver` single-roundtrip UNION ALL override
- **`libs/langgraph/langgraph/pregel/`** — `channels_from_checkpoint` /
`create_checkpoint` wiring, async write-ordering safety
---
## Follow-ups
- **Batch reconstruction**: each DeltaChannel field issues its own
`_get_channel_writes_history` call; a single walk collecting all
sentinel channels would reduce roundtrips proportionally to the number
of DeltaChannel fields.
- **Sync write ordering**: `BackgroundExecutor.__exit__` guarantees
completion before `invoke()` returns, but within a run there's no
explicit ordering between `put_writes` and `put`. Two-phase commit for
sync would close this gap.
- **`ShallowPostgresSaver` compatibility**: shallow savers keep only the
latest checkpoint and have no parent chain to walk; DeltaChannel is
currently incompatible and should raise or warn at compile time.
- Updating the writes table w/ delta epoch ids for more efficient reads
- follow up w/ LSD checkpointer implementations to support delta
channel! and update prune
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: ccurme <chester.curme@gmail.com>
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
|
||
|
|
a48a045596 |
chore: dynamic push-task timeouts (#7646)
You can Send(..., timeout=...) now. Much fun. This would allow us to do something like adding support for an annotation to let the LLM to pick a timeout for a given tool call, etc. --------- Co-authored-by: Will Fu-Hinthorn <will@langchain.dev> |
||
|
|
85cd64ed69 |
fix(checkpoint): revive lc=2 JSON blobs for safe types without allowlist (#7582)
## Summary Fixes #7498 — `MESSAGE_COERCION_FAILURE` when resuming threads checkpointed before v1.0.1. **Root cause:** PR #6269 (v1.0.1) added an `_allowed_json_modules` security gate to `JsonPlusSerializer._reviver`. The gate defaults to `None`, so old `"json"`-format checkpoint blobs containing `lc=2` constructor dicts (the pre-msgpack serialization format for pydantic objects like `HumanMessage`) are now returned as raw dicts instead of being reconstructed. Those raw dicts reach `add_messages → convert_to_messages`, which sees `type="constructor"` and raises `MESSAGE_COERCION_FAILURE`. Fresh first-turn messages are unaffected because current `dumps_typed` only writes `"msgpack"` blobs. **Fix:** `_reviver` now reconstructs `lc=2` blobs whose target class is already in `SAFE_MSGPACK_TYPES` — the same curated allowlist already used by the msgpack deserialization path (includes all standard LangChain message types). Unknown classes are still blocked, preserving the security intent of #6269. ## Changes - `libs/checkpoint/langgraph/checkpoint/serde/jsonplus.py` — add `_is_safe_json_type()` helper; update `_reviver` and `_check_allowed_json_modules` to allow safe types without an explicit allowlist - `libs/checkpoint/tests/test_jsonplus.py` — two new regression tests: safe-type `lc=2` blobs revive correctly; unknown-type `lc=2` blobs stay blocked ## Test plan - [ ] `test_lc2_json_safe_type_revives_without_allowlist` — `HumanMessage`/`AIMessage` lc=2 JSON blobs round-trip to proper `BaseMessage` objects with no allowlist configured - [ ] `test_lc2_json_unknown_type_stays_blocked_without_allowlist` — `pprint.pprint` lc=2 blob still returns raw dict (not reconstructed) - [ ] `test_deserde_invalid_module` — existing behaviour unchanged - [ ] Full `test_jsonplus.py` suite: 93/93 passing Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
f44b49b33d |
chore: dedup warnings (#7257)
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev> |
||
|
|
7fa49bd550 |
docs: document LANGGRAPH_STRICT_MSGPACK for checkpoint security (#7517)
## Summary - Add `LANGGRAPH_STRICT_MSGPACK=true` guidance to `JsonPlusSerializer` docstring and inline comments - Update the warning message emitted for unregistered types to mention the env var - Add module docstring to `_msgpack.py` explaining the safety controls - Add Security sections to checkpoint, checkpoint-postgres, and checkpoint-sqlite READMEs ## Context Multiple security advisories have reported the same msgpack deserialization pattern (`ext_hook` → `importlib.import_module` → `getattr` → call). The underlying behavior is documented in the repo's threat model as T1, but the `LANGGRAPH_STRICT_MSGPACK` env var that mitigates it is not surfaced in user-facing docs, docstrings, or warning messages. This PR closes that gap. ## Test plan - [x] Verify READMEs render correctly on GitHub (callout boxes use `> [!IMPORTANT]` syntax) - [x] Verify `JsonPlusSerializer` docstring renders in IDE tooltips - [x] Confirm warning message format: `LANGGRAPH_STRICT_MSGPACK=true PYTHON_CMD 2>&1 | grep -i strict` --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
60385e452f |
fix(checkpoint): don't add the task to the checkpoint batch if it was… (#7168)
Cleaning up CI for #6701 ```md I'm trying to help a customer with some issues related to their checkpointing in postgres. They have some timeouts and retries around the langgraph checkpoint queries and I suspect the queue may be filling up with cancelled tasks. This PR adds some eager checks to not execute an operation if the future was already cancelled. This is to prevent the queue executing tasks that might have already timed out, which would otherwise cause more tasks to timeout due to the longer execution delay. Thank you for contributing to LangGraph! Follow these steps to mark your pull request as ready for review. **If any of these steps are not completed, your PR will not be considered for review.** ``` Co-authored-by: Conrad Ludgate <conradludgate@gmail.com> |
||
|
|
901ab6b3f8 | chore: add serde events (#6954) | ||
|
|
5ddfce1814 | chore: support workflow dispatch on ci (#6952) | ||
|
|
e2e90da5dc |
chore: improve subclass handling (#6948)
If subclass doesn't support the new parameter, we the current implementation would create an error. |
||
|
|
50df7d423a |
Merge commit from fork
* Patch * Add more tests * update idempotency tests --------- Co-authored-by: William Fu-Hinthorn <13333726+hinthornw@users.noreply.github.com> |
||
|
|
9b9de5bd16 |
chore: conformance testing (#6842)
Add some conformance tests for checkpointer implementations. Includes additona methods that will be useful if you want to integarte in the agent server. |
||
|
|
3ff6340379 |
docs: add clarity to use of thread_id (#6515)
|
||
|
|
f91d79d0c8 |
fix: flip default on base cache (#6677)
rebasing https://github.com/langchain-ai/langgraph/pull/6562 w/ current main |
||
|
|
b6cc022861 |
fix(checkpoint): InMemorySaver context managers should return self in… (#6529)
h/t to @lexi-k for openening. merged here to check/fix CI Co-authored-by: lexi-k <69981673+lexi-k@users.noreply.github.com> |
||
|
|
2b78bf3bad | fix: docstring for serializer protocol (#6525) | ||
|
|
d45f52ab0c | chore: clean up some refs (#6487) | ||
|
|
092c9ecde6 |
chore: update ormsgpack minbound and add OPT_REPLACE_SURROGATES (#6395)
This lets the default `msgpack` serialization mode handle more cases where user data contains invalid unicode. |
||
|
|
c5744f583b |
chore: Restrict "json" type deserialization (#6269)
- Rm untyped loads/dumps - Restrict to an allow list --------- Co-authored-by: Sydney Runkle <sydneymarierunkle@gmail.com> |
||
|
|
7e666b58cd | style: fixes for ref docs (#6297) | ||
|
|
2d3121a17c |
chore: drop Python 3.9 (and syntax) (#6289)
* `strict=False` is the default, pyupgrade to min version 3.10 adds this to be explicit w/ behavior |
||
|
|
d9e3d83894 |
docs: style linting (#6260)
also fixes some links --------- Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Co-authored-by: ccurme <chester.curme@gmail.com> Co-authored-by: Sydney Runkle <sydneymarierunkle@gmail.com> Co-authored-by: William FH <13333726+hinthornw@users.noreply.github.com> |
||
|
|
9b46cba1fb |
fix: rename away from LangGraph Platform (#6281)
some of these changes were obvious, and some were less obvious. In a few spots, it felt like a judgement call if we should be saying LangSmith Deployment of LangGraph Server. But hopefully either works. |
||
|
|
1ba96f49bf |
fix(checkpoint): handle metadata.writes when serializing old checkpoints with Jsonb (#6236)
Issue Support for `Checkpoint.metadata.writes` was dropped in `langgraph` v0.5.x. In `langgraph-checkpoint-postgres` v2.0.23, metadata was serialized with `BasePostgresSaver._dump_metadata` -> `JsonPlusSerializer.dumps` which handles `pydantic.BaseModel`. In v2.0.23, metadata is serialized with `psycopg.types.json.Jsonb`, which raises `TypeError: Object of type AIMessage is not JSON serializable` when trying to serialize `writes`. Solution - Add `BaseCheckpointSaver.get_serializable_checkpoint_metadata` which pops the `writes` key. - Log deprecation warning when strange version combinations are used Solves https://github.com/langchain-ai/langgraph/issues/5769 --------- Co-authored-by: Alex Kondratev <56111142+soapun@users.noreply.github.com> |
||
|
|
682f39e0d3 |
fix(checkpoint): preserve non-ascii text in InMemoryStore embeddings (#6111)
### Description
* Set `ensure_ascii=False` for all `json.dumps` calls in
`get_text_at_path`. Preserves non-ASCII text instead of embedding
`\uXXXX` escapes.
**Before**
```python
store.put(("user_123", "memories"), "1", {"text": "这是中文"})
# embeds {"text": "\\u8fd9\\u662f\\u4e2d\\u6587"}
```
**After**
```python
store.put(("user_123", "memories"), "1", {"text": "这是中文"})
# embeds {"text": "这是中文"}
```
### Tests & Docs
* Add unit test `test_non_ascii` that writes three records (Chinese,
Japanese, Korean) to an `InMemoryStore`, searches with the same strings,
and asserts the correct top hit with a score >= 0.15 for each.
### Issue
Fixes #5946
|
||
|
|
16b363fbb0 |
feat(langgraph): implement redis node level cache (#5834)
### Description Adds Redis as a supported cache backend for LangGraph node-level caching, enabling distributed caching across multiple processes/servers. This implementation follows the same patterns as existing InMemoryCache and SqliteCache. ### Key changes - New RedisCache class implementing the BaseCache interface - Support for TTL-based expiration and batch operations - Worker-specific cache prefixes for parallel test isolation ### Dependencies - redis package (already included in dev dependencies) ### Test Plan - Unit tests: Added Redis cache tests covering basic operations, TTL, batch operations, and error handling - Integration tests: Redis cache integrated into existing LangGraph test suite, tested with all checkpointer combinations |
||
|
|
b5504506a7 | fix: add resiliency for task cancellation (#5846) | ||
|
|
c6ae8d25b9 |
perf: Save updated_channels to checkpoint (#5828)
- This makes prepare_next_tasks constant on number of nodes in all cases, whereas before we were falling back to node iteration when resuming from an existing checkpoint |
||
|
|
90ba4c5205 | Merge branch 'main' into v1 | ||
|
|
c6d674cd3e | Merge branch 'main' into v1 | ||
|
|
0d8dfa7bba |
fix(checkpoint): fix AsyncBatchedBaseStore getting stuck
This commit fixes #5503 Gist of it is: - `asyncio.exception.InvalidStateError` were being raised when the future was cancelled - this exception bubbled up and killed the background task - `AsyncBatchedBaseStore` stopped doing queries because the background task wasn't running anymore This commit adds some "if future is not done" checks to guard against this. |
||
|
|
e91f72dd48 |
fix(checkpoint): correct logging call to use logger (#5458)
fix[checkpoint]: correct logging call to use logger |
||
|
|
c989f1c898 |
langgraph: remove support for thread_ts (old alias for checkpoint_id) (#5295)
* remove support for thread_ts * docs and tests |
||
|
|
a1c856c088 |
Reduce extraneous keys in checkpoint.metadata
- Leave it up to each checkpointer implementation to decide whether to merge in configurable/metadata (previously PregelLoop would do some of this always) - Never copy over internal langgraph keys into checkpoint.metadata (these are redundant/misleading to include) |
||
|
|
1134017d07 |
Preparation for 0.5 release: langgraph-checkpoint (#5124)
Prepare langgraph-checkpoint for 0.5 - Given we have no upper bound on langgraph-checkpoint dep need to undo all changes in langgraph-checkpoint that might break previous versions of langgraph |
||
|
|
5498893780 |
Restore compatibility with custom checkpointer classes created in prior versions
- Ensure existing custom checkpointer classes are compatible with new langgraph-checkpoint release |
||
|
|
3ad061f0d7 | serialize/deserialize pandas with pickle fallback (#5057) |