Compare commits

..
Author SHA1 Message Date
Sydney RunkleandGitHub 3614e88c58 release: bump alpha packages to official versions (#7775)
## Summary

Bumps all alpha-versioned packages to their official releases:

- `langgraph`: `1.2.0a7` → `1.2.0`
- `langgraph-checkpoint`: `4.1.0a4` → `4.1.0`
- `langgraph-checkpoint-postgres`: `3.1.0a4` → `3.1.0`
- `langgraph-checkpoint-sqlite`: `3.1.0a1` → `3.1.0`
- `langgraph-prebuilt`: `1.1.0a2` → `1.1.0`

Also removes the `a*` alpha specifiers from cross-dependency pins in
`langgraph`, `checkpoint-postgres`, and `checkpoint-sqlite`, and
regenerates all `uv.lock` files.
2026-05-11 23:27:38 -04:00
Quanzheng LongandGitHub 6dff3b3bce feat(langgraph): durable error-handler resume across host crashes (#7773)
## Summary

- **Consolidate error writes:** When a node fails and has an error
handler, `commit()` now appends both `ERROR` and `ERROR_SOURCE_NODE` in
a single `put_writes` call, eliminating the redundant overwrite that
`schedule_error_handler` used to do.
- **Ensure durability before handler execution:** Reuses the
`_delta_write_futs` pattern — a new `_error_handler_write_futs` list
collects the persistence future from `put_writes` when
`ERROR_SOURCE_NODE` is written, and `schedule_error_handler` /
`aschedule_error_handler` drain it (sync: `concurrent.futures.wait`,
async: `asyncio.gather`) before preparing the handler task.
- **Resume directly to error handler:** Adds
`_resume_error_handlers_if_applicable()` to `PregelLoop`, called from
`tick()` after `_reapply_writes_to_succeeded_nodes()`. On resume, it
detects `ERROR_SOURCE_NODE` markers in `checkpoint_pending_writes`,
marks the original task as done (so the runner skips it), and schedules
a fresh handler task.
- **Rename internal methods for clarity:** `_match_writes` →
`_reapply_writes_to_succeeded_nodes` (makes it clear that
failed/interrupted tasks are skipped); `_resume_error_handlers` →
`_resume_error_handlers_if_applicable`.

## Test plan

- [x] `test_error_handler_resumes_after_crash`: single node fails,
handler crashes, resume re-runs the handler (not the original node).
Verifies `NodeError.node` and error content survive checkpoint
round-trip.
- [x] `test_error_handler_resumes_after_crash_multiple_nodes`: two nodes
fail concurrently in the same superstep, each with its own handler.
Verifies error handler starts while other nodes are still in-flight (via
`threading.Event`), and on resume both handlers re-run with correct
`NodeError.node` and error content.
- [x] All 101 tests in `test_retry.py` pass (including 19 error-handler
tests).
- [x] `make format` + `make lint` clean.
2026-05-12 01:39:03 +00:00
Josh RogersandGitHub 786c42fde3 release(cli): 0.4.26 (#7772)
Bumps the version of the cli to allow support for pre-release version
pinning of langgraph-api:
https://github.com/langchain-ai/langgraph/pull/7771
2026-05-11 20:24:21 -04:00
Josh RogersandGitHub 8537ea94d3 fix(cli): add support for prerelease api_versions (#7771)
Today, pre-release versions aren't supported for the `api_version`
config parameter in langgraph.json. This adds support for them to enable
latest and rc images going forward.
2026-05-11 20:20:17 -04:00
3db82d5b6d feat(langgraph): add set_node_defaults() to StateGraph (#7747)
Add `StateGraph.set_node_defaults()` — a fluent builder method for
setting graph-wide node policies in one place. Per-node values from
`add_node()` always take precedence. Defaults are applied at `compile()`
time.

```python
graph = (
    StateGraph(State)
    .set_node_defaults(
        retry_policy=RetryPolicy(max_attempts=3),
        error_handler=my_fallback_handler,
        timeout=TimeoutPolicy(run_timeout=30),
    )
    .add_node("a", node_a)
    .add_node("b", node_b, retry_policy=custom)  # overrides default
    .add_edge(START, "a")
    .compile()
)
```

Error handlers are never invoked on error-handler nodes themselves —
handler failures fail the run. Not inherited by subgraphs.

## Supported defaults

| `set_node_defaults()` kwarg | Fallback for `add_node(...)` kwarg |
Applies to error-handler nodes? |
|---|---|---|
| `retry_policy` | `retry_policy` | Yes |
| `cache_policy` | `cache_policy` | No — caching handler results is
unsafe |
| `error_handler` | `error_handler` | No — handlers must never catch
themselves |
| `timeout` | `timeout` | Yes |

## Changes

- `libs/langgraph/langgraph/graph/state.py` — new `_NodeDefaults`
dataclass, `set_node_defaults()` method on `StateGraph`; `compile()`
applies builder defaults to node specs with per-branch rules for which
defaults apply to error-handler nodes.
- `libs/langgraph/tests/test_retry.py` — 14 new tests covering all four
policy types, per-node override precedence, chaining, combined
retry+handler, handler exclusion, `RunnableConfig` injection, and name
collision.

## Verification

`make format`, `make lint`, `make test` all passing in `libs/langgraph`.

---------

Co-authored-by: Sydney Runkle <sydneymarierunkle@gmail.com>
2026-05-11 19:37:16 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
029eabf444 chore(deps): bump urllib3 from 2.6.3 to 2.7.0 in /libs/checkpoint (#7762)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/releases">urllib3's
releases</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h2>Security</h2>
<p>Addressed high-severity security issues. Impact was limited to
specific use cases detailed in the accompanying advisories; overall user
exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been read and decompressed partially. (Reported by <a
href="https://github.com/Cycloctane"><code>@​Cycloctane</code></a>)</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed using the official <a
href="https://pypi.org/project/brotli/">Brotli</a> library. (Reported by
<a
href="https://github.com/kimkou2024"><code>@​kimkou2024</code></a>)</li>
</ol>
<p>See GHSA-mf9v-mfxr-j63j for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip sensitive
headers specified in <code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host. (GHSA-qccp-gfcp-xxvc reported by <a
href="https://github.com/christos-spearbit"><code>@​christos-spearbit</code></a>)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better visibility of existing
deprecation notices. Rescheduled the removal of deprecated features to
version 3.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3763">urllib3/urllib3#3763</a>)</li>
<li>Removed support for end-of-life Python 3.9. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3720">urllib3/urllib3#3720</a>)</li>
<li>Removed support for end-of-life PyPy3.10. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">urllib3/urllib3#4979</a>)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3777">urllib3/urllib3#3777</a>)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed data buffered from previous partial reads. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3636">urllib3/urllib3#3636</a>)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the response after a partial read when
<code>cache_content=True</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4967">urllib3/urllib3#4967</a>)</li>
<li>Fixed <code>HTTPResponse.stream()</code> and
<code>HTTPResponse.read_chunked()</code> to handle <code>amt=0</code>.
(<a
href="https://redirect.github.com/urllib3/urllib3/issues/3793">urllib3/urllib3#3793</a>)</li>
<li>Updated <code>_TYPE_BODY</code> type alias to include missing
<code>Iterable[str]</code>, matching the documented and runtime behavior
of chunked request bodies. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3798">urllib3/urllib3#3798</a>)</li>
<li>Fixed <code>LocationParseError</code> when paths resembling
schemeless URIs were passed to
<code>HTTPConnectionPool.urlopen()</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3352">urllib3/urllib3#3352</a>)</li>
<li>Fixed <code>BaseHTTPResponse.readinto()</code> type annotation to
accept <code>memoryview</code> in addition to <code>bytearray</code>,
matching the <code>io.RawIOBase.readinto</code> contract and enabling
use with <code>io.BufferedReader</code> without type errors. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3764">urllib3/urllib3#3764</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's
changelog</a>.</em></p>
<blockquote>
<h1>2.7.0 (2026-05-07)</h1>
<h2>Security</h2>
<p>Addressed high-severity security issues.
Impact was limited to specific use cases detailed in the accompanying
advisories; overall user exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been
read and decompressed partially.</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed
using the official <code>Brotli
&lt;https://pypi.org/project/brotli/&gt;</code>__ library.</li>
</ol>
<p>See <code>GHSA-mf9v-mfxr-j63j
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-mf9v-mfxr-j63j&gt;</code>__
for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip
sensitive headers specified in
<code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host.
(<code>GHSA-qccp-gfcp-xxvc
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-qccp-gfcp-xxvc&gt;</code>__)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better
visibility of existing deprecation notices. Rescheduled the removal of
deprecated features to version 3.0.
(<code>[#3763](https://github.com/urllib3/urllib3/issues/3763)
&lt;https://github.com/urllib3/urllib3/issues/3763&gt;</code>__)</li>
<li>Removed support for end-of-life Python 3.9.
(<code>[#3720](https://github.com/urllib3/urllib3/issues/3720)
&lt;https://github.com/urllib3/urllib3/issues/3720&gt;</code>__)</li>
<li>Removed support for end-of-life PyPy3.10.
(<code>[#4979](https://github.com/urllib3/urllib3/issues/4979)
&lt;https://github.com/urllib3/urllib3/issues/4979&gt;</code>__)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0.
(<code>[#3777](https://github.com/urllib3/urllib3/issues/3777)
&lt;https://github.com/urllib3/urllib3/issues/3777&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed
data buffered from previous partial reads.
(<code>[#3636](https://github.com/urllib3/urllib3/issues/3636)
&lt;https://github.com/urllib3/urllib3/issues/3636&gt;</code>__)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the
response after a partial read when <code>cache_content=True</code>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/urllib3/urllib3/commit/9a950b92d999f906b6020bb2d1076ee56cddd5d2"><code>9a950b9</code></a>
Release 2.7.0</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/5ec0de499b9166ca71c65ab04f2a7e4eb0d66fcc"><code>5ec0de4</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2bdcc44d1e163fb5cc48a8662425e35e15adfe6a"><code>2bdcc44</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/f45b0df09d8620ac6ed0491eb9362c8c87b7bc2c"><code>f45b0df</code></a>
Fix a misleading example for <code>ProxyManager</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4970">#4970</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/577193ca029872384f82c133449e0935f6d8a64b"><code>577193c</code></a>
Switch to nightly PyPy3.11 in CI for now (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4984">#4984</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/e90af45bb006c3a452a3a21644a2681523f5c7fc"><code>e90af45</code></a>
Avoid infinite loop in <code>HTTPResponse.read_chunked</code> when
<code>amt=0</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4974">#4974</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/67ed74fdaec6659a6534621ec8e3aaaa6f976210"><code>67ed74f</code></a>
Bump dev dependencies (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4972">#4972</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/3abd481097b54d87b574ac7ea593c3f40938a84d"><code>3abd481</code></a>
Upgrade mypy to version 1.20.2 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4978">#4978</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2b8725dfcac4f21d4d93cc0cc3a64a33af08f890"><code>2b8725d</code></a>
Drop support for EOL PyPy3.10 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">#4979</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2944b2a0a6c573f5548a39cfd17196f98ee21b33"><code>2944b2a</code></a>
Upgrade <code>setup-chrome</code> and <code>setup-firefox</code> to fix
warnings (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4973">#4973</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=uv&previous-version=2.6.3&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 22:23:15 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f52f1ce365 chore(deps): bump the uv group across 2 directories with 1 update (#7749)
Bumps the uv group with 1 update in the /libs/cli/uv-examples/monorepo
directory: [langchain-core](https://github.com/langchain-ai/langchain).
Bumps the uv group with 1 update in the /libs/cli/uv-examples/simple
directory: [langchain-core](https://github.com/langchain-ai/langchain).

Updates `langchain-core` from 1.2.28 to 1.3.3
<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.3.3</h2>
<p>Changes since langchain-core==1.3.2</p>
<p>release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)
chore: bump notebook from 7.5.0 to 7.5.6 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37109">#37109</a>)
chore: bump types-pyyaml from 6.0.12.20250915 to 6.0.12.20260408 in
/libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37129">#37129</a>)
fix(core): preserve structured <code>inputs</code> on tool runs in
tracers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37108">#37108</a>)
release(perplexity): 1.2.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37091">#37091</a>)
chore(docs): update x handle references (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37081">#37081</a>)
fix(core): make <code>removal</code> optional in
<code>warn_deprecated</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37056">#37056</a>)
fix(core): validate batch_size in _batch and _abatch to prevent infinite
loop (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36663">#36663</a>)
chore(core): mark stream_v2/astream_v2 as beta (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36992">#36992</a>)</p>
<h2>langchain-core==1.3.2</h2>
<p>Changes since langchain-core==1.3.1</p>
<p>release(core): 1.3.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36990">#36990</a>)
feat(core): add content-block-centric streaming (v2) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36834">#36834</a>)</p>
<h2>langchain-core==1.3.1</h2>
<p>Changes since langchain-core==1.3.0</p>
<p>release(core): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36972">#36972</a>)
feat(core): allow _format_output to pass through list of ToolOutputMixin
instances (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36963">#36963</a>)
chore: bump nbconvert from 7.17.0 to 7.17.1 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36923">#36923</a>)
feat(core): Update inheritance behavior for tracer metadata for special
keys (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36900">#36900</a>)
chore: bump langsmith from 0.7.13 to 0.7.31 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36813">#36813</a>)</p>
<h2>langchain-core==1.3.0</h2>
<p>Changes since langchain-core==1.2.31</p>
<p>release(core): release 1.3.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36851">#36851</a>)
release(core): 1.3.0a3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36829">#36829</a>)
chore(core): keep checkpoint_ns behavior in streaming metadata for
backwards compat (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36828">#36828</a>)
feat(core): Add chat model and LLM invocation params to traceable
metadata (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36771">#36771</a>)
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36816">#36816</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore(core): harden private SSRF utilities (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36768">#36768</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore: bump pytest from 9.0.2 to 9.0.3 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36719">#36719</a>)
release(core): 1.3.0.a2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36698">#36698</a>)
fix(core): Use reference counting for storing inherited run trees to
support garbage collection (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36660">#36660</a>)
docs(core): nit (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36685">#36685</a>)
release(core): 1.3.0a1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36656">#36656</a>)
chore(core): reduce streaming metadata / perf (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36588">#36588</a>)</p>
<h2>langchain-core==1.3.0a3</h2>
<p>Initial release</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/5039dfec1f8e78459540a7f1b52fb0d6d82e3f07"><code>5039dfe</code></a>
release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/55a7707837742c2f7a9b7e4a5dd428bf615f3b82"><code>55a7707</code></a>
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/c979c6187b6d82f4bef35b10b84b39fa44806b22"><code>c979c61</code></a>
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/d7031101da78e3f6b6c5956b48a5170c1a33292b"><code>d703110</code></a>
docs: update README.md (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37190">#37190</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4d50a2a68b38e9acc53027ea7e7cc89e2d80b4c7"><code>4d50a2a</code></a>
ci(infra): run pre-release checks before TestPyPI publish (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37194">#37194</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9bd730e1991baac4ea80aa07d93110dc1b52ee25"><code>9bd730e</code></a>
fix(fireworks): require <code>api_key</code> in
<code>FireworksEmbeddings</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37193">#37193</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/f475f4191fc3a8d3cf14063b44d524594c080c04"><code>f475f41</code></a>
release(mistralai): 1.1.4 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37191">#37191</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7dbff48aff508b01dc231ea0cbd4e4e09da92c97"><code>7dbff48</code></a>
fix(mistralai): strip non-wire keys from <code>ToolMessage</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37188">#37188</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/913816c440ea79295370e1af6484e17b86e5d03c"><code>913816c</code></a>
release(fireworks): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37189">#37189</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4498d3dc84a85688fa4d15476403a900bc7f9114"><code>4498d3d</code></a>
fix(fireworks): strip non-wire keys from <code>ToolMessage</code> text
content blocks (#...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.28...langchain-core==1.3.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `langchain-core` from 1.2.28 to 1.3.3
<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.3.3</h2>
<p>Changes since langchain-core==1.3.2</p>
<p>release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)
chore: bump notebook from 7.5.0 to 7.5.6 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37109">#37109</a>)
chore: bump types-pyyaml from 6.0.12.20250915 to 6.0.12.20260408 in
/libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37129">#37129</a>)
fix(core): preserve structured <code>inputs</code> on tool runs in
tracers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37108">#37108</a>)
release(perplexity): 1.2.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37091">#37091</a>)
chore(docs): update x handle references (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37081">#37081</a>)
fix(core): make <code>removal</code> optional in
<code>warn_deprecated</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37056">#37056</a>)
fix(core): validate batch_size in _batch and _abatch to prevent infinite
loop (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36663">#36663</a>)
chore(core): mark stream_v2/astream_v2 as beta (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36992">#36992</a>)</p>
<h2>langchain-core==1.3.2</h2>
<p>Changes since langchain-core==1.3.1</p>
<p>release(core): 1.3.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36990">#36990</a>)
feat(core): add content-block-centric streaming (v2) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36834">#36834</a>)</p>
<h2>langchain-core==1.3.1</h2>
<p>Changes since langchain-core==1.3.0</p>
<p>release(core): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36972">#36972</a>)
feat(core): allow _format_output to pass through list of ToolOutputMixin
instances (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36963">#36963</a>)
chore: bump nbconvert from 7.17.0 to 7.17.1 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36923">#36923</a>)
feat(core): Update inheritance behavior for tracer metadata for special
keys (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36900">#36900</a>)
chore: bump langsmith from 0.7.13 to 0.7.31 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36813">#36813</a>)</p>
<h2>langchain-core==1.3.0</h2>
<p>Changes since langchain-core==1.2.31</p>
<p>release(core): release 1.3.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36851">#36851</a>)
release(core): 1.3.0a3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36829">#36829</a>)
chore(core): keep checkpoint_ns behavior in streaming metadata for
backwards compat (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36828">#36828</a>)
feat(core): Add chat model and LLM invocation params to traceable
metadata (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36771">#36771</a>)
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36816">#36816</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore(core): harden private SSRF utilities (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36768">#36768</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore: bump pytest from 9.0.2 to 9.0.3 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36719">#36719</a>)
release(core): 1.3.0.a2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36698">#36698</a>)
fix(core): Use reference counting for storing inherited run trees to
support garbage collection (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36660">#36660</a>)
docs(core): nit (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36685">#36685</a>)
release(core): 1.3.0a1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36656">#36656</a>)
chore(core): reduce streaming metadata / perf (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36588">#36588</a>)</p>
<h2>langchain-core==1.3.0a3</h2>
<p>Initial release</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/5039dfec1f8e78459540a7f1b52fb0d6d82e3f07"><code>5039dfe</code></a>
release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/55a7707837742c2f7a9b7e4a5dd428bf615f3b82"><code>55a7707</code></a>
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/c979c6187b6d82f4bef35b10b84b39fa44806b22"><code>c979c61</code></a>
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/d7031101da78e3f6b6c5956b48a5170c1a33292b"><code>d703110</code></a>
docs: update README.md (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37190">#37190</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4d50a2a68b38e9acc53027ea7e7cc89e2d80b4c7"><code>4d50a2a</code></a>
ci(infra): run pre-release checks before TestPyPI publish (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37194">#37194</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9bd730e1991baac4ea80aa07d93110dc1b52ee25"><code>9bd730e</code></a>
fix(fireworks): require <code>api_key</code> in
<code>FireworksEmbeddings</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37193">#37193</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/f475f4191fc3a8d3cf14063b44d524594c080c04"><code>f475f41</code></a>
release(mistralai): 1.1.4 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37191">#37191</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7dbff48aff508b01dc231ea0cbd4e4e09da92c97"><code>7dbff48</code></a>
fix(mistralai): strip non-wire keys from <code>ToolMessage</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37188">#37188</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/913816c440ea79295370e1af6484e17b86e5d03c"><code>913816c</code></a>
release(fireworks): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37189">#37189</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4498d3dc84a85688fa4d15476403a900bc7f9114"><code>4498d3d</code></a>
fix(fireworks): strip non-wire keys from <code>ToolMessage</code> text
content blocks (#...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.28...langchain-core==1.3.3">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
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 15:15:55 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>John Kennedy
e47a7bb540 chore(deps): bump langchain-core from 1.3.2 to 1.3.3 in /libs/checkpoint (#7752)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.3.2 to 1.3.3.
<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.3.3</h2>
<p>Changes since langchain-core==1.3.2</p>
<p>release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)
chore: bump notebook from 7.5.0 to 7.5.6 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37109">#37109</a>)
chore: bump types-pyyaml from 6.0.12.20250915 to 6.0.12.20260408 in
/libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37129">#37129</a>)
fix(core): preserve structured <code>inputs</code> on tool runs in
tracers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37108">#37108</a>)
release(perplexity): 1.2.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37091">#37091</a>)
chore(docs): update x handle references (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37081">#37081</a>)
fix(core): make <code>removal</code> optional in
<code>warn_deprecated</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37056">#37056</a>)
fix(core): validate batch_size in _batch and _abatch to prevent infinite
loop (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36663">#36663</a>)
chore(core): mark stream_v2/astream_v2 as beta (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36992">#36992</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/5039dfec1f8e78459540a7f1b52fb0d6d82e3f07"><code>5039dfe</code></a>
release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/55a7707837742c2f7a9b7e4a5dd428bf615f3b82"><code>55a7707</code></a>
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/c979c6187b6d82f4bef35b10b84b39fa44806b22"><code>c979c61</code></a>
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/d7031101da78e3f6b6c5956b48a5170c1a33292b"><code>d703110</code></a>
docs: update README.md (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37190">#37190</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4d50a2a68b38e9acc53027ea7e7cc89e2d80b4c7"><code>4d50a2a</code></a>
ci(infra): run pre-release checks before TestPyPI publish (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37194">#37194</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9bd730e1991baac4ea80aa07d93110dc1b52ee25"><code>9bd730e</code></a>
fix(fireworks): require <code>api_key</code> in
<code>FireworksEmbeddings</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37193">#37193</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/f475f4191fc3a8d3cf14063b44d524594c080c04"><code>f475f41</code></a>
release(mistralai): 1.1.4 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37191">#37191</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7dbff48aff508b01dc231ea0cbd4e4e09da92c97"><code>7dbff48</code></a>
fix(mistralai): strip non-wire keys from <code>ToolMessage</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37188">#37188</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/913816c440ea79295370e1af6484e17b86e5d03c"><code>913816c</code></a>
release(fireworks): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37189">#37189</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4498d3dc84a85688fa4d15476403a900bc7f9114"><code>4498d3d</code></a>
fix(fireworks): strip non-wire keys from <code>ToolMessage</code> text
content blocks (#...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.3.2...langchain-core==1.3.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.3.2&new-version=1.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

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>
2026-05-11 15:14:39 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
29fefa0f05 chore(deps): bump urllib3 from 2.6.3 to 2.7.0 in /libs/langgraph (#7766)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/releases">urllib3's
releases</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h2>Security</h2>
<p>Addressed high-severity security issues. Impact was limited to
specific use cases detailed in the accompanying advisories; overall user
exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been read and decompressed partially. (Reported by <a
href="https://github.com/Cycloctane"><code>@​Cycloctane</code></a>)</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed using the official <a
href="https://pypi.org/project/brotli/">Brotli</a> library. (Reported by
<a
href="https://github.com/kimkou2024"><code>@​kimkou2024</code></a>)</li>
</ol>
<p>See GHSA-mf9v-mfxr-j63j for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip sensitive
headers specified in <code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host. (GHSA-qccp-gfcp-xxvc reported by <a
href="https://github.com/christos-spearbit"><code>@​christos-spearbit</code></a>)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better visibility of existing
deprecation notices. Rescheduled the removal of deprecated features to
version 3.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3763">urllib3/urllib3#3763</a>)</li>
<li>Removed support for end-of-life Python 3.9. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3720">urllib3/urllib3#3720</a>)</li>
<li>Removed support for end-of-life PyPy3.10. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">urllib3/urllib3#4979</a>)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3777">urllib3/urllib3#3777</a>)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed data buffered from previous partial reads. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3636">urllib3/urllib3#3636</a>)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the response after a partial read when
<code>cache_content=True</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4967">urllib3/urllib3#4967</a>)</li>
<li>Fixed <code>HTTPResponse.stream()</code> and
<code>HTTPResponse.read_chunked()</code> to handle <code>amt=0</code>.
(<a
href="https://redirect.github.com/urllib3/urllib3/issues/3793">urllib3/urllib3#3793</a>)</li>
<li>Updated <code>_TYPE_BODY</code> type alias to include missing
<code>Iterable[str]</code>, matching the documented and runtime behavior
of chunked request bodies. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3798">urllib3/urllib3#3798</a>)</li>
<li>Fixed <code>LocationParseError</code> when paths resembling
schemeless URIs were passed to
<code>HTTPConnectionPool.urlopen()</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3352">urllib3/urllib3#3352</a>)</li>
<li>Fixed <code>BaseHTTPResponse.readinto()</code> type annotation to
accept <code>memoryview</code> in addition to <code>bytearray</code>,
matching the <code>io.RawIOBase.readinto</code> contract and enabling
use with <code>io.BufferedReader</code> without type errors. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3764">urllib3/urllib3#3764</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's
changelog</a>.</em></p>
<blockquote>
<h1>2.7.0 (2026-05-07)</h1>
<h2>Security</h2>
<p>Addressed high-severity security issues.
Impact was limited to specific use cases detailed in the accompanying
advisories; overall user exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been
read and decompressed partially.</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed
using the official <code>Brotli
&lt;https://pypi.org/project/brotli/&gt;</code>__ library.</li>
</ol>
<p>See <code>GHSA-mf9v-mfxr-j63j
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-mf9v-mfxr-j63j&gt;</code>__
for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip
sensitive headers specified in
<code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host.
(<code>GHSA-qccp-gfcp-xxvc
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-qccp-gfcp-xxvc&gt;</code>__)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better
visibility of existing deprecation notices. Rescheduled the removal of
deprecated features to version 3.0.
(<code>[#3763](https://github.com/urllib3/urllib3/issues/3763)
&lt;https://github.com/urllib3/urllib3/issues/3763&gt;</code>__)</li>
<li>Removed support for end-of-life Python 3.9.
(<code>[#3720](https://github.com/urllib3/urllib3/issues/3720)
&lt;https://github.com/urllib3/urllib3/issues/3720&gt;</code>__)</li>
<li>Removed support for end-of-life PyPy3.10.
(<code>[#4979](https://github.com/urllib3/urllib3/issues/4979)
&lt;https://github.com/urllib3/urllib3/issues/4979&gt;</code>__)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0.
(<code>[#3777](https://github.com/urllib3/urllib3/issues/3777)
&lt;https://github.com/urllib3/urllib3/issues/3777&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed
data buffered from previous partial reads.
(<code>[#3636](https://github.com/urllib3/urllib3/issues/3636)
&lt;https://github.com/urllib3/urllib3/issues/3636&gt;</code>__)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the
response after a partial read when <code>cache_content=True</code>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/urllib3/urllib3/commit/9a950b92d999f906b6020bb2d1076ee56cddd5d2"><code>9a950b9</code></a>
Release 2.7.0</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/5ec0de499b9166ca71c65ab04f2a7e4eb0d66fcc"><code>5ec0de4</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2bdcc44d1e163fb5cc48a8662425e35e15adfe6a"><code>2bdcc44</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/f45b0df09d8620ac6ed0491eb9362c8c87b7bc2c"><code>f45b0df</code></a>
Fix a misleading example for <code>ProxyManager</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4970">#4970</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/577193ca029872384f82c133449e0935f6d8a64b"><code>577193c</code></a>
Switch to nightly PyPy3.11 in CI for now (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4984">#4984</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/e90af45bb006c3a452a3a21644a2681523f5c7fc"><code>e90af45</code></a>
Avoid infinite loop in <code>HTTPResponse.read_chunked</code> when
<code>amt=0</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4974">#4974</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/67ed74fdaec6659a6534621ec8e3aaaa6f976210"><code>67ed74f</code></a>
Bump dev dependencies (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4972">#4972</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/3abd481097b54d87b574ac7ea593c3f40938a84d"><code>3abd481</code></a>
Upgrade mypy to version 1.20.2 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4978">#4978</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2b8725dfcac4f21d4d93cc0cc3a64a33af08f890"><code>2b8725d</code></a>
Drop support for EOL PyPy3.10 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">#4979</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2944b2a0a6c573f5548a39cfd17196f98ee21b33"><code>2944b2a</code></a>
Upgrade <code>setup-chrome</code> and <code>setup-firefox</code> to fix
warnings (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4973">#4973</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=uv&previous-version=2.6.3&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:20:18 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0c23779869 chore(deps): bump urllib3 from 2.6.3 to 2.7.0 in /libs/cli (#7765)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/releases">urllib3's
releases</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h2>Security</h2>
<p>Addressed high-severity security issues. Impact was limited to
specific use cases detailed in the accompanying advisories; overall user
exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been read and decompressed partially. (Reported by <a
href="https://github.com/Cycloctane"><code>@​Cycloctane</code></a>)</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed using the official <a
href="https://pypi.org/project/brotli/">Brotli</a> library. (Reported by
<a
href="https://github.com/kimkou2024"><code>@​kimkou2024</code></a>)</li>
</ol>
<p>See GHSA-mf9v-mfxr-j63j for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip sensitive
headers specified in <code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host. (GHSA-qccp-gfcp-xxvc reported by <a
href="https://github.com/christos-spearbit"><code>@​christos-spearbit</code></a>)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better visibility of existing
deprecation notices. Rescheduled the removal of deprecated features to
version 3.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3763">urllib3/urllib3#3763</a>)</li>
<li>Removed support for end-of-life Python 3.9. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3720">urllib3/urllib3#3720</a>)</li>
<li>Removed support for end-of-life PyPy3.10. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">urllib3/urllib3#4979</a>)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3777">urllib3/urllib3#3777</a>)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed data buffered from previous partial reads. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3636">urllib3/urllib3#3636</a>)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the response after a partial read when
<code>cache_content=True</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4967">urllib3/urllib3#4967</a>)</li>
<li>Fixed <code>HTTPResponse.stream()</code> and
<code>HTTPResponse.read_chunked()</code> to handle <code>amt=0</code>.
(<a
href="https://redirect.github.com/urllib3/urllib3/issues/3793">urllib3/urllib3#3793</a>)</li>
<li>Updated <code>_TYPE_BODY</code> type alias to include missing
<code>Iterable[str]</code>, matching the documented and runtime behavior
of chunked request bodies. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3798">urllib3/urllib3#3798</a>)</li>
<li>Fixed <code>LocationParseError</code> when paths resembling
schemeless URIs were passed to
<code>HTTPConnectionPool.urlopen()</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3352">urllib3/urllib3#3352</a>)</li>
<li>Fixed <code>BaseHTTPResponse.readinto()</code> type annotation to
accept <code>memoryview</code> in addition to <code>bytearray</code>,
matching the <code>io.RawIOBase.readinto</code> contract and enabling
use with <code>io.BufferedReader</code> without type errors. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3764">urllib3/urllib3#3764</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's
changelog</a>.</em></p>
<blockquote>
<h1>2.7.0 (2026-05-07)</h1>
<h2>Security</h2>
<p>Addressed high-severity security issues.
Impact was limited to specific use cases detailed in the accompanying
advisories; overall user exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been
read and decompressed partially.</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed
using the official <code>Brotli
&lt;https://pypi.org/project/brotli/&gt;</code>__ library.</li>
</ol>
<p>See <code>GHSA-mf9v-mfxr-j63j
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-mf9v-mfxr-j63j&gt;</code>__
for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip
sensitive headers specified in
<code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host.
(<code>GHSA-qccp-gfcp-xxvc
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-qccp-gfcp-xxvc&gt;</code>__)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better
visibility of existing deprecation notices. Rescheduled the removal of
deprecated features to version 3.0.
(<code>[#3763](https://github.com/urllib3/urllib3/issues/3763)
&lt;https://github.com/urllib3/urllib3/issues/3763&gt;</code>__)</li>
<li>Removed support for end-of-life Python 3.9.
(<code>[#3720](https://github.com/urllib3/urllib3/issues/3720)
&lt;https://github.com/urllib3/urllib3/issues/3720&gt;</code>__)</li>
<li>Removed support for end-of-life PyPy3.10.
(<code>[#4979](https://github.com/urllib3/urllib3/issues/4979)
&lt;https://github.com/urllib3/urllib3/issues/4979&gt;</code>__)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0.
(<code>[#3777](https://github.com/urllib3/urllib3/issues/3777)
&lt;https://github.com/urllib3/urllib3/issues/3777&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed
data buffered from previous partial reads.
(<code>[#3636](https://github.com/urllib3/urllib3/issues/3636)
&lt;https://github.com/urllib3/urllib3/issues/3636&gt;</code>__)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the
response after a partial read when <code>cache_content=True</code>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/urllib3/urllib3/commit/9a950b92d999f906b6020bb2d1076ee56cddd5d2"><code>9a950b9</code></a>
Release 2.7.0</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/5ec0de499b9166ca71c65ab04f2a7e4eb0d66fcc"><code>5ec0de4</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2bdcc44d1e163fb5cc48a8662425e35e15adfe6a"><code>2bdcc44</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/f45b0df09d8620ac6ed0491eb9362c8c87b7bc2c"><code>f45b0df</code></a>
Fix a misleading example for <code>ProxyManager</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4970">#4970</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/577193ca029872384f82c133449e0935f6d8a64b"><code>577193c</code></a>
Switch to nightly PyPy3.11 in CI for now (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4984">#4984</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/e90af45bb006c3a452a3a21644a2681523f5c7fc"><code>e90af45</code></a>
Avoid infinite loop in <code>HTTPResponse.read_chunked</code> when
<code>amt=0</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4974">#4974</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/67ed74fdaec6659a6534621ec8e3aaaa6f976210"><code>67ed74f</code></a>
Bump dev dependencies (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4972">#4972</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/3abd481097b54d87b574ac7ea593c3f40938a84d"><code>3abd481</code></a>
Upgrade mypy to version 1.20.2 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4978">#4978</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2b8725dfcac4f21d4d93cc0cc3a64a33af08f890"><code>2b8725d</code></a>
Drop support for EOL PyPy3.10 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">#4979</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2944b2a0a6c573f5548a39cfd17196f98ee21b33"><code>2944b2a</code></a>
Upgrade <code>setup-chrome</code> and <code>setup-firefox</code> to fix
warnings (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4973">#4973</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=uv&previous-version=2.6.3&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:19:50 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
38f4936aab chore(deps): bump urllib3 from 2.6.3 to 2.7.0 in /libs/prebuilt (#7763)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/releases">urllib3's
releases</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h2>Security</h2>
<p>Addressed high-severity security issues. Impact was limited to
specific use cases detailed in the accompanying advisories; overall user
exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been read and decompressed partially. (Reported by <a
href="https://github.com/Cycloctane"><code>@​Cycloctane</code></a>)</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed using the official <a
href="https://pypi.org/project/brotli/">Brotli</a> library. (Reported by
<a
href="https://github.com/kimkou2024"><code>@​kimkou2024</code></a>)</li>
</ol>
<p>See GHSA-mf9v-mfxr-j63j for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip sensitive
headers specified in <code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host. (GHSA-qccp-gfcp-xxvc reported by <a
href="https://github.com/christos-spearbit"><code>@​christos-spearbit</code></a>)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better visibility of existing
deprecation notices. Rescheduled the removal of deprecated features to
version 3.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3763">urllib3/urllib3#3763</a>)</li>
<li>Removed support for end-of-life Python 3.9. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3720">urllib3/urllib3#3720</a>)</li>
<li>Removed support for end-of-life PyPy3.10. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">urllib3/urllib3#4979</a>)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3777">urllib3/urllib3#3777</a>)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed data buffered from previous partial reads. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3636">urllib3/urllib3#3636</a>)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the response after a partial read when
<code>cache_content=True</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4967">urllib3/urllib3#4967</a>)</li>
<li>Fixed <code>HTTPResponse.stream()</code> and
<code>HTTPResponse.read_chunked()</code> to handle <code>amt=0</code>.
(<a
href="https://redirect.github.com/urllib3/urllib3/issues/3793">urllib3/urllib3#3793</a>)</li>
<li>Updated <code>_TYPE_BODY</code> type alias to include missing
<code>Iterable[str]</code>, matching the documented and runtime behavior
of chunked request bodies. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3798">urllib3/urllib3#3798</a>)</li>
<li>Fixed <code>LocationParseError</code> when paths resembling
schemeless URIs were passed to
<code>HTTPConnectionPool.urlopen()</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3352">urllib3/urllib3#3352</a>)</li>
<li>Fixed <code>BaseHTTPResponse.readinto()</code> type annotation to
accept <code>memoryview</code> in addition to <code>bytearray</code>,
matching the <code>io.RawIOBase.readinto</code> contract and enabling
use with <code>io.BufferedReader</code> without type errors. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3764">urllib3/urllib3#3764</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's
changelog</a>.</em></p>
<blockquote>
<h1>2.7.0 (2026-05-07)</h1>
<h2>Security</h2>
<p>Addressed high-severity security issues.
Impact was limited to specific use cases detailed in the accompanying
advisories; overall user exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been
read and decompressed partially.</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed
using the official <code>Brotli
&lt;https://pypi.org/project/brotli/&gt;</code>__ library.</li>
</ol>
<p>See <code>GHSA-mf9v-mfxr-j63j
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-mf9v-mfxr-j63j&gt;</code>__
for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip
sensitive headers specified in
<code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host.
(<code>GHSA-qccp-gfcp-xxvc
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-qccp-gfcp-xxvc&gt;</code>__)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better
visibility of existing deprecation notices. Rescheduled the removal of
deprecated features to version 3.0.
(<code>[#3763](https://github.com/urllib3/urllib3/issues/3763)
&lt;https://github.com/urllib3/urllib3/issues/3763&gt;</code>__)</li>
<li>Removed support for end-of-life Python 3.9.
(<code>[#3720](https://github.com/urllib3/urllib3/issues/3720)
&lt;https://github.com/urllib3/urllib3/issues/3720&gt;</code>__)</li>
<li>Removed support for end-of-life PyPy3.10.
(<code>[#4979](https://github.com/urllib3/urllib3/issues/4979)
&lt;https://github.com/urllib3/urllib3/issues/4979&gt;</code>__)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0.
(<code>[#3777](https://github.com/urllib3/urllib3/issues/3777)
&lt;https://github.com/urllib3/urllib3/issues/3777&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed
data buffered from previous partial reads.
(<code>[#3636](https://github.com/urllib3/urllib3/issues/3636)
&lt;https://github.com/urllib3/urllib3/issues/3636&gt;</code>__)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the
response after a partial read when <code>cache_content=True</code>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/urllib3/urllib3/commit/9a950b92d999f906b6020bb2d1076ee56cddd5d2"><code>9a950b9</code></a>
Release 2.7.0</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/5ec0de499b9166ca71c65ab04f2a7e4eb0d66fcc"><code>5ec0de4</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2bdcc44d1e163fb5cc48a8662425e35e15adfe6a"><code>2bdcc44</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/f45b0df09d8620ac6ed0491eb9362c8c87b7bc2c"><code>f45b0df</code></a>
Fix a misleading example for <code>ProxyManager</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4970">#4970</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/577193ca029872384f82c133449e0935f6d8a64b"><code>577193c</code></a>
Switch to nightly PyPy3.11 in CI for now (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4984">#4984</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/e90af45bb006c3a452a3a21644a2681523f5c7fc"><code>e90af45</code></a>
Avoid infinite loop in <code>HTTPResponse.read_chunked</code> when
<code>amt=0</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4974">#4974</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/67ed74fdaec6659a6534621ec8e3aaaa6f976210"><code>67ed74f</code></a>
Bump dev dependencies (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4972">#4972</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/3abd481097b54d87b574ac7ea593c3f40938a84d"><code>3abd481</code></a>
Upgrade mypy to version 1.20.2 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4978">#4978</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2b8725dfcac4f21d4d93cc0cc3a64a33af08f890"><code>2b8725d</code></a>
Drop support for EOL PyPy3.10 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">#4979</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2944b2a0a6c573f5548a39cfd17196f98ee21b33"><code>2944b2a</code></a>
Upgrade <code>setup-chrome</code> and <code>setup-firefox</code> to fix
warnings (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4973">#4973</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=uv&previous-version=2.6.3&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:18:47 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0397e41eaf chore(deps): bump urllib3 from 2.6.3 to 2.7.0 in /libs/checkpoint-postgres (#7761)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/releases">urllib3's
releases</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h2>Security</h2>
<p>Addressed high-severity security issues. Impact was limited to
specific use cases detailed in the accompanying advisories; overall user
exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been read and decompressed partially. (Reported by <a
href="https://github.com/Cycloctane"><code>@​Cycloctane</code></a>)</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed using the official <a
href="https://pypi.org/project/brotli/">Brotli</a> library. (Reported by
<a
href="https://github.com/kimkou2024"><code>@​kimkou2024</code></a>)</li>
</ol>
<p>See GHSA-mf9v-mfxr-j63j for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip sensitive
headers specified in <code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host. (GHSA-qccp-gfcp-xxvc reported by <a
href="https://github.com/christos-spearbit"><code>@​christos-spearbit</code></a>)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better visibility of existing
deprecation notices. Rescheduled the removal of deprecated features to
version 3.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3763">urllib3/urllib3#3763</a>)</li>
<li>Removed support for end-of-life Python 3.9. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3720">urllib3/urllib3#3720</a>)</li>
<li>Removed support for end-of-life PyPy3.10. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">urllib3/urllib3#4979</a>)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3777">urllib3/urllib3#3777</a>)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed data buffered from previous partial reads. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3636">urllib3/urllib3#3636</a>)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the response after a partial read when
<code>cache_content=True</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4967">urllib3/urllib3#4967</a>)</li>
<li>Fixed <code>HTTPResponse.stream()</code> and
<code>HTTPResponse.read_chunked()</code> to handle <code>amt=0</code>.
(<a
href="https://redirect.github.com/urllib3/urllib3/issues/3793">urllib3/urllib3#3793</a>)</li>
<li>Updated <code>_TYPE_BODY</code> type alias to include missing
<code>Iterable[str]</code>, matching the documented and runtime behavior
of chunked request bodies. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3798">urllib3/urllib3#3798</a>)</li>
<li>Fixed <code>LocationParseError</code> when paths resembling
schemeless URIs were passed to
<code>HTTPConnectionPool.urlopen()</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3352">urllib3/urllib3#3352</a>)</li>
<li>Fixed <code>BaseHTTPResponse.readinto()</code> type annotation to
accept <code>memoryview</code> in addition to <code>bytearray</code>,
matching the <code>io.RawIOBase.readinto</code> contract and enabling
use with <code>io.BufferedReader</code> without type errors. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3764">urllib3/urllib3#3764</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's
changelog</a>.</em></p>
<blockquote>
<h1>2.7.0 (2026-05-07)</h1>
<h2>Security</h2>
<p>Addressed high-severity security issues.
Impact was limited to specific use cases detailed in the accompanying
advisories; overall user exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been
read and decompressed partially.</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed
using the official <code>Brotli
&lt;https://pypi.org/project/brotli/&gt;</code>__ library.</li>
</ol>
<p>See <code>GHSA-mf9v-mfxr-j63j
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-mf9v-mfxr-j63j&gt;</code>__
for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip
sensitive headers specified in
<code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host.
(<code>GHSA-qccp-gfcp-xxvc
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-qccp-gfcp-xxvc&gt;</code>__)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better
visibility of existing deprecation notices. Rescheduled the removal of
deprecated features to version 3.0.
(<code>[#3763](https://github.com/urllib3/urllib3/issues/3763)
&lt;https://github.com/urllib3/urllib3/issues/3763&gt;</code>__)</li>
<li>Removed support for end-of-life Python 3.9.
(<code>[#3720](https://github.com/urllib3/urllib3/issues/3720)
&lt;https://github.com/urllib3/urllib3/issues/3720&gt;</code>__)</li>
<li>Removed support for end-of-life PyPy3.10.
(<code>[#4979](https://github.com/urllib3/urllib3/issues/4979)
&lt;https://github.com/urllib3/urllib3/issues/4979&gt;</code>__)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0.
(<code>[#3777](https://github.com/urllib3/urllib3/issues/3777)
&lt;https://github.com/urllib3/urllib3/issues/3777&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed
data buffered from previous partial reads.
(<code>[#3636](https://github.com/urllib3/urllib3/issues/3636)
&lt;https://github.com/urllib3/urllib3/issues/3636&gt;</code>__)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the
response after a partial read when <code>cache_content=True</code>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/urllib3/urllib3/commit/9a950b92d999f906b6020bb2d1076ee56cddd5d2"><code>9a950b9</code></a>
Release 2.7.0</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/5ec0de499b9166ca71c65ab04f2a7e4eb0d66fcc"><code>5ec0de4</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2bdcc44d1e163fb5cc48a8662425e35e15adfe6a"><code>2bdcc44</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/f45b0df09d8620ac6ed0491eb9362c8c87b7bc2c"><code>f45b0df</code></a>
Fix a misleading example for <code>ProxyManager</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4970">#4970</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/577193ca029872384f82c133449e0935f6d8a64b"><code>577193c</code></a>
Switch to nightly PyPy3.11 in CI for now (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4984">#4984</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/e90af45bb006c3a452a3a21644a2681523f5c7fc"><code>e90af45</code></a>
Avoid infinite loop in <code>HTTPResponse.read_chunked</code> when
<code>amt=0</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4974">#4974</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/67ed74fdaec6659a6534621ec8e3aaaa6f976210"><code>67ed74f</code></a>
Bump dev dependencies (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4972">#4972</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/3abd481097b54d87b574ac7ea593c3f40938a84d"><code>3abd481</code></a>
Upgrade mypy to version 1.20.2 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4978">#4978</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2b8725dfcac4f21d4d93cc0cc3a64a33af08f890"><code>2b8725d</code></a>
Drop support for EOL PyPy3.10 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">#4979</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2944b2a0a6c573f5548a39cfd17196f98ee21b33"><code>2944b2a</code></a>
Upgrade <code>setup-chrome</code> and <code>setup-firefox</code> to fix
warnings (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4973">#4973</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=uv&previous-version=2.6.3&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:18:21 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c42b0cba15 chore(deps): bump urllib3 from 2.6.3 to 2.7.0 in /libs/checkpoint-sqlite (#7760)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/releases">urllib3's
releases</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h2>🚀 urllib3 is fundraising for HTTP/2 support</h2>
<p><a
href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3
is raising ~$40,000 USD</a> to release HTTP/2 support and ensure
long-term sustainable maintenance of the project after a sharp decline
in financial support. If your company or organization uses Python and
would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and
thousands of other projects <a
href="https://opencollective.com/urllib3">please consider contributing
financially</a> to ensure HTTP/2 support is developed sustainably and
maintained for the long-haul.</p>
<p>Thank you for your support.</p>
<h2>Security</h2>
<p>Addressed high-severity security issues. Impact was limited to
specific use cases detailed in the accompanying advisories; overall user
exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been read and decompressed partially. (Reported by <a
href="https://github.com/Cycloctane"><code>@​Cycloctane</code></a>)</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed using the official <a
href="https://pypi.org/project/brotli/">Brotli</a> library. (Reported by
<a
href="https://github.com/kimkou2024"><code>@​kimkou2024</code></a>)</li>
</ol>
<p>See GHSA-mf9v-mfxr-j63j for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip sensitive
headers specified in <code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host. (GHSA-qccp-gfcp-xxvc reported by <a
href="https://github.com/christos-spearbit"><code>@​christos-spearbit</code></a>)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better visibility of existing
deprecation notices. Rescheduled the removal of deprecated features to
version 3.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3763">urllib3/urllib3#3763</a>)</li>
<li>Removed support for end-of-life Python 3.9. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3720">urllib3/urllib3#3720</a>)</li>
<li>Removed support for end-of-life PyPy3.10. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">urllib3/urllib3#4979</a>)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3777">urllib3/urllib3#3777</a>)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed data buffered from previous partial reads. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3636">urllib3/urllib3#3636</a>)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the response after a partial read when
<code>cache_content=True</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4967">urllib3/urllib3#4967</a>)</li>
<li>Fixed <code>HTTPResponse.stream()</code> and
<code>HTTPResponse.read_chunked()</code> to handle <code>amt=0</code>.
(<a
href="https://redirect.github.com/urllib3/urllib3/issues/3793">urllib3/urllib3#3793</a>)</li>
<li>Updated <code>_TYPE_BODY</code> type alias to include missing
<code>Iterable[str]</code>, matching the documented and runtime behavior
of chunked request bodies. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3798">urllib3/urllib3#3798</a>)</li>
<li>Fixed <code>LocationParseError</code> when paths resembling
schemeless URIs were passed to
<code>HTTPConnectionPool.urlopen()</code>. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3352">urllib3/urllib3#3352</a>)</li>
<li>Fixed <code>BaseHTTPResponse.readinto()</code> type annotation to
accept <code>memoryview</code> in addition to <code>bytearray</code>,
matching the <code>io.RawIOBase.readinto</code> contract and enabling
use with <code>io.BufferedReader</code> without type errors. (<a
href="https://redirect.github.com/urllib3/urllib3/issues/3764">urllib3/urllib3#3764</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's
changelog</a>.</em></p>
<blockquote>
<h1>2.7.0 (2026-05-07)</h1>
<h2>Security</h2>
<p>Addressed high-severity security issues.
Impact was limited to specific use cases detailed in the accompanying
advisories; overall user exposure was estimated to be marginal.</p>
<ul>
<li>
<p>Decompression-bomb safeguards of the streaming API were bypassed:</p>
<ol>
<li>When <code>HTTPResponse.drain_conn()</code> was called after the
response had been
read and decompressed partially.</li>
<li>During the second <code>HTTPResponse.read(amt=N)</code> or
<code>HTTPResponse.stream(amt=N)</code> call when the response was
decompressed
using the official <code>Brotli
&lt;https://pypi.org/project/brotli/&gt;</code>__ library.</li>
</ol>
<p>See <code>GHSA-mf9v-mfxr-j63j
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-mf9v-mfxr-j63j&gt;</code>__
for details.</p>
</li>
<li>
<p>HTTP pools created using
<code>ProxyManager.connection_from_url</code> did not strip
sensitive headers specified in
<code>Retry.remove_headers_on_redirect</code> when
redirecting to a different host.
(<code>GHSA-qccp-gfcp-xxvc
&lt;https://github.com/urllib3/urllib3/security/advisories/GHSA-qccp-gfcp-xxvc&gt;</code>__)</p>
</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Used <code>FutureWarning</code> instead of
<code>DeprecationWarning</code> for better
visibility of existing deprecation notices. Rescheduled the removal of
deprecated features to version 3.0.
(<code>[#3763](https://github.com/urllib3/urllib3/issues/3763)
&lt;https://github.com/urllib3/urllib3/issues/3763&gt;</code>__)</li>
<li>Removed support for end-of-life Python 3.9.
(<code>[#3720](https://github.com/urllib3/urllib3/issues/3720)
&lt;https://github.com/urllib3/urllib3/issues/3720&gt;</code>__)</li>
<li>Removed support for end-of-life PyPy3.10.
(<code>[#4979](https://github.com/urllib3/urllib3/issues/4979)
&lt;https://github.com/urllib3/urllib3/issues/4979&gt;</code>__)</li>
<li>Bumped the minimum supported pyOpenSSL version to 19.0.0.
(<code>[#3777](https://github.com/urllib3/urllib3/issues/3777)
&lt;https://github.com/urllib3/urllib3/issues/3777&gt;</code>__)</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>Fixed a bug where <code>HTTPResponse.read(amt=None)</code> was
ignoring decompressed
data buffered from previous partial reads.
(<code>[#3636](https://github.com/urllib3/urllib3/issues/3636)
&lt;https://github.com/urllib3/urllib3/issues/3636&gt;</code>__)</li>
<li>Fixed a bug where <code>HTTPResponse.read()</code> could cache only
part of the
response after a partial read when <code>cache_content=True</code>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/urllib3/urllib3/commit/9a950b92d999f906b6020bb2d1076ee56cddd5d2"><code>9a950b9</code></a>
Release 2.7.0</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/5ec0de499b9166ca71c65ab04f2a7e4eb0d66fcc"><code>5ec0de4</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2bdcc44d1e163fb5cc48a8662425e35e15adfe6a"><code>2bdcc44</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/f45b0df09d8620ac6ed0491eb9362c8c87b7bc2c"><code>f45b0df</code></a>
Fix a misleading example for <code>ProxyManager</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4970">#4970</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/577193ca029872384f82c133449e0935f6d8a64b"><code>577193c</code></a>
Switch to nightly PyPy3.11 in CI for now (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4984">#4984</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/e90af45bb006c3a452a3a21644a2681523f5c7fc"><code>e90af45</code></a>
Avoid infinite loop in <code>HTTPResponse.read_chunked</code> when
<code>amt=0</code> (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4974">#4974</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/67ed74fdaec6659a6534621ec8e3aaaa6f976210"><code>67ed74f</code></a>
Bump dev dependencies (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4972">#4972</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/3abd481097b54d87b574ac7ea593c3f40938a84d"><code>3abd481</code></a>
Upgrade mypy to version 1.20.2 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4978">#4978</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2b8725dfcac4f21d4d93cc0cc3a64a33af08f890"><code>2b8725d</code></a>
Drop support for EOL PyPy3.10 (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4979">#4979</a>)</li>
<li><a
href="https://github.com/urllib3/urllib3/commit/2944b2a0a6c573f5548a39cfd17196f98ee21b33"><code>2944b2a</code></a>
Upgrade <code>setup-chrome</code> and <code>setup-firefox</code> to fix
warnings (<a
href="https://redirect.github.com/urllib3/urllib3/issues/4973">#4973</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=uv&previous-version=2.6.3&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:18:05 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dcfcd1dbb4 chore(deps): bump langchain-core from 1.3.2 to 1.3.3 in /libs/checkpoint-postgres (#7754)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.3.2 to 1.3.3.
<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.3.3</h2>
<p>Changes since langchain-core==1.3.2</p>
<p>release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)
chore: bump notebook from 7.5.0 to 7.5.6 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37109">#37109</a>)
chore: bump types-pyyaml from 6.0.12.20250915 to 6.0.12.20260408 in
/libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37129">#37129</a>)
fix(core): preserve structured <code>inputs</code> on tool runs in
tracers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37108">#37108</a>)
release(perplexity): 1.2.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37091">#37091</a>)
chore(docs): update x handle references (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37081">#37081</a>)
fix(core): make <code>removal</code> optional in
<code>warn_deprecated</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37056">#37056</a>)
fix(core): validate batch_size in _batch and _abatch to prevent infinite
loop (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36663">#36663</a>)
chore(core): mark stream_v2/astream_v2 as beta (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36992">#36992</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/5039dfec1f8e78459540a7f1b52fb0d6d82e3f07"><code>5039dfe</code></a>
release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/55a7707837742c2f7a9b7e4a5dd428bf615f3b82"><code>55a7707</code></a>
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/c979c6187b6d82f4bef35b10b84b39fa44806b22"><code>c979c61</code></a>
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/d7031101da78e3f6b6c5956b48a5170c1a33292b"><code>d703110</code></a>
docs: update README.md (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37190">#37190</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4d50a2a68b38e9acc53027ea7e7cc89e2d80b4c7"><code>4d50a2a</code></a>
ci(infra): run pre-release checks before TestPyPI publish (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37194">#37194</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9bd730e1991baac4ea80aa07d93110dc1b52ee25"><code>9bd730e</code></a>
fix(fireworks): require <code>api_key</code> in
<code>FireworksEmbeddings</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37193">#37193</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/f475f4191fc3a8d3cf14063b44d524594c080c04"><code>f475f41</code></a>
release(mistralai): 1.1.4 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37191">#37191</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7dbff48aff508b01dc231ea0cbd4e4e09da92c97"><code>7dbff48</code></a>
fix(mistralai): strip non-wire keys from <code>ToolMessage</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37188">#37188</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/913816c440ea79295370e1af6484e17b86e5d03c"><code>913816c</code></a>
release(fireworks): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37189">#37189</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4498d3dc84a85688fa4d15476403a900bc7f9114"><code>4498d3d</code></a>
fix(fireworks): strip non-wire keys from <code>ToolMessage</code> text
content blocks (#...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.3.2...langchain-core==1.3.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.3.2&new-version=1.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:17:55 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
61b9801b9c chore(deps): bump langchain-core from 1.3.0 to 1.3.3 in /libs/cli (#7753)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.3.0 to 1.3.3.
<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.3.3</h2>
<p>Changes since langchain-core==1.3.2</p>
<p>release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)
chore: bump notebook from 7.5.0 to 7.5.6 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37109">#37109</a>)
chore: bump types-pyyaml from 6.0.12.20250915 to 6.0.12.20260408 in
/libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37129">#37129</a>)
fix(core): preserve structured <code>inputs</code> on tool runs in
tracers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37108">#37108</a>)
release(perplexity): 1.2.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37091">#37091</a>)
chore(docs): update x handle references (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37081">#37081</a>)
fix(core): make <code>removal</code> optional in
<code>warn_deprecated</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37056">#37056</a>)
fix(core): validate batch_size in _batch and _abatch to prevent infinite
loop (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36663">#36663</a>)
chore(core): mark stream_v2/astream_v2 as beta (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36992">#36992</a>)</p>
<h2>langchain-core==1.3.2</h2>
<p>Changes since langchain-core==1.3.1</p>
<p>release(core): 1.3.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36990">#36990</a>)
feat(core): add content-block-centric streaming (v2) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36834">#36834</a>)</p>
<h2>langchain-core==1.3.1</h2>
<p>Changes since langchain-core==1.3.0</p>
<p>release(core): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36972">#36972</a>)
feat(core): allow _format_output to pass through list of ToolOutputMixin
instances (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36963">#36963</a>)
chore: bump nbconvert from 7.17.0 to 7.17.1 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36923">#36923</a>)
feat(core): Update inheritance behavior for tracer metadata for special
keys (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36900">#36900</a>)
chore: bump langsmith from 0.7.13 to 0.7.31 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36813">#36813</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/5039dfec1f8e78459540a7f1b52fb0d6d82e3f07"><code>5039dfe</code></a>
release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/55a7707837742c2f7a9b7e4a5dd428bf615f3b82"><code>55a7707</code></a>
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/c979c6187b6d82f4bef35b10b84b39fa44806b22"><code>c979c61</code></a>
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/d7031101da78e3f6b6c5956b48a5170c1a33292b"><code>d703110</code></a>
docs: update README.md (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37190">#37190</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4d50a2a68b38e9acc53027ea7e7cc89e2d80b4c7"><code>4d50a2a</code></a>
ci(infra): run pre-release checks before TestPyPI publish (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37194">#37194</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9bd730e1991baac4ea80aa07d93110dc1b52ee25"><code>9bd730e</code></a>
fix(fireworks): require <code>api_key</code> in
<code>FireworksEmbeddings</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37193">#37193</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/f475f4191fc3a8d3cf14063b44d524594c080c04"><code>f475f41</code></a>
release(mistralai): 1.1.4 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37191">#37191</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7dbff48aff508b01dc231ea0cbd4e4e09da92c97"><code>7dbff48</code></a>
fix(mistralai): strip non-wire keys from <code>ToolMessage</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37188">#37188</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/913816c440ea79295370e1af6484e17b86e5d03c"><code>913816c</code></a>
release(fireworks): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37189">#37189</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4498d3dc84a85688fa4d15476403a900bc7f9114"><code>4498d3d</code></a>
fix(fireworks): strip non-wire keys from <code>ToolMessage</code> text
content blocks (#...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.3.0...langchain-core==1.3.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.3.0&new-version=1.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:17:42 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8b893e9776 chore(deps): bump langchain-core from 1.2.28 to 1.3.3 in /libs/checkpoint-sqlite (#7751)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.2.28 to 1.3.3.
<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.3.3</h2>
<p>Changes since langchain-core==1.3.2</p>
<p>release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)
chore: bump notebook from 7.5.0 to 7.5.6 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37109">#37109</a>)
chore: bump types-pyyaml from 6.0.12.20250915 to 6.0.12.20260408 in
/libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37129">#37129</a>)
fix(core): preserve structured <code>inputs</code> on tool runs in
tracers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37108">#37108</a>)
release(perplexity): 1.2.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37091">#37091</a>)
chore(docs): update x handle references (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37081">#37081</a>)
fix(core): make <code>removal</code> optional in
<code>warn_deprecated</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37056">#37056</a>)
fix(core): validate batch_size in _batch and _abatch to prevent infinite
loop (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36663">#36663</a>)
chore(core): mark stream_v2/astream_v2 as beta (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36992">#36992</a>)</p>
<h2>langchain-core==1.3.2</h2>
<p>Changes since langchain-core==1.3.1</p>
<p>release(core): 1.3.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36990">#36990</a>)
feat(core): add content-block-centric streaming (v2) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36834">#36834</a>)</p>
<h2>langchain-core==1.3.1</h2>
<p>Changes since langchain-core==1.3.0</p>
<p>release(core): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36972">#36972</a>)
feat(core): allow _format_output to pass through list of ToolOutputMixin
instances (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36963">#36963</a>)
chore: bump nbconvert from 7.17.0 to 7.17.1 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36923">#36923</a>)
feat(core): Update inheritance behavior for tracer metadata for special
keys (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36900">#36900</a>)
chore: bump langsmith from 0.7.13 to 0.7.31 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36813">#36813</a>)</p>
<h2>langchain-core==1.3.0</h2>
<p>Changes since langchain-core==1.2.31</p>
<p>release(core): release 1.3.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36851">#36851</a>)
release(core): 1.3.0a3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36829">#36829</a>)
chore(core): keep checkpoint_ns behavior in streaming metadata for
backwards compat (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36828">#36828</a>)
feat(core): Add chat model and LLM invocation params to traceable
metadata (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36771">#36771</a>)
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36816">#36816</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore(core): harden private SSRF utilities (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36768">#36768</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore: bump pytest from 9.0.2 to 9.0.3 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36719">#36719</a>)
release(core): 1.3.0.a2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36698">#36698</a>)
fix(core): Use reference counting for storing inherited run trees to
support garbage collection (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36660">#36660</a>)
docs(core): nit (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36685">#36685</a>)
release(core): 1.3.0a1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36656">#36656</a>)
chore(core): reduce streaming metadata / perf (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36588">#36588</a>)</p>
<h2>langchain-core==1.3.0a3</h2>
<p>Initial release</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/5039dfec1f8e78459540a7f1b52fb0d6d82e3f07"><code>5039dfe</code></a>
release(core): 1.3.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37198">#37198</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/55a7707837742c2f7a9b7e4a5dd428bf615f3b82"><code>55a7707</code></a>
fix(core): set deprecation <code>since</code> to 1.3.3 to match release
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37200">#37200</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/c979c6187b6d82f4bef35b10b84b39fa44806b22"><code>c979c61</code></a>
fix(core, langchain): harden <code>load()</code> against untrusted
manifests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37197">#37197</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/d7031101da78e3f6b6c5956b48a5170c1a33292b"><code>d703110</code></a>
docs: update README.md (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37190">#37190</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4d50a2a68b38e9acc53027ea7e7cc89e2d80b4c7"><code>4d50a2a</code></a>
ci(infra): run pre-release checks before TestPyPI publish (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37194">#37194</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9bd730e1991baac4ea80aa07d93110dc1b52ee25"><code>9bd730e</code></a>
fix(fireworks): require <code>api_key</code> in
<code>FireworksEmbeddings</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37193">#37193</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/f475f4191fc3a8d3cf14063b44d524594c080c04"><code>f475f41</code></a>
release(mistralai): 1.1.4 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37191">#37191</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7dbff48aff508b01dc231ea0cbd4e4e09da92c97"><code>7dbff48</code></a>
fix(mistralai): strip non-wire keys from <code>ToolMessage</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37188">#37188</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/913816c440ea79295370e1af6484e17b86e5d03c"><code>913816c</code></a>
release(fireworks): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/37189">#37189</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4498d3dc84a85688fa4d15476403a900bc7f9114"><code>4498d3d</code></a>
fix(fireworks): strip non-wire keys from <code>ToolMessage</code> text
content blocks (#...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.28...langchain-core==1.3.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.2.28&new-version=1.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:17:25 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
57f9d3b1a5 chore(deps): bump mistune from 3.2.0 to 3.2.1 in /libs/langgraph (#7733)
Bumps [mistune](https://github.com/lepture/mistune) from 3.2.0 to 3.2.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/lepture/mistune/releases">mistune's
releases</a>.</em></p>
<blockquote>
<h2>v3.2.1</h2>
<h3>   🐞 Bug Fixes</h3>
<ul>
<li>Resolve Windows compatibility issues in file inclusion and tests  - 
by <a href="https://github.com/Yuki9814"><code>@​Yuki9814</code></a> <a
href="https://github.com/lepture/mistune/commit/2547102"><!-- raw HTML
omitted -->(25471)<!-- raw HTML omitted --></a></li>
<li>Escape html text  -  by <a
href="https://github.com/lepture"><code>@​lepture</code></a> <a
href="https://github.com/lepture/mistune/commit/a3cb6e5"><!-- raw HTML
omitted -->(a3cb6)<!-- raw HTML omitted --></a></li>
<li>Update link reference  -  by <a
href="https://github.com/lepture"><code>@​lepture</code></a> <a
href="https://github.com/lepture/mistune/commit/85eb54f"><!-- raw HTML
omitted -->(85eb5)<!-- raw HTML omitted --></a></li>
<li>Handle escaped dollar signs in inline math  -  by <a
href="https://github.com/saschabuehrle"><code>@​saschabuehrle</code></a>
in <a
href="https://redirect.github.com/lepture/mistune/issues/370">lepture/mistune#370</a>
<a href="https://github.com/lepture/mistune/commit/7bd5709"><!-- raw
HTML omitted -->(7bd57)<!-- raw HTML omitted --></a></li>
<li>Escape id of toc  -  by <a
href="https://github.com/lepture"><code>@​lepture</code></a> <a
href="https://github.com/lepture/mistune/commit/04880a0"><!-- raw HTML
omitted -->(04880)<!-- raw HTML omitted --></a></li>
<li>Escape id of headings  -  by <a
href="https://github.com/lepture"><code>@​lepture</code></a> <a
href="https://github.com/lepture/mistune/commit/2855622"><!-- raw HTML
omitted -->(28556)<!-- raw HTML omitted --></a></li>
<li>Remove double-encoding of image alt text  -  by <a
href="https://github.com/lawrence3699"><code>@​lawrence3699</code></a>
<a href="https://github.com/lepture/mistune/commit/0d6f3d8"><!-- raw
HTML omitted -->(0d6f3)<!-- raw HTML omitted --></a></li>
<li>Escape xml for math plugin  -  by <a
href="https://github.com/lepture"><code>@​lepture</code></a> <a
href="https://github.com/lepture/mistune/commit/5fa092e"><!-- raw HTML
omitted -->(5fa09)<!-- raw HTML omitted --></a></li>
<li>Use strict regex for image's height and width  -  by <a
href="https://github.com/lepture"><code>@​lepture</code></a> <a
href="https://github.com/lepture/mistune/commit/8d0cb75"><!-- raw HTML
omitted -->(8d0cb)<!-- raw HTML omitted --></a></li>
</ul>
<h5>    <a
href="https://github.com/lepture/mistune/compare/v3.2.0...v3.2.1">View
changes on GitHub</a></h5>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/lepture/mistune/blob/main/docs/changes.rst">mistune's
changelog</a>.</em></p>
<blockquote>
<h2>Version 3.2.1</h2>
<p><strong>Released on May 3, 2026</strong></p>
<ul>
<li>Escape link in <code>render_toc_ul</code>.</li>
<li>Escape text in math plugin.</li>
<li>Fix regex for math plugin.</li>
<li>Escape heading's ID attribute.</li>
<li>Fix <code>LINK_TITLE_RE</code> to prevent DoS.</li>
<li>Escape class attribute for admonition directive.</li>
<li>Remove double-encoding of image alt text.</li>
<li>Escape class attribute for image directive.</li>
<li>Fix width/height attribute for image directive.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/lepture/mistune/commit/067f90861088a496942f5eb43236135352b85d39"><code>067f908</code></a>
chore: release 3.2.1</li>
<li><a
href="https://github.com/lepture/mistune/commit/bf5503067a7d8c3b065fb143f67a3a08eca77bb6"><code>bf55030</code></a>
Merge pull request <a
href="https://redirect.github.com/lepture/mistune/issues/438">#438</a>
from saschabuehrle/fix/issue-370</li>
<li><a
href="https://github.com/lepture/mistune/commit/8d0cb7539a9ac82e633b98476b9922632eb8b948"><code>8d0cb75</code></a>
fix: use strict regex for image's height and width</li>
<li><a
href="https://github.com/lepture/mistune/commit/5fa092e3053b86f16e4c49b9d3ba0b7ab63f09ab"><code>5fa092e</code></a>
fix: escape xml for math plugin</li>
<li><a
href="https://github.com/lepture/mistune/commit/71ec9477ebfcf8dab0068804baf2c77461d77fbb"><code>71ec947</code></a>
Merge pull request <a
href="https://redirect.github.com/lepture/mistune/issues/440">#440</a>
from lawrence3699/fix/image-alt-double-encoding</li>
<li><a
href="https://github.com/lepture/mistune/commit/0d6f3d850283d51e9c60e5a1b3c9343a18df3722"><code>0d6f3d8</code></a>
fix: remove double-encoding of image alt text</li>
<li><a
href="https://github.com/lepture/mistune/commit/2855622d7fe235c6c805716edff943b5945d1eea"><code>2855622</code></a>
fix: escape id of headings</li>
<li><a
href="https://github.com/lepture/mistune/commit/04880a004cb28318e5ebd7ee9e63c79fc9f9ed04"><code>04880a0</code></a>
fix: escape id of toc</li>
<li><a
href="https://github.com/lepture/mistune/commit/7bd57096715385062505b3f78972be9fa823d6d4"><code>7bd5709</code></a>
fix: handle escaped dollar signs in inline math (fixes <a
href="https://redirect.github.com/lepture/mistune/issues/370">#370</a>)</li>
<li><a
href="https://github.com/lepture/mistune/commit/85eb54ff17da26327399bf188f9ff9b8fd515278"><code>85eb54f</code></a>
fix: update link reference</li>
<li>Additional commits viewable in <a
href="https://github.com/lepture/mistune/compare/v3.2.0...v3.2.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=mistune&package-manager=uv&previous-version=3.2.0&new-version=3.2.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 14:16:53 -07:00
Nick HollonandGitHub 2c04b03f72 chore(langgraph): bump langchain-core to 1.4.0 (#7767) 2026-05-11 12:25:30 -07:00
Quanzheng LongandGitHub 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)
2026-05-08 21:39:31 +00:00
ccurmeandGitHub dc0d992b90 fix(checkpoint): specify allowed_objects in Reviver (#7743) 2026-05-08 15:08:29 -04:00
open-swe[bot]GitHubopen-swe[bot] <open-swe@users.noreply.github.com>Parker J. Rule
ed168deb97 feat(sdk-py): support metadata filter for crons search/count (#7737)
## Description
Mirrors a server-side change by accepting an optional \`metadata\`
filter on \`crons.search\` and \`crons.count\` in both the async and
sync Python SDK clients. Matches the existing pattern used for
assistants/threads search.

## Release Note
Python SDK: \`crons.search\` and \`crons.count\` now accept an optional
\`metadata\` filter that is forwarded to the server.

## Test Plan
- [ ] New unit tests in \`tests/test_crons_client.py\` verify metadata
is forwarded for both async and sync clients and omitted when not
provided.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Parker J. Rule <pjrule@users.noreply.github.com>
2026-05-08 14:21:45 -04:00
Quanzheng LongandGitHub fb6e5c2bce chore: remove keepset helper (#7745)
Comment follow up
2026-05-08 18:00:31 +00:00
6dade64aa7 chore: remove unnecessary missing-typed-dict-key suppression (#7744)
Address a comment as follow up

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-08 10:39:54 -07:00
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>
2026-05-07 17:34:46 -07:00
d736564eb1 test(langgraph): de-flake heartbeat progress test (#7735)
## Summary

De-flake
`test_arun_with_retry_timeout_observer_emits_progress_on_heartbeat` —
the test was hitting a CI-runner-load-sensitive race where the
idle-timeout watchdog could fire before the task body's first await ran.

## Root cause

`_TimedAttemptScope.__init__` sets `_last_progress = time.monotonic()`
immediately, but the watchdog itself doesn't start polling until *after*
`wrap_config` and task scheduling. Under heavy CI load that gap can grow
large enough that:

```
T₀  scope.__init__()  →  _last_progress = T₀
… some scheduling slack …
Tₙ  watchdog runs, computes  remaining = T₀ + 0.2 − Tₙ ≤ 0  →  TimeoutError fires
```

The error reports `elapsed: 0.000s` because `elapsed` is measured from
the post-scheduling `start` (≈Tₙ), not from `_last_progress` (T₀). The
previous test set `idle_timeout=0.2s`, which left almost no headroom for
that scheduling slack.

## Fix (test-side only — no production change)

- **Heartbeat at task-body entry**: `runtime.heartbeat()` is now called
before the first `await asyncio.sleep(...)`, which resets
`_last_progress` to "now" the moment the task body actually starts
running. This eliminates the scope-init-to-first-await gap as a flake
source.
- **Idle timeout 0.2s → 1.0s**: gives ~5× headroom over the ~400ms task
duration, so scheduling pressure stays comfortably within budget.

## Why test-side instead of fixing the production race

The proper production fix would be to set `_last_progress` at
watchdog-entry time rather than at scope-init time. That's a behaviour
change in the retry/timeout machinery and out of scope for a flaky-test
fix. The two test-side defenses make this particular test stable without
touching production semantics; the underlying race in
`_TimedAttemptScope` is worth a separate follow-up.

## Test plan

- 10/10 repeated local runs pass:
  ```
uv run pytest
tests/test_retry.py::test_arun_with_retry_timeout_observer_emits_progress_on_heartbeat
--count=10
  ```
- All assertions still meaningful: still verifies start/finish events,
at least one progress event, rate-limited progress count (≤ total
events), and per-event metadata (task_name, attempt, idle_timeout_secs,
progress_at).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-07 10:31:18 -07:00
69f2d3a430 chore(langgraph): re-implement exit mode for delta channel (#7730)
## Summary

Replaces `durability="exit"`'s blanket force-snapshot of every
`DeltaChannel` with proper write persistence that honors per-channel
`snapshot_frequency`, plus closes two latent bugs the force-snapshot was
masking.

Before: every exit-mode run wrote a full `_DeltaSnapshot` blob for every
delta channel, even when the channel had zero updates this run and was
nowhere near its `snapshot_frequency`. After: the same count-based
decision used by `durability="sync"`/`"async"` applies — channels at or
above `snapshot_frequency` snapshot; channels below it persist their
accumulated writes via a lazy "stub" anchor; untouched channels write
nothing.

## What changed

**Core redesign** (`pregel/_loop.py`, `pregel/_checkpoint.py`)

- Drop `force_delta_snapshot` from `create_checkpoint` and
`_should_snapshot_delta`.
- Add `decide_delta_snapshots(channels, counts)` pure helper used by
both `create_checkpoint` and the new exit-mode peek-ahead path.
- Add `_exit_delta_writes` accumulator: every delta-channel write
produced during a `durability="exit"` run (input writes from `_first` +
per-superstep writes captured before `pending_writes.clear()` in
`after_tick`) is collected into this list.
- Add `_put_exit_delta_writes` (sync + async): runs from
`_suppress_interrupt` BEFORE `_put_checkpoint(exiting=True)`. Filters
out channels that will snapshot, then persists remaining writes to
`checkpoint_writes` under an anchor parent. The anchor is the existing
saved parent on resumed runs, or a lazily-created empty stub on first
runs.
- Visibility ordering: stub put goes onto `_put_checkpoint_fut` (becomes
the next put's `prev`); exit-write futures go onto `_delta_write_futs`.
The existing `_checkpointer_put_after_previous` already drains both
before calling `saver.put`, so `final_checkpoint` is structurally
guaranteed to land last — readers never see a partial view.

**Latent bugs fixed (previously masked by force-snapshot)**

- **Sync drain race**: `SyncPregelLoop` now initializes
`_delta_write_futs = []` in `__enter__` and drains it in sync
`_checkpointer_put_after_previous` before `put`, mirroring the async
version. Without this, a multi-worker `BackgroundExecutor` could publish
a checkpoint before the writes that produced it.
- **Count double-bump in exit mode**: in `_put_checkpoint`,
`delta_updates_since_snapshot` was being incremented twice for the last
superstep — once by the intermediate `after_tick` call, once by
`_suppress_interrupt`. Force-snapshot used to reset all counts to 0 so
this never persisted; without it, snapshots would fire one superstep
early after every exit-mode run. Fixed by gating the count-bump behind
`not exiting`.

**Pre-existing input-durability gap**

- In the plain (non-Command) input path of `_first`, delta-channel input
writes are now persisted via `put_writes` (mirroring the Command path),
so sub-frequency inputs survive a `get_state` on resumed runs in
`sync`/`async` durability. Note: first-run `sync`/`async` still has the
same gap (writes orphan on the synthetic-empty parent id). That's
flagged as a follow-up — out of scope for this PR.

## Test plan

- Existing `tests/test_pregel.py` and `tests/test_pregel_async.py` pass
unchanged.
- Existing `tests/test_channels.py` (29 tests) and
`tests/test_delta_channel_migration.py` pass unchanged.
- New `tests/test_exit_delta_persistence.py` (11 tests) covers:
- **Write-path**: zero-write exit (no stub), all-snapshot first run (no
stub), sub-freq first run (single shared stub), sub-freq resumed run
(anchor on saved parent), sync-vs-exit count parity, mixed
snapshot/non-snapshot channels, snapshot fires at frequency.
- **Read-path**: K-run replay chain reads correctly across
stub→saved-parent transition; metadata `delta_updates_since_snapshot`
round-trips correctly; mixed sync/exit durability alternation produces
correct final state; snapshot+tail-deltas combination reads correctly.
- `make format && make lint && make test` in `libs/langgraph/`.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Sydney Runkle <sydneymarierunkle@gmail.com>
2026-05-07 09:47:01 -07:00
Asamu DavidandGitHub 95b41d058f release: bump cli version (#7734)
## Summary

This release adds the following to the`langgraph deploy` command:
- json event output 
- non-interactive mode
2026-05-07 17:29:13 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>John KennedyClaude Opus 4.7
e49c093f48 chore(deps): bump ty from 0.0.23 to 0.0.33 in /libs/sdk-py (#7666)
Bumps the minor-and-patch group with 1 update in the /libs/sdk-py
directory: [ty](https://github.com/astral-sh/ty).

Updates `ty` from 0.0.23 to 0.0.33
<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.33</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-28.</p>
<h3>Notable changes</h3>
<ul>
<li>
<p>ty now prefers the declared type of an annotated assignment in more
situations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24802">#24802</a>).
Consider this example:</p>
<pre lang="py"><code>from some_library import untyped_function
<p>threshold: int | None = 0
result: str = untyped_function()
</code></pre></p>
<p>ty previously favored the <em>inferred</em> type of the right hand
side expression when <code>threshold</code> and <code>result</code> were
used. This is useful for <code>threshold</code>, as it allows something
like <code>threshold += 1</code> to work without an error: we know that
<code>threshold</code> could later become <code>None</code>, but
<em>right now</em>, we see that it is an <code>int</code>. However, for
<code>result</code>, the inferred type is <code>Unknown</code>. This is
<em>not</em> a useful type and it can lead to false negatives. Starting
with this release, ty will therefore prefer
the declared type <em>if the inferred and declared types are mutually
assignable</em>. In the above example, <code>threshold</code> will still
be inferred as <code>int</code> (or rather <code>Literal[1]</code>), but
<code>result</code> will now be inferred as <code>str</code>. If you
previously added <code>cast</code>s to work around this behavior, you
should be able to remove them after upgrading.</p>
</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Fix reporting of annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24811">#24811</a>)</li>
<li>Fix project and workspace selection (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24824">#24824</a>)</li>
<li>Fix go-to definition for generic classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24714">#24714</a>)</li>
<li>Fix receiver coloring for aliased decorators (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24884">#24884</a>)</li>
</ul>
<h3>LSP server</h3>
<ul>
<li>Add support for go-to definition in literal enum member inlay hints
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24792">#24792</a>)</li>
<li>Add support for &quot;baking&quot; keyword argument inlay hints into
the source code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24667">#24667</a>)</li>
<li>Don't allow inlay hint edits when introducing a non global scope
symbol (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24797">#24797</a>)</li>
<li>Omit semantic highlighting for unresolved symbols (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24718">#24718</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Support narrowing with aliased conditional expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24302">#24302</a>)</li>
<li>Model short-circuiting control flow in Boolean expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24458">#24458</a>)</li>
<li>Handle <code>finally</code> blocks where all
<code>try</code>/<code>except</code> blocks are terminal (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24882">#24882</a>)</li>
<li>Detect invalid <code>ClassVar</code> vs instance-attribute overrides
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24767">#24767</a>)</li>
<li>Emit diagnostic for invalid uses of <code>Unpack[...]</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24868">#24868</a>)</li>
<li>Infer lambda parameter types with <code>Callable</code> type context
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24317">#24317</a>)</li>
<li>Support <code>**</code> unpacking of <code>TypedDict</code> in
dict-literal assignments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24703">#24703</a>)</li>
<li>Support <code>Unpack[TypedDict]</code> in <code>**kwargs</code>
signatures (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24653">#24653</a>)</li>
<li>Treat <code>[*xs]</code> as an irrefutable pattern when matching on
<code>Sequence</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24787">#24787</a>)</li>
<li>Improve generics solving for unions in invariant positions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24698">#24698</a>)</li>
<li>Improve generics solving for unions when matching against protocols
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24837">#24837</a>)</li>
</ul>
<h3>Diagnostics</h3>
<!-- 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.33</h2>
<p>Released on 2026-04-28.</p>
<h3>Notable changes</h3>
<ul>
<li>
<p>ty now prefers the declared type of an annotated assignment in more
situations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24802">#24802</a>).
Consider this example:</p>
<pre lang="py"><code>from some_library import untyped_function
<p>threshold: int | None = 0
result: str = untyped_function()
</code></pre></p>
<p>ty previously favored the <em>inferred</em> type of the right hand
side expression when <code>threshold</code> and <code>result</code> were
used. This is useful for <code>threshold</code>, as it allows something
like <code>threshold += 1</code> to work without an error: we know that
<code>threshold</code> could later become <code>None</code>, but
<em>right now</em>, we see that it is an <code>int</code>. However, for
<code>result</code>, the inferred type is <code>Unknown</code>. This is
<em>not</em> a useful type and it can lead to false negatives. Starting
with this release, ty will therefore prefer
the declared type <em>if the inferred and declared types are mutually
assignable</em>. In the above example, <code>threshold</code> will still
be inferred as <code>int</code> (or rather <code>Literal[1]</code>), but
<code>result</code> will now be inferred as <code>str</code>. If you
previously added <code>cast</code>s to work around this behavior, you
should be able to remove them after upgrading.</p>
</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Fix reporting of annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24811">#24811</a>)</li>
<li>Fix project and workspace selection (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24824">#24824</a>)</li>
<li>Fix go-to definition for generic classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24714">#24714</a>)</li>
<li>Fix receiver coloring for aliased decorators (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24884">#24884</a>)</li>
</ul>
<h3>LSP server</h3>
<ul>
<li>Add support for go-to definition in literal enum member inlay hints
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24792">#24792</a>)</li>
<li>Add support for &quot;baking&quot; keyword argument inlay hints into
the source code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24667">#24667</a>)</li>
<li>Don't allow inlay hint edits when introducing a non global scope
symbol (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24797">#24797</a>)</li>
<li>Omit semantic highlighting for unresolved symbols (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24718">#24718</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Support narrowing with aliased conditional expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24302">#24302</a>)</li>
<li>Model short-circuiting control flow in Boolean expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24458">#24458</a>)</li>
<li>Handle <code>finally</code> blocks where all
<code>try</code>/<code>except</code> blocks are terminal (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24882">#24882</a>)</li>
<li>Detect invalid <code>ClassVar</code> vs instance-attribute overrides
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24767">#24767</a>)</li>
<li>Emit diagnostic for invalid uses of <code>Unpack[...]</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24868">#24868</a>)</li>
<li>Infer lambda parameter types with <code>Callable</code> type context
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24317">#24317</a>)</li>
<li>Support <code>**</code> unpacking of <code>TypedDict</code> in
dict-literal assignments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24703">#24703</a>)</li>
<li>Support <code>Unpack[TypedDict]</code> in <code>**kwargs</code>
signatures (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24653">#24653</a>)</li>
<li>Treat <code>[*xs]</code> as an irrefutable pattern when matching on
<code>Sequence</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24787">#24787</a>)</li>
<li>Improve generics solving for unions in invariant positions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24698">#24698</a>)</li>
<li>Improve generics solving for unions when matching against protocols
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24837">#24837</a>)</li>
</ul>
<h3>Diagnostics</h3>
<ul>
<li>Add error context to <code>invalid-return-type</code> diagnostics,
<code>invalid-yield</code> diagnostics, attribute assignment diagnostics
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24770">#24770</a>,
<a
href="https://redirect.github.com/astral-sh/ruff/pull/24771">#24771</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/c512d8425418a2170e92aa7fbbd70952d4e04118"><code>c512d84</code></a>
Bump version to 0.0.33 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3368">#3368</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/4cd7b334b90eba09042700e7b654044c2d6bcd15"><code>4cd7b33</code></a>
Upgrade Depot runners from macOS 14 to 15 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3363">#3363</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/c78b8324515bf15a662f61e1d74fd85484d67e8c"><code>c78b832</code></a>
Update rui314/setup-mold digest to 9c9c13b (<a
href="https://redirect.github.com/astral-sh/ty/issues/3342">#3342</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/dea338134aef96d741a48886e414f622dbf10426"><code>dea3381</code></a>
Update actions/cache action to v5.0.5 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3343">#3343</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/d451af477bd5517e132609c3b016799d697f4182"><code>d451af4</code></a>
update typing-features and faqs (<a
href="https://redirect.github.com/astral-sh/ty/issues/3335">#3335</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/052d70bc1a7457f50c5acbafe8de7a846f7d5e77"><code>052d70b</code></a>
Update prek dependencies (<a
href="https://redirect.github.com/astral-sh/ty/issues/3344">#3344</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/66b5e878163ce4ab4810a45a2679a98011932b8b"><code>66b5e87</code></a>
Update astral-sh/setup-uv action to v8.1.0 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3345">#3345</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/7ec6712a6f02d0255de4c7f0066d5bb23987a9bb"><code>7ec6712</code></a>
Add a 'Diagnostics improvements' section to the changelogs (<a
href="https://redirect.github.com/astral-sh/ty/issues/3309">#3309</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/978dfdb38dfb568943f73779d769a965c1d5a397"><code>978dfdb</code></a>
Add version metadata publishing to the release process (<a
href="https://redirect.github.com/astral-sh/ty/issues/3292">#3292</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/4d1e1fc57ca8bfdcbcee513ba92135d2932eb279"><code>4d1e1fc</code></a>
Bump version to 0.0.32 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3302">#3302</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ty/compare/0.0.23...0.0.33">compare
view</a></li>
</ul>
</details>
<br />

---------

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: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:21:49 -04:00
Sydney RunkleandGitHub 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`
2026-05-07 06:44:54 -04:00
Sydney RunkleandGitHub 1a989f22bb fix(checkpoint-postgres): add column aliases to seed-blob branch of delta stage-2 UNION ALL (#7728)
## Summary

- Ports the langgraph API checkpointer fix to the OSS Postgres
checkpoint.
- The `'b'` (seed-blob) branch of `_build_delta_stage2_sql`'s `UNION
ALL` was missing column aliases (`AS _kind`, `AS checkpoint_id`, `AS
task_id`, `AS idx`).
- In PostgreSQL, `UNION ALL` column names are taken from the **first**
`SELECT`. When `channels_with_chain` is empty but `channels_with_seed`
is not (e.g. very first run of a DeltaChannel graph, or
`snapshot_frequency=1`), the `'b'` branch becomes the first `SELECT`, so
columns got default names (`text`, `null`) instead of the expected
aliases — causing `KeyError: '_kind'` in
`_build_delta_channels_writes_history`.

## Test plan

- [ ] Existing `checkpoint-postgres` delta channel tests pass (`make
test` in `libs/checkpoint-postgres`)
- [ ] Manually verified fix matches the correction suggested in the
upstream API PR review comment

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-06 14:36:30 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b1331fb9a6 chore(deps): bump jupyter-server from 2.17.0 to 2.18.0 in /libs/langgraph (#7713)
Bumps [jupyter-server](https://github.com/jupyter-server/jupyter_server)
from 2.17.0 to 2.18.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jupyter-server/jupyter_server/releases">jupyter-server's
releases</a>.</em></p>
<blockquote>
<h2>v2.18.0</h2>
<h2>2.18.0</h2>
<p>(<a
href="https://github.com/jupyter-server/jupyter_server/compare/v2.17.0...49b34392feaa97735b3b777e3baf8f22f2a14ed8">Full
Changelog</a>)</p>
<h3>Security patches</h3>
<ul>
<li>CVE-2026-40110 <a
href="https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-24qx-w28j-9m6p">https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-24qx-w28j-9m6p</a></li>
<li>CVE-2025-61669 <a
href="https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-qh7q-6qm3-653w">https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-qh7q-6qm3-653w</a></li>
<li>CVE-2026-40934 <a
href="https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-5mrq-x3x5-8v8f">https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-5mrq-x3x5-8v8f</a></li>
<li>CVE-2026-35397 <a
href="https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-5789-5fc7-67v3">https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-5789-5fc7-67v3</a></li>
</ul>
<h3>API and Breaking Changes</h3>
<ul>
<li>Add query param to sanitize HTML in GET /nbconvert/html <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1618">#1618</a>
(<a href="https://github.com/Yann-P"><code>@​Yann-P</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
</ul>
<h3>Enhancements made</h3>
<ul>
<li>Update handlers.py to fix ioloop blockers(sync file operations) <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1617">#1617</a>
(<a
href="https://github.com/zolyfarkas-fb"><code>@​zolyfarkas-fb</code></a>,
<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Add resolvePath API for resolving kernel-relative paths <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1331">#1331</a>
(<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/blink1073"><code>@​blink1073</code></a>)</li>
</ul>
<h3>Bugs fixed</h3>
<ul>
<li>Move check origin into a util function and add it to websocket <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1630">#1630</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/Yann-P"><code>@​Yann-P</code></a>)</li>
<li>Fix flaky test_restart_kernel by unsticking nudge() after
port-changing restart <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1628">#1628</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/claude"><code>@​claude</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>Try to fix flaky test &quot;test_restart_kernel&quot; <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1625">#1625</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Fix potential unraisable pytest error <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1624">#1624</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>fix: use %s placeholders in HTTPError to prevent Tornado from
doubling % in gateway URLs <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1620">#1620</a>
(<a
href="https://github.com/terminalchai"><code>@​terminalchai</code></a>,
<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/ptch314"><code>@​ptch314</code></a>)</li>
<li>Fix three file descriptor leaks in kernel connection lifecycle (<a
href="https://redirect.github.com/jupyter-server/jupyter_server/issues/1506">#1506</a>)
<a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1619">#1619</a>
(<a href="https://github.com/tonyx93"><code>@​tonyx93</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Use web.HTTPError for kernel restart failures <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1616">#1616</a>
(<a href="https://github.com/YDawn"><code>@​YDawn</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Handle EADDRINUSE and EACCES in _bind_http_server_tcp <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1613">#1613</a>
(<a href="https://github.com/YDawn"><code>@​YDawn</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Use st_birthtime for file created timestamp on macOS/BSD <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1594">#1594</a>
(<a href="https://github.com/ktaletsk"><code>@​ktaletsk</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Fix double write when refusing hidden files in contents handler <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1585">#1585</a>
(<a href="https://github.com/Krish-876"><code>@​Krish-876</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Close all sockets in _find_http_port explicitly <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1584">#1584</a>
(<a
href="https://github.com/MaryushSoroka"><code>@​MaryushSoroka</code></a>,
<a href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Fix writing on remote file systems with attribute cache <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1574">#1574</a>
(<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>Add IdentityProvider.cookie_secret_hook <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1569">#1569</a>
(<a href="https://github.com/emin63"><code>@​emin63</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>fix context pollution <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1561">#1561</a>
(<a href="https://github.com/dualc"><code>@​dualc</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>Fix gateway cookie handling <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1558">#1558</a>
(<a
href="https://github.com/kevin-bates"><code>@​kevin-bates</code></a>, <a
href="https://github.com/RRosio"><code>@​RRosio</code></a>, <a
href="https://github.com/lresende"><code>@​lresende</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>fix connection exception cause high cpu load <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1484">#1484</a>
(<a href="https://github.com/dualc"><code>@​dualc</code></a>, <a
href="https://github.com/lresende"><code>@​lresende</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
</ul>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Start to test on Python 3.13 and 3.14 <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1623">#1623</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Bump actions/create-github-app-token from 2 to 3 in the actions
group across 1 directory <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1621">#1621</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Bump brace-expansion from 1.1.12 to 1.1.13 <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1615">#1615</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Fix package spec for jupytext <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1614">#1614</a>
(<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>chore: update pre-commit hooks <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1607">#1607</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>try to fix ci on windows <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1600">#1600</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>run prerelease tests on 3.14 <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1599">#1599</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Pin sphinx to an older version (&lt;9) to fix docs <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1597">#1597</a>
(<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/jupyter-server/jupyter_server/blob/main/CHANGELOG.md">jupyter-server's
changelog</a>.</em></p>
<blockquote>
<h2>2.18.0</h2>
<p>(<a
href="https://github.com/jupyter-server/jupyter_server/compare/v2.9.1...49b34392feaa97735b3b777e3baf8f22f2a14ed8">Full
Changelog</a>)</p>
<h3>API and Breaking Changes</h3>
<ul>
<li>Add query param to sanitize HTML in GET /nbconvert/html <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1618">#1618</a>
(<a href="https://github.com/Yann-P"><code>@​Yann-P</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
</ul>
<h3>Enhancements made</h3>
<ul>
<li>Update handlers.py to fix ioloop blockers(sync file operations) <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1617">#1617</a>
(<a
href="https://github.com/zolyfarkas-fb"><code>@​zolyfarkas-fb</code></a>,
<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Avoid redundant call to <code>_get_os_path</code> in
<code>_dir_model</code> <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1547">#1547</a>
(<a href="https://github.com/joeyutong"><code>@​joeyutong</code></a>, <a
href="https://github.com/vidartf"><code>@​vidartf</code></a>)</li>
<li>Allow specifying extra params to scrub from logs <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1538">#1538</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/vidartf"><code>@​vidartf</code></a>)</li>
<li>Add a logger to the ExtensionPoint API <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1523">#1523</a>
(<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/vidartf"><code>@​vidartf</code></a>)</li>
<li>Allow user to update identity values <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1518">#1518</a>
(<a href="https://github.com/brichet"><code>@​brichet</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>If ServerApp.ip is ipv6 use [::1] as local_url <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1495">#1495</a>
(<a href="https://github.com/manics"><code>@​manics</code></a>, <a
href="https://github.com/afshin"><code>@​afshin</code></a>)</li>
<li>Better error message when starting kernel for session. <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1478">#1478</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/davidbrochart"><code>@​davidbrochart</code></a>,
<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Add a traitlet to disable recording HTTP request metrics <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1472">#1472</a>
(<a href="https://github.com/yuvipanda"><code>@​yuvipanda</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>prometheus: Expose 3 activity metrics <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1471">#1471</a>
(<a href="https://github.com/yuvipanda"><code>@​yuvipanda</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>Add prometheus info metrics listing server extensions + versions <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1470">#1470</a>
(<a href="https://github.com/yuvipanda"><code>@​yuvipanda</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>Add prometheus metric with version information <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1467">#1467</a>
(<a href="https://github.com/yuvipanda"><code>@​yuvipanda</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>Don't hide .so,.dylib files by default <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1457">#1457</a>
(<a href="https://github.com/nokados"><code>@​nokados</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>, <a
href="https://github.com/vidartf"><code>@​vidartf</code></a>)</li>
<li>Better hash format error message <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1442">#1442</a>
(<a href="https://github.com/fcollonval"><code>@​fcollonval</code></a>,
<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>Removing excessive logging from reading local files <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1420">#1420</a>
(<a href="https://github.com/lresende"><code>@​lresende</code></a>, <a
href="https://github.com/kevin-bates"><code>@​kevin-bates</code></a>)</li>
<li>Add async start hook to ExtensionApp API <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1417">#1417</a>
(<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/Darshan808"><code>@​Darshan808</code></a>, <a
href="https://github.com/bollwyvl"><code>@​bollwyvl</code></a>, <a
href="https://github.com/fcollonval"><code>@​fcollonval</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>Do not include token in dashboard link, when available <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1406">#1406</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>, <a
href="https://github.com/blink1073"><code>@​blink1073</code></a>)</li>
<li>Add an option to have authentication enabled for all endpoints by
default <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1392">#1392</a>
(<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/Wh1isper"><code>@​Wh1isper</code></a>, <a
href="https://github.com/blink1073"><code>@​blink1073</code></a>, <a
href="https://github.com/bollwyvl"><code>@​bollwyvl</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>, <a
href="https://github.com/yuvipanda"><code>@​yuvipanda</code></a>)</li>
<li>websockets: add configurations for ping interval and timeout <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1391">#1391</a>
(<a
href="https://github.com/oliver-sanders"><code>@​oliver-sanders</code></a>,
<a
href="https://github.com/blink1073"><code>@​blink1073</code></a>)</li>
<li>log extension import time at debug level unless it's actually slow
<a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1375">#1375</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/yuvipanda"><code>@​yuvipanda</code></a>)</li>
<li>Add support for async Authorizers (part 2) <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1374">#1374</a>
(<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/blink1073"><code>@​blink1073</code></a>)</li>
<li>Support async Authorizers <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1373">#1373</a>
(<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/blink1073"><code>@​blink1073</code></a>)</li>
<li>Support get file(notebook) md5 <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1363">#1363</a>
(<a href="https://github.com/Wh1isper"><code>@​Wh1isper</code></a>, <a
href="https://github.com/blink1073"><code>@​blink1073</code></a>, <a
href="https://github.com/bollwyvl"><code>@​bollwyvl</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>Update kernel env to reflect changes in session <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1354">#1354</a>
(<a href="https://github.com/blink1073"><code>@​blink1073</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>Add resolvePath API for resolving kernel-relative paths <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1331">#1331</a>
(<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/blink1073"><code>@​blink1073</code></a>)</li>
</ul>
<h3>Bugs fixed</h3>
<ul>
<li>Move check origin into a util function and add it to websocket <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1630">#1630</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/Yann-P"><code>@​Yann-P</code></a>)</li>
<li>Fix flaky test_restart_kernel by unsticking nudge() after
port-changing restart <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1628">#1628</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/claude"><code>@​claude</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>Try to fix flaky test &quot;test_restart_kernel&quot; <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1625">#1625</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Fix potential unraisable pytest error <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1624">#1624</a>
(<a href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>fix: use %s placeholders in HTTPError to prevent Tornado from
doubling % in gateway URLs <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1620">#1620</a>
(<a
href="https://github.com/terminalchai"><code>@​terminalchai</code></a>,
<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/ptch314"><code>@​ptch314</code></a>)</li>
<li>Fix three file descriptor leaks in kernel connection lifecycle (<a
href="https://redirect.github.com/jupyter-server/jupyter_server/issues/1506">#1506</a>)
<a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1619">#1619</a>
(<a href="https://github.com/tonyx93"><code>@​tonyx93</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Use web.HTTPError for kernel restart failures <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1616">#1616</a>
(<a href="https://github.com/YDawn"><code>@​YDawn</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
<li>Handle EADDRINUSE and EACCES in _bind_http_server_tcp <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1613">#1613</a>
(<a href="https://github.com/YDawn"><code>@​YDawn</code></a>, <a
href="https://github.com/Zsailer"><code>@​Zsailer</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Use st_birthtime for file created timestamp on macOS/BSD <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1594">#1594</a>
(<a href="https://github.com/ktaletsk"><code>@​ktaletsk</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Fix double write when refusing hidden files in contents handler <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1585">#1585</a>
(<a href="https://github.com/Krish-876"><code>@​Krish-876</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Close all sockets in _find_http_port explicitly <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1584">#1584</a>
(<a
href="https://github.com/MaryushSoroka"><code>@​MaryushSoroka</code></a>,
<a href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
<li>Fix writing on remote file systems with attribute cache <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1574">#1574</a>
(<a href="https://github.com/krassowski"><code>@​krassowski</code></a>,
<a href="https://github.com/Zsailer"><code>@​Zsailer</code></a>)</li>
<li>Add IdentityProvider.cookie_secret_hook <a
href="https://redirect.github.com/jupyter-server/jupyter_server/pull/1569">#1569</a>
(<a href="https://github.com/emin63"><code>@​emin63</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/0ceed45a803ca36735f0fcfeb6d9db9430a50aa0"><code>0ceed45</code></a>
Publish 2.18.0</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/49b34392feaa97735b3b777e3baf8f22f2a14ed8"><code>49b3439</code></a>
Move check origin into a util function and add it to websocket (<a
href="https://redirect.github.com/jupyter-server/jupyter_server/issues/1630">#1630</a>)</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/e2e08c845ddbc41fddcb2449601d2ecbd9dd5977"><code>e2e08c8</code></a>
Add test case for bad next URL format</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/624d6c0daf573e254a3fd69ca5318ce8194235bf"><code>624d6c0</code></a>
Delete outdated patch code</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/d825b93d9cf2da248d5baa6ca910611f275fa449"><code>d825b93</code></a>
Apply suggestion from <a
href="https://github.com/minrk"><code>@​minrk</code></a></li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/789fed081a489ff38c50ec9f6c0364cb677d4cd2"><code>789fed0</code></a>
patch open redirect in /login</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/2ee51eccf3ff2e27068cc0b7a39101eeedc4f665"><code>2ee51ec</code></a>
fix(CVE-2026-35397): path traversal when target dir starts with root
dir</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/057869a327c46730afede3eab0ca2d2e3e74acea"><code>057869a</code></a>
Fix allow_origin_pat to do full matching instead of prefix matching</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/4862199a0fffacd4d2e4a0c2e61fc8bb5ffa52f0"><code>4862199</code></a>
Add resolvePath API for resolving kernel-relative paths</li>
<li><a
href="https://github.com/jupyter-server/jupyter_server/commit/e31d51406de6a9b167b796ec79ab3315630d514a"><code>e31d514</code></a>
Bump actions/create-github-app-token from 2 to 3 in the actions group
across ...</li>
<li>Additional commits viewable in <a
href="https://github.com/jupyter-server/jupyter_server/compare/v2.17.0...v2.18.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=jupyter-server&package-manager=uv&previous-version=2.17.0&new-version=2.18.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 21:58:10 -07:00
Asamu DavidandGitHub b333d4c838 feat(cli): support studio deploy (#7394)
# Description 

Supports deploying to langsmith from langgraph studio 

Adds the following to the langgraph deploy command:
- json event output 
- non-interactive mode
2026-05-06 03:20:50 +01:00
Sydney RunkleandGitHub 86baa5d08e feat(checkpoint-sqlite): override get_delta_channel_history with streaming walk (#7702)
## 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)
2026-05-05 15:30:57 -04:00
open-swe[bot]GitHubopen-swe[bot] <open-swe@users.noreply.github.com>
0ae01c7366 release(sdk-py): 0.3.14 (#7712)
Cuts a release for langgraph-sdk to publish #7704 (`return_minimal` on
`threads.update`). Needed by langchain-ai/langchainplus#24203 to swap
Fleet Python `threads.update` callsites from raw `Prefer` headers to the
first-class `return_minimal=True` parameter.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-05 10:47:08 -07:00
Naomi PentrelandGitHub 37014c09b9 docs: update README.md (#7710) 2026-05-05 17:58:37 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8c0e498eaf chore(deps-dev): bump the minor-and-patch group across 1 directory with 3 updates (#7698)
Bumps the minor-and-patch group with 3 updates in the /libs/prebuilt
directory: [psycopg-binary](https://github.com/psycopg/psycopg),
[ruff](https://github.com/astral-sh/ruff) and
[mypy](https://github.com/python/mypy).

Updates `psycopg-binary` from 3.3.3 to 3.3.4
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/psycopg/psycopg/blob/master/docs/news.rst">psycopg-binary's
changelog</a>.</em></p>
<blockquote>
<p>.. currentmodule:: psycopg</p>
<p>.. index::
single: Release notes
single: News</p>
<h1><code>psycopg</code> release notes</h1>
<h2>Current release</h2>
<p>Psycopg 3.3.4
^^^^^^^^^^^^^</p>
<ul>
<li>Fix possible spurious connection timeout in systems with very long
uptimes
in C extension
(🎫<code>[#1280](https://github.com/psycopg/psycopg/issues/1280)</code>).</li>
<li>Fix client-side adaptation of enums whose name require quotes

(🎫<code>[#1298](https://github.com/psycopg/psycopg/issues/1298)</code>).</li>
<li>Consistently populate <code>~Cursor.statusmessage</code> after
<code>~Cursor.executemany()</code>

(🎫<code>[#1302](https://github.com/psycopg/psycopg/issues/1302)</code>).</li>
</ul>
<p>Psycopg 3.3.3
^^^^^^^^^^^^^</p>
<ul>
<li>Retain <code>Error.pgconn</code> when raising a single exception for
multiple connection
attempt errors
(🎫<code>[#1246](https://github.com/psycopg/psycopg/issues/1246)</code>).</li>
<li>Return a proper error when server sends <code>ErrorResponse</code>
for a <code>Sync</code> after
a <code>Parse</code>
(🎫<code>[#1260](https://github.com/psycopg/psycopg/issues/1260)</code>).</li>
</ul>
<p>Psycopg 3.3.2
^^^^^^^^^^^^^</p>
<p>Fix race condition in adapters at startup
(🎫<code>[#1230](https://github.com/psycopg/psycopg/issues/1230)</code>).</p>
<p>Psycopg 3.3.1
^^^^^^^^^^^^^</p>
<p>Fix iteration on server-side cursors
(🎫<code>[#1226](https://github.com/psycopg/psycopg/issues/1226)</code>).</p>
<h2>Psycopg 3.3.0</h2>
<p>.. rubric:: New top-level features</p>
<ul>
<li>Add :ref:<code>template strings queries
\&lt;template-strings&gt;</code>
(🎫<code>[#1054](https://github.com/psycopg/psycopg/issues/1054)</code>).</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/psycopg/psycopg/commit/83f110367cdd249cc0a352e2246ecea9e878e5a0"><code>83f1103</code></a>
chore: bump psycopg_pool package version to 3.3.1</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/18287707f56a2b4f0817d9c23d137f5d69db6e31"><code>1828770</code></a>
chore: bump psycopg package version to 3.3.4</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/8be14bbc18f74de7652606d2777e0950ec804cc0"><code>8be14bb</code></a>
Merge pull request <a
href="https://redirect.github.com/psycopg/psycopg/issues/1301">#1301</a>
from oliverhaas/fix/sync-pool-open-race</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/aee0bf2659db77c31154acf583baf0a98675c192"><code>aee0bf2</code></a>
fix(pool): fix race in the construction of the sync ConnectionPool
lock</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/bc4d30375557fc32f2a91c2f8b75a5d21711ea89"><code>bc4d303</code></a>
chore(deps): bump the actions group across 1 directory with 4
updates</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/785379f196fc17473d312451a1fd4a06ef8dc895"><code>785379f</code></a>
fix: retain statusmessage after executemany with returning=False</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/8882a73871e65727549657085922d043a106127c"><code>8882a73</code></a>
perf: do less if X in Y: return Y[X] for cache-like patterns</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/2f785395c189e709da5fa74a02d3797bfb9db6a4"><code>2f78539</code></a>
Merge pull request <a
href="https://redirect.github.com/psycopg/psycopg/issues/1299">#1299</a>
from dvarrazzo/fix-camel-enum</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/37ef1dc4e6d19dc4af062d45a63243cb96a261c2"><code>37ef1dc</code></a>
test: skip test on crdb depending on precise regtype behaviour</li>
<li><a
href="https://github.com/psycopg/psycopg/commit/7f2f1d159df881260e3086fd61ea71343ca98e58"><code>7f2f1d1</code></a>
fix: fix client-side representation of enums requiring quotes</li>
<li>Additional commits viewable in <a
href="https://github.com/psycopg/psycopg/compare/3.3.3...3.3.4">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.15.9 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>0.15.11</h2>
<p>Released on 2026-04-16.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>ruff</code>] Ignore <code>RUF029</code> when function is
decorated with <code>asynccontextmanager</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24642">#24642</a>)</li>
<li>[<code>airflow</code>] Implement
<code>airflow-xcom-pull-in-template-string</code> (<code>AIR201</code>)
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/23583">#23583</a>)</li>
<li>[<code>flake8-bandit</code>] Fix <code>S103</code> false positives
and negatives in mask analysis (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24424">#24424</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/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.9...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `mypy` from 1.20.0 to 1.20.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's
changelog</a>.</em></p>
<blockquote>
<h3>Mypy 1.20.2</h3>
<ul>
<li>Use WAL with SQLite cache and fix close (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21154">21154</a>)</li>
<li>Adjust SQLite journal mode (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21217">21217</a>)</li>
<li>Correctly aggregate narrowing information on parent expressions
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21206">21206</a>)</li>
<li>Fix regression related to generic callables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21208">21208</a>)</li>
<li>Fix regression by avoiding widening types in some contexts
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21242">21242</a>)</li>
<li>Fix slicing in non-strict optional mode (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21282">21282</a>)</li>
<li>mypyc: Fix match statement semantics for &quot;or&quot; pattern
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21156">21156</a>)</li>
<li>mypyc: Fix issue with module dunder attributes (Piotr Sawicki, PR <a
href="https://redirect.github.com/python/mypy/pull/21275">21275</a>)</li>
<li>Initial support for Python 3.15.0a8 (Marc Mueller, PR <a
href="https://redirect.github.com/python/mypy/pull/21255">21255</a>)</li>
</ul>
<h3>Acknowledgements</h3>
<p>Thanks to all mypy contributors who contributed to this release:</p>
<ul>
<li>A5rocks</li>
<li>Aaron Wieczorek</li>
<li>Adam Turner</li>
<li>Ali Hamdan</li>
<li>asce</li>
<li>BobTheBuidler</li>
<li>Brent Westbrook</li>
<li>Brian Schubert</li>
<li>bzoracler</li>
<li>Chris Burroughs</li>
<li>Christoph Tyralla</li>
<li>Colin Watson</li>
<li>Donghoon Nam</li>
<li>E. M. Bray</li>
<li>Emma Smith</li>
<li>Ethan Sarp</li>
<li>George Ogden</li>
<li>getzze</li>
<li>grayjk</li>
<li>Gregor Riepl</li>
<li>Ivan Levkivskyi</li>
<li>James Hilliard</li>
<li>James Le Cuirot</li>
<li>Jeremy Nimmer</li>
<li>Joren Hammudoglu</li>
<li>Kai (Kazuya Ito)</li>
<li>kaushal trivedi</li>
<li>Kevin Kannammalil</li>
<li>Lukas Geiger</li>
<li>Łukasz Langa</li>
<li>Marc Mueller</li>
<li>Michael R. Crusoe</li>
<li>michaelm-openai</li>
<li>Neil Schemenauer</li>
<li>Piotr Sawicki</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/python/mypy/commit/145a062651b5f9996b75ef32b7040bd2e885ed82"><code>145a062</code></a>
Bump version to 1.20.2</li>
<li><a
href="https://github.com/python/mypy/commit/81cd49215c288eacb987de066f02daff2553b7c7"><code>81cd492</code></a>
Fix slicing with nonstrict optional (<a
href="https://redirect.github.com/python/mypy/issues/21282">#21282</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/908d3441eecbaa2a6193165317177db834d7ca1a"><code>908d344</code></a>
[mypyc] Set dunder attrs when adding module to sys.modules (<a
href="https://redirect.github.com/python/mypy/issues/21275">#21275</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/ba28610fac9d2b33be210ca8dcfe4bc47b7af424"><code>ba28610</code></a>
Initial support for Python 3.15.0a8 (<a
href="https://redirect.github.com/python/mypy/issues/21255">#21255</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/7b0e09f48dbd3717ed008a273cd17e8e960c2037"><code>7b0e09f</code></a>
Fix match statement semantics for &quot;or&quot; pattern (<a
href="https://redirect.github.com/python/mypy/issues/21156">#21156</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/92b74f226de62f7505f5ef5cb158e8ec9c58b8b7"><code>92b74f2</code></a>
Avoid widening types in conditional_types (<a
href="https://redirect.github.com/python/mypy/issues/21242">#21242</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/0dcbfaa40b0e360a16baea9cf851955375d91b54"><code>0dcbfaa</code></a>
Fix is_overlapping_types for generic callables (<a
href="https://redirect.github.com/python/mypy/issues/21208">#21208</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/210f518dede35292033ef0d387847406a0ccef8f"><code>210f518</code></a>
Correctly aggregate narrowing information on parent expressions (<a
href="https://redirect.github.com/python/mypy/issues/21206">#21206</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/c34530e53a10e385d8b0f1af4baa88a596b5ceaa"><code>c34530e</code></a>
Only set journal mode in coordinator (<a
href="https://redirect.github.com/python/mypy/issues/21217">#21217</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/79a3ec6d01b56a27c00e9b3320c2b1d4d73a77f9"><code>79a3ec6</code></a>
Use WAL with SQLite cache, fix close (<a
href="https://redirect.github.com/python/mypy/issues/21154">#21154</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python/mypy/compare/v1.20.0...v1.20.2">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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-04 22:41:05 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5657ecd0bf chore(deps): bump the minor-and-patch group across 1 directory with 3 updates (#7670)
Bumps the minor-and-patch group with 3 updates in the /libs/checkpoint
directory: [langchain-core](https://github.com/langchain-ai/langchain),
[ruff](https://github.com/astral-sh/ruff) and
[mypy](https://github.com/python/mypy).

Updates `langchain-core` from 1.2.28 to 1.3.2
<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.3.2</h2>
<p>Changes since langchain-core==1.3.1</p>
<p>release(core): 1.3.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36990">#36990</a>)
feat(core): add content-block-centric streaming (v2) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36834">#36834</a>)</p>
<h2>langchain-core==1.3.1</h2>
<p>Changes since langchain-core==1.3.0</p>
<p>release(core): 1.3.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36972">#36972</a>)
feat(core): allow _format_output to pass through list of ToolOutputMixin
instances (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36963">#36963</a>)
chore: bump nbconvert from 7.17.0 to 7.17.1 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36923">#36923</a>)
feat(core): Update inheritance behavior for tracer metadata for special
keys (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36900">#36900</a>)
chore: bump langsmith from 0.7.13 to 0.7.31 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36813">#36813</a>)</p>
<h2>langchain-core==1.3.0</h2>
<p>Changes since langchain-core==1.2.31</p>
<p>release(core): release 1.3.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36851">#36851</a>)
release(core): 1.3.0a3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36829">#36829</a>)
chore(core): keep checkpoint_ns behavior in streaming metadata for
backwards compat (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36828">#36828</a>)
feat(core): Add chat model and LLM invocation params to traceable
metadata (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36771">#36771</a>)
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36816">#36816</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore(core): harden private SSRF utilities (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36768">#36768</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore: bump pytest from 9.0.2 to 9.0.3 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36719">#36719</a>)
release(core): 1.3.0.a2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36698">#36698</a>)
fix(core): Use reference counting for storing inherited run trees to
support garbage collection (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36660">#36660</a>)
docs(core): nit (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36685">#36685</a>)
release(core): 1.3.0a1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36656">#36656</a>)
chore(core): reduce streaming metadata / perf (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36588">#36588</a>)</p>
<h2>langchain-core==1.3.0a3</h2>
<p>Initial release</p>
<p>release(core): 1.3.0a3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36829">#36829</a>)
chore(core): keep checkpoint_ns behavior in streaming metadata for
backwards compat (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36828">#36828</a>)
feat(core): Add chat model and LLM invocation params to traceable
metadata (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36771">#36771</a>)
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36816">#36816</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore(core): harden private SSRF utilities (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36768">#36768</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore: bump pytest from 9.0.2 to 9.0.3 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36719">#36719</a>)
release(core): 1.3.0.a2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36698">#36698</a>)
fix(core): Use reference counting for storing inherited run trees to
support garbage collection (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36660">#36660</a>)
docs(core): nit (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36685">#36685</a>)
release(core): 1.3.0a1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36656">#36656</a>)
chore(core): reduce streaming metadata / perf (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36588">#36588</a>)
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</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/fa0f0d8efa54005b9256f8d4b28d1ff770372901"><code>fa0f0d8</code></a>
release(core): 1.3.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36990">#36990</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9ce72eba9f6b3961d469a70b1b1980543f4cca91"><code>9ce72eb</code></a>
feat(core): add content-block-centric streaming (v2) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36834">#36834</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/889a45b664638ef646fa58257cfe263d44cb6c6a"><code>889a45b</code></a>
ci(infra): overlay local <code>langchain-*</code> installs for external
partners (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36989">#36989</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/ffaac42bf9e345ec1096d38965f36542a7b0467a"><code>ffaac42</code></a>
ci(infra): add <code>pytest-xdist</code> to partner test groups (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36988">#36988</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/cc2feb1aea239556fa427fd8bdabbae60f8279a6"><code>cc2feb1</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36982">#36982</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3dd0ad958eb2d5a51a4055e104598bb26aeb3b65"><code>3dd0ad9</code></a>
release(fireworks): 1.2.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36978">#36978</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7b09eb7bda4dbe99615ff5e5f74539aea682d718"><code>7b09eb7</code></a>
fix(fireworks): honor <code>max_retries</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36973">#36973</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/d30ef8a8aa2edd47c614838c6be362456be5c2a5"><code>d30ef8a</code></a>
feat(fireworks): populate <code>usage_metadata</code> on streaming (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36977">#36977</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/2715a7499a642c99901e6cfe5e9a6ea3aecedb0f"><code>2715a74</code></a>
fix(fireworks): swap undeployed Kimi K2 slug in integration tests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36975">#36975</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/2d3b49162caca316d9a24286421737471ea9e388"><code>2d3b491</code></a>
ci(infra): shorten working-directory dropdown labels (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36974">#36974</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.28...langchain-core==1.3.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.15.8 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>0.15.11</h2>
<p>Released on 2026-04-16.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>ruff</code>] Ignore <code>RUF029</code> when function is
decorated with <code>asynccontextmanager</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24642">#24642</a>)</li>
<li>[<code>airflow</code>] Implement
<code>airflow-xcom-pull-in-template-string</code> (<code>AIR201</code>)
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/23583">#23583</a>)</li>
<li>[<code>flake8-bandit</code>] Fix <code>S103</code> false positives
and negatives in mask analysis (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24424">#24424</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/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.8...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `mypy` from 1.20.0 to 1.20.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's
changelog</a>.</em></p>
<blockquote>
<h3>Mypy 1.20.2</h3>
<ul>
<li>Use WAL with SQLite cache and fix close (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21154">21154</a>)</li>
<li>Adjust SQLite journal mode (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21217">21217</a>)</li>
<li>Correctly aggregate narrowing information on parent expressions
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21206">21206</a>)</li>
<li>Fix regression related to generic callables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21208">21208</a>)</li>
<li>Fix regression by avoiding widening types in some contexts
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21242">21242</a>)</li>
<li>Fix slicing in non-strict optional mode (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21282">21282</a>)</li>
<li>mypyc: Fix match statement semantics for &quot;or&quot; pattern
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21156">21156</a>)</li>
<li>mypyc: Fix issue with module dunder attributes (Piotr Sawicki, PR <a
href="https://redirect.github.com/python/mypy/pull/21275">21275</a>)</li>
<li>Initial support for Python 3.15.0a8 (Marc Mueller, PR <a
href="https://redirect.github.com/python/mypy/pull/21255">21255</a>)</li>
</ul>
<h3>Acknowledgements</h3>
<p>Thanks to all mypy contributors who contributed to this release:</p>
<ul>
<li>A5rocks</li>
<li>Aaron Wieczorek</li>
<li>Adam Turner</li>
<li>Ali Hamdan</li>
<li>asce</li>
<li>BobTheBuidler</li>
<li>Brent Westbrook</li>
<li>Brian Schubert</li>
<li>bzoracler</li>
<li>Chris Burroughs</li>
<li>Christoph Tyralla</li>
<li>Colin Watson</li>
<li>Donghoon Nam</li>
<li>E. M. Bray</li>
<li>Emma Smith</li>
<li>Ethan Sarp</li>
<li>George Ogden</li>
<li>getzze</li>
<li>grayjk</li>
<li>Gregor Riepl</li>
<li>Ivan Levkivskyi</li>
<li>James Hilliard</li>
<li>James Le Cuirot</li>
<li>Jeremy Nimmer</li>
<li>Joren Hammudoglu</li>
<li>Kai (Kazuya Ito)</li>
<li>kaushal trivedi</li>
<li>Kevin Kannammalil</li>
<li>Lukas Geiger</li>
<li>Łukasz Langa</li>
<li>Marc Mueller</li>
<li>Michael R. Crusoe</li>
<li>michaelm-openai</li>
<li>Neil Schemenauer</li>
<li>Piotr Sawicki</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/python/mypy/commit/145a062651b5f9996b75ef32b7040bd2e885ed82"><code>145a062</code></a>
Bump version to 1.20.2</li>
<li><a
href="https://github.com/python/mypy/commit/81cd49215c288eacb987de066f02daff2553b7c7"><code>81cd492</code></a>
Fix slicing with nonstrict optional (<a
href="https://redirect.github.com/python/mypy/issues/21282">#21282</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/908d3441eecbaa2a6193165317177db834d7ca1a"><code>908d344</code></a>
[mypyc] Set dunder attrs when adding module to sys.modules (<a
href="https://redirect.github.com/python/mypy/issues/21275">#21275</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/ba28610fac9d2b33be210ca8dcfe4bc47b7af424"><code>ba28610</code></a>
Initial support for Python 3.15.0a8 (<a
href="https://redirect.github.com/python/mypy/issues/21255">#21255</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/7b0e09f48dbd3717ed008a273cd17e8e960c2037"><code>7b0e09f</code></a>
Fix match statement semantics for &quot;or&quot; pattern (<a
href="https://redirect.github.com/python/mypy/issues/21156">#21156</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/92b74f226de62f7505f5ef5cb158e8ec9c58b8b7"><code>92b74f2</code></a>
Avoid widening types in conditional_types (<a
href="https://redirect.github.com/python/mypy/issues/21242">#21242</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/0dcbfaa40b0e360a16baea9cf851955375d91b54"><code>0dcbfaa</code></a>
Fix is_overlapping_types for generic callables (<a
href="https://redirect.github.com/python/mypy/issues/21208">#21208</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/210f518dede35292033ef0d387847406a0ccef8f"><code>210f518</code></a>
Correctly aggregate narrowing information on parent expressions (<a
href="https://redirect.github.com/python/mypy/issues/21206">#21206</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/c34530e53a10e385d8b0f1af4baa88a596b5ceaa"><code>c34530e</code></a>
Only set journal mode in coordinator (<a
href="https://redirect.github.com/python/mypy/issues/21217">#21217</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/79a3ec6d01b56a27c00e9b3320c2b1d4d73a77f9"><code>79a3ec6</code></a>
Use WAL with SQLite cache, fix close (<a
href="https://redirect.github.com/python/mypy/issues/21154">#21154</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python/mypy/compare/v1.20.0...v1.20.2">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-04 22:39:39 -07:00
Quanzheng LongandGitHub 127c6c2c7b chore: "chore: minor clean up around checkpoint and delta channel" (#7706)
Reverts langchain-ai/langgraph#7705
2026-05-05 03:46:33 +00:00
Quanzheng LongandGitHub 0ae8870432 chore: minor clean up around checkpoint and delta channel (#7705) 2026-05-04 17:20:15 -07:00
Connor BraaandGitHub 60e305596d feat(sdk-py): add return_minimal to threads update (#7704)
Adds a first-class `return_minimal` option to the Python
`threads.update` clients. When enabled, the SDK sends `Prefer:
return=minimal` and returns `None` for the 204 response. Covers both
async and sync clients with focused tests.
2026-05-04 15:46:13 -07:00
Sydney RunkleandGitHub 125d10052c release: alpha bump (a4) for langgraph, checkpoint, checkpoint-postgres (#7701)
- Bumps `langgraph` 1.2.0a6 → 1.2.0a7
- Bumps `langgraph-checkpoint` 4.1.0a3 → 4.1.0a4
- Bumps `langgraph-checkpoint-postgres` 3.1.0a3 → 3.1.0a4
- Bumps min `langgraph-checkpoint` constraint in `langgraph` and
`checkpoint-postgres` to `>=4.1.0a4`
- Refreshes uv locks across the workspace
2026-05-04 15:26:20 -04:00
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>
2026-05-04 15:18:43 -04:00
Nick HollonandGitHub 35cea28707 release: alpha bump langgraph 1.2.0a6 (#7697) 2026-05-04 08:58:57 -04:00
Nick HollonandGitHub 3fff7bc928 feat(langgraph): forward kwargs through stream_events(version="v3") (#7696) 2026-05-04 08:52:52 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a0c4bdc3cb chore(deps): bump the minor-and-patch group across 1 directory with 6 updates (#7675)
Bumps the minor-and-patch group with 6 updates in the /libs/langgraph
directory:

| Package | From | To |
| --- | --- | --- |
| [xxhash](https://github.com/ifduyue/python-xxhash) | `3.6.0` | `3.7.0`
|
| [pydantic](https://github.com/pydantic/pydantic) | `2.12.5` | `2.13.3`
|
| [py-spy](https://github.com/benfred/py-spy) | `0.4.1` | `0.4.2` |
| [mypy](https://github.com/python/mypy) | `1.19.1` | `1.20.2` |
| [ruff](https://github.com/astral-sh/ruff) | `0.15.7` | `0.15.12` |
| [types-requests](https://github.com/python/typeshed) |
`2.32.4.20260324` | `2.33.0.20260408` |


Updates `xxhash` from 3.6.0 to 3.7.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ifduyue/python-xxhash/releases">xxhash's
releases</a>.</em></p>
<blockquote>
<h2>v3.7.0</h2>
<ul>
<li>Drop support for Python 3.7</li>
<li>Build armv7l manylinux/musllinux wheels</li>
<li>Build riscv64 manylinux/musllinux wheels</li>
<li>Build android and ios wheels</li>
</ul>
<hr />
<p>Full list of changes: <a
href="https://github.com/ifduyue/python-xxhash/compare/v3.6.0...v3.7.0">https://github.com/ifduyue/python-xxhash/compare/v3.6.0...v3.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ifduyue/python-xxhash/blob/master/CHANGELOG.rst">xxhash's
changelog</a>.</em></p>
<blockquote>
<p>v3.7.0 2025-04-25</p>
<pre><code>
- Drop support for Python 3.7
- Build armv7l manylinux/musllinux wheels
- Build riscv64 manylinux/musllinux wheels
- Build android and ios wheels
</code></pre>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/404589e3d5735591755d2e7f32c87a15f8724c02"><code>404589e</code></a>
Prepare 3.7.0</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/d3cd66446da1bea6de24b4edebc31a84cee45aab"><code>d3cd664</code></a>
Bump pypa/gh-action-pypi-publish from 1.13.0 to 1.14.0</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/0d7d943288e3ad86988d57002ec5ebd3ce8e61da"><code>0d7d943</code></a>
Bump pypa/cibuildwheel from 3.4.0 to 3.4.1</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/c4b8775fe0fd60598e4244ea0c522134cc3c3cf5"><code>c4b8775</code></a>
Bump actions/download-artifact from 7 to 8</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/1ae3e5b60e4cf1f5f78278183dde90dba932962a"><code>1ae3e5b</code></a>
Bump actions/upload-artifact from 6 to 7</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/8faaa340c9a647dd78ce1c6515097b28f744352b"><code>8faaa34</code></a>
Bump pypa/cibuildwheel from 3.3.1 to 3.4.0</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/4ba9b0cc343c50f903ff67918032971e0d9842c3"><code>4ba9b0c</code></a>
Bump docker/setup-qemu-action from 3 to 4</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/43bb2030f1a7fd389cc876037e653674b850e9e7"><code>43bb203</code></a>
Bump actions/download-artifact from 6 to 7</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/a7a3bd22f2a536220bd08c5765e2fe06e9210ba8"><code>a7a3bd2</code></a>
Bump actions/upload-artifact from 5 to 6</li>
<li><a
href="https://github.com/ifduyue/python-xxhash/commit/c35bf0125bfe2a11794d7d5440159ed8b124e0e1"><code>c35bf01</code></a>
Bump pypa/cibuildwheel from 3.3.0 to 3.3.1</li>
<li>Additional commits viewable in <a
href="https://github.com/ifduyue/python-xxhash/compare/v3.6.0...v3.7.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `pydantic` from 2.12.5 to 2.13.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pydantic/pydantic/releases">pydantic's
releases</a>.</em></p>
<blockquote>
<h2>v2.13.3 2026-04-20</h2>
<h2>v2.13.3 (2026-04-20)</h2>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Handle <code>AttributeError</code> subclasses with
<code>from_attributes</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13096">#13096</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pydantic/pydantic/compare/v2.13.2...v2.13.3">https://github.com/pydantic/pydantic/compare/v2.13.2...v2.13.3</a></p>
<h2>v2.13.2 2026-04-17</h2>
<h2>v2.13.2 (2026-04-17)</h2>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.field_name</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13084">#13084</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pydantic/pydantic/compare/v2.13.1...v2.13.2">https://github.com/pydantic/pydantic/compare/v2.13.1...v2.13.2</a></p>
<h2>v2.13.1 2026-04-15</h2>
<h2>v2.13.1 (2026-04-15)</h2>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.data</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/davidhewitt"><code>@​davidhewitt</code></a> in
<a
href="https://redirect.github.com/pydantic/pydantic/pull/13079">#13079</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pydantic/pydantic/compare/v2.13.0...v2.13.1">https://github.com/pydantic/pydantic/compare/v2.13.0...v2.13.1</a></p>
<h2>v2.13.0 2026-04-13</h2>
<h2>v2.13.0 (2026-04-13)</h2>
<p>The highlights of the v2.13 release are available in the <a
href="https://pydantic.dev/articles/pydantic-v2-13-release">blog
post</a>.
Several minor changes (considered non-breaking changes according to our
<a
href="https://pydantic.dev/docs/validation/2.13/get-started/version-policy/#pydantic-v2">versioning
policy</a>) are also included in this release. Make sure to look into
them before upgrading.</p>
<p>This release contains the updated <code>pydantic.v1</code> namespace,
matching version 1.10.26 which includes support for Python 3.14.</p>
<h3>What's Changed</h3>
<p>See the beta releases for all changes sinces 2.12.</p>
<h4>Packaging</h4>
<ul>
<li>Add zizmor for GitHub Actions workflow linting by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13039">#13039</a></li>
<li>Update jiter to v0.14.0 to fix a segmentation fault on musl Linux by
<a href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13064">#13064</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pydantic/pydantic/blob/main/HISTORY.md">pydantic's
changelog</a>.</em></p>
<blockquote>
<h2>v2.13.3 (2026-04-20)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.3">GitHub
release</a></p>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Handle <code>AttributeError</code> subclasses with
<code>from_attributes</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13096">#13096</a></li>
</ul>
<h2>v2.13.2 (2026-04-17)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.2">GitHub
release</a></p>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.field_name</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13084">#13084</a></li>
</ul>
<h2>v2.13.1 (2026-04-15)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.1">GitHub
release</a></p>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.data</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/davidhewitt"><code>@​davidhewitt</code></a> in
<a
href="https://redirect.github.com/pydantic/pydantic/pull/13079">#13079</a></li>
</ul>
<h2>v2.13.0 (2026-04-13)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.0">GitHub
release</a></p>
<p>The highlights of the v2.13 release are available in the <a
href="https://pydantic.dev/articles/pydantic-v2-13-release">blog
post</a>.
Several minor changes (considered non-breaking changes according to our
<a
href="https://pydantic.dev/docs/validation/2.13/get-started/version-policy/#pydantic-v2">versioning
policy</a>)
are also included in this release. Make sure to look into them before
upgrading.</p>
<p>This release contains the updated <code>pydantic.v1</code> namespace,
matching version 1.10.26 which includes support for Python 3.14.</p>
<h3>What's Changed</h3>
<p>See the beta releases for all changes sinces 2.12.</p>
<h4>New Features</h4>
<ul>
<li>Allow default factories of private attributes to take validated
model data by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13013">#13013</a></li>
</ul>
<h4>Changes</h4>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pydantic/pydantic/commit/9e9a11132c980c4ec84ff6d2df06d4c1a55ca8f3"><code>9e9a111</code></a>
Fix backported test</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/1ec8c6aa0cae1c78409c6f31af738a24e16c7124"><code>1ec8c6a</code></a>
Prepare release v2.13.3</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/fb4f2044436e9fa4ff9745479ce263016a99c4d6"><code>fb4f204</code></a>
Handle <code>AttributeError</code> subclasses with
<code>from_attributes</code></li>
<li><a
href="https://github.com/pydantic/pydantic/commit/ca3ddd1149bd3b14273922a29da174804496a055"><code>ca3ddd1</code></a>
Prepare release v2.13.2</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/000e823a3ab7545b0c6064fde34effdcd02044ab"><code>000e823</code></a>
Fix <code>ValidationInfo.field_name</code> missing with
<code>model_validate_json()</code></li>
<li><a
href="https://github.com/pydantic/pydantic/commit/d45d8be3ffb6dfc6d0c64da6980140c91ff841fb"><code>d45d8be</code></a>
Prepare release 2.13.1</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/54aca609ce58e73dfe1c681d93432fbe3a4621da"><code>54aca60</code></a>
Fix <code>ValidationInfo.data</code> missing with
<code>model_validate_json()</code></li>
<li><a
href="https://github.com/pydantic/pydantic/commit/46bf4fa648af3a1fbf4603a37f210e9d9c618357"><code>46bf4fa</code></a>
Fix Pydantic release workflow (<a
href="https://redirect.github.com/pydantic/pydantic/issues/13067">#13067</a>)</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/1b359edab09c623464d23c6fd2503ae5ff276d43"><code>1b359ed</code></a>
Prepare release v2.13.0 (<a
href="https://redirect.github.com/pydantic/pydantic/issues/13065">#13065</a>)</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/b1bf19445d8ac144a7a0e82674d2d87eebab6c18"><code>b1bf194</code></a>
Fix model equality when using runtime <code>extra</code> configuration
(<a
href="https://redirect.github.com/pydantic/pydantic/issues/13062">#13062</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pydantic/pydantic/compare/v2.12.5...v2.13.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `py-spy` from 0.4.1 to 0.4.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/benfred/py-spy/releases">py-spy's
releases</a>.</em></p>
<blockquote>
<h2>v0.4.2</h2>
<h2>Changes</h2>
<h2>🚀 Features</h2>
<ul>
<li>Add Python 3.14 support <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/833">#833</a>)</li>
<li>Add support for native extensions on linux aarch64 <a
href="https://github.com/tehrengruber"><code>@​tehrengruber</code></a>
(<a
href="https://redirect.github.com/benfred/py-spy/issues/779">#779</a>)</li>
<li>Use Py_Version symbol for detecting the python version <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/835">#835</a>)</li>
<li>Show backtrace on errors when RUST_BACKTRACE=1 environment variable
is set <a href="https://github.com/benfred"><code>@​benfred</code></a>
(<a
href="https://redirect.github.com/benfred/py-spy/issues/841">#841</a>)</li>
</ul>
<h2>🐛 Bug Fixes</h2>
<ul>
<li>Fix getting symbols from OSX universal binaries <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/843">#843</a>)</li>
<li>Fix intermittent OSX CI errors <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/842">#842</a>)</li>
<li>Fix corrupted <code>subprocess</code> output <a
href="https://github.com/ravwojdyla"><code>@​ravwojdyla</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/832">#832</a>)</li>
</ul>
<h2>🧰 Maintenance</h2>
<ul>
<li>Minor code cleanups <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/844">#844</a>)</li>
<li>Set github actions workflow permissions <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/840">#840</a>)</li>
<li>Fix release drafter config <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/839">#839</a>)</li>
<li>Update dependencies <a
href="https://github.com/benfred"><code>@​benfred</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/838">#838</a>)</li>
<li>Bump the github-actions group across 1 directory with 6 updates @<a
href="https://github.com/apps/dependabot">dependabot[bot]</a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/836">#836</a>)</li>
<li>fix: make build and cargo clippy happy <a
href="https://github.com/yihong0618"><code>@​yihong0618</code></a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/795">#795</a>)</li>
<li>Bump the github-actions group with 2 updates @<a
href="https://github.com/apps/dependabot">dependabot[bot]</a> (<a
href="https://redirect.github.com/benfred/py-spy/issues/798">#798</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/benfred/py-spy/commit/d230d8208d652ba168256ffb66170d1c6cd8b650"><code>d230d82</code></a>
Bump version: 0.4.1 → 0.4.2</li>
<li><a
href="https://github.com/benfred/py-spy/commit/48af4e5d69bd016db193f9113c041b9baf105130"><code>48af4e5</code></a>
Minor code cleanups (<a
href="https://redirect.github.com/benfred/py-spy/issues/844">#844</a>)</li>
<li><a
href="https://github.com/benfred/py-spy/commit/13c8863569cdd220c3d1a611f7c87ee515a845a9"><code>13c8863</code></a>
Add support for native extensions on linux aarch64 (<a
href="https://redirect.github.com/benfred/py-spy/issues/779">#779</a>)</li>
<li><a
href="https://github.com/benfred/py-spy/commit/2982dab1df02958add15e7c9c60100c22580a2a8"><code>2982dab</code></a>
Use Py_Version symbol for detecting the python version (<a
href="https://redirect.github.com/benfred/py-spy/issues/835">#835</a>)</li>
<li><a
href="https://github.com/benfred/py-spy/commit/c478370afbd0ee6bb3706d00e7b33295bcaf4e22"><code>c478370</code></a>
Fix getting symbols from OSX universal binaries (<a
href="https://redirect.github.com/benfred/py-spy/issues/843">#843</a>)</li>
<li><a
href="https://github.com/benfred/py-spy/commit/344703d74c720c62e3990a635705df485c0cf078"><code>344703d</code></a>
Set github actions workflow permissions (<a
href="https://redirect.github.com/benfred/py-spy/issues/840">#840</a>)</li>
<li><a
href="https://github.com/benfred/py-spy/commit/f84084a403cd172d7593a75fd1128966c43f7bea"><code>f84084a</code></a>
Fix intermittent OSX CI errors (<a
href="https://redirect.github.com/benfred/py-spy/issues/842">#842</a>)</li>
<li><a
href="https://github.com/benfred/py-spy/commit/d7d22b616a6918a57f7ef11fbcb85afac41b2758"><code>d7d22b6</code></a>
Show backtrace on errors when RUST_BACKTRACE=1 environment variable is
set (#...</li>
<li><a
href="https://github.com/benfred/py-spy/commit/4580345efeb494f8d5efe251506f70d2af0c5f41"><code>4580345</code></a>
Fix corrupted <code>subprocess</code> output (<a
href="https://redirect.github.com/benfred/py-spy/issues/832">#832</a>)</li>
<li><a
href="https://github.com/benfred/py-spy/commit/39bbc405d41fc02deb4bc62ff39b4e010e4ab07f"><code>39bbc40</code></a>
Fix release drafter config (<a
href="https://redirect.github.com/benfred/py-spy/issues/839">#839</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/benfred/py-spy/compare/v0.4.1...v0.4.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `mypy` from 1.19.1 to 1.20.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's
changelog</a>.</em></p>
<blockquote>
<h3>Mypy 1.20.2</h3>
<ul>
<li>Use WAL with SQLite cache and fix close (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21154">21154</a>)</li>
<li>Adjust SQLite journal mode (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21217">21217</a>)</li>
<li>Correctly aggregate narrowing information on parent expressions
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21206">21206</a>)</li>
<li>Fix regression related to generic callables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21208">21208</a>)</li>
<li>Fix regression by avoiding widening types in some contexts
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21242">21242</a>)</li>
<li>Fix slicing in non-strict optional mode (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21282">21282</a>)</li>
<li>mypyc: Fix match statement semantics for &quot;or&quot; pattern
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21156">21156</a>)</li>
<li>mypyc: Fix issue with module dunder attributes (Piotr Sawicki, PR <a
href="https://redirect.github.com/python/mypy/pull/21275">21275</a>)</li>
<li>Initial support for Python 3.15.0a8 (Marc Mueller, PR <a
href="https://redirect.github.com/python/mypy/pull/21255">21255</a>)</li>
</ul>
<h3>Acknowledgements</h3>
<p>Thanks to all mypy contributors who contributed to this release:</p>
<ul>
<li>A5rocks</li>
<li>Aaron Wieczorek</li>
<li>Adam Turner</li>
<li>Ali Hamdan</li>
<li>asce</li>
<li>BobTheBuidler</li>
<li>Brent Westbrook</li>
<li>Brian Schubert</li>
<li>bzoracler</li>
<li>Chris Burroughs</li>
<li>Christoph Tyralla</li>
<li>Colin Watson</li>
<li>Donghoon Nam</li>
<li>E. M. Bray</li>
<li>Emma Smith</li>
<li>Ethan Sarp</li>
<li>George Ogden</li>
<li>getzze</li>
<li>grayjk</li>
<li>Gregor Riepl</li>
<li>Ivan Levkivskyi</li>
<li>James Hilliard</li>
<li>James Le Cuirot</li>
<li>Jeremy Nimmer</li>
<li>Joren Hammudoglu</li>
<li>Kai (Kazuya Ito)</li>
<li>kaushal trivedi</li>
<li>Kevin Kannammalil</li>
<li>Lukas Geiger</li>
<li>Łukasz Langa</li>
<li>Marc Mueller</li>
<li>Michael R. Crusoe</li>
<li>michaelm-openai</li>
<li>Neil Schemenauer</li>
<li>Piotr Sawicki</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/python/mypy/commit/145a062651b5f9996b75ef32b7040bd2e885ed82"><code>145a062</code></a>
Bump version to 1.20.2</li>
<li><a
href="https://github.com/python/mypy/commit/81cd49215c288eacb987de066f02daff2553b7c7"><code>81cd492</code></a>
Fix slicing with nonstrict optional (<a
href="https://redirect.github.com/python/mypy/issues/21282">#21282</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/908d3441eecbaa2a6193165317177db834d7ca1a"><code>908d344</code></a>
[mypyc] Set dunder attrs when adding module to sys.modules (<a
href="https://redirect.github.com/python/mypy/issues/21275">#21275</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/ba28610fac9d2b33be210ca8dcfe4bc47b7af424"><code>ba28610</code></a>
Initial support for Python 3.15.0a8 (<a
href="https://redirect.github.com/python/mypy/issues/21255">#21255</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/7b0e09f48dbd3717ed008a273cd17e8e960c2037"><code>7b0e09f</code></a>
Fix match statement semantics for &quot;or&quot; pattern (<a
href="https://redirect.github.com/python/mypy/issues/21156">#21156</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/92b74f226de62f7505f5ef5cb158e8ec9c58b8b7"><code>92b74f2</code></a>
Avoid widening types in conditional_types (<a
href="https://redirect.github.com/python/mypy/issues/21242">#21242</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/0dcbfaa40b0e360a16baea9cf851955375d91b54"><code>0dcbfaa</code></a>
Fix is_overlapping_types for generic callables (<a
href="https://redirect.github.com/python/mypy/issues/21208">#21208</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/210f518dede35292033ef0d387847406a0ccef8f"><code>210f518</code></a>
Correctly aggregate narrowing information on parent expressions (<a
href="https://redirect.github.com/python/mypy/issues/21206">#21206</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/c34530e53a10e385d8b0f1af4baa88a596b5ceaa"><code>c34530e</code></a>
Only set journal mode in coordinator (<a
href="https://redirect.github.com/python/mypy/issues/21217">#21217</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/79a3ec6d01b56a27c00e9b3320c2b1d4d73a77f9"><code>79a3ec6</code></a>
Use WAL with SQLite cache, fix close (<a
href="https://redirect.github.com/python/mypy/issues/21154">#21154</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python/mypy/compare/v1.19.1...v1.20.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.15.7 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>0.15.11</h2>
<p>Released on 2026-04-16.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>ruff</code>] Ignore <code>RUF029</code> when function is
decorated with <code>asynccontextmanager</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24642">#24642</a>)</li>
<li>[<code>airflow</code>] Implement
<code>airflow-xcom-pull-in-template-string</code> (<code>AIR201</code>)
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/23583">#23583</a>)</li>
<li>[<code>flake8-bandit</code>] Fix <code>S103</code> false positives
and negatives in mask analysis (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24424">#24424</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/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.7...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `types-requests` from 2.32.4.20260324 to 2.33.0.20260408
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/python/typeshed/commits">compare view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-03 15:15:43 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0d0324ad3a chore(deps): bump the minor-and-patch group in /libs/checkpoint-sqlite with 2 updates (#7668)
Bumps the minor-and-patch group in /libs/checkpoint-sqlite with 2
updates: [ruff](https://github.com/astral-sh/ruff) and
[mypy](https://github.com/python/mypy).

Updates `ruff` from 0.15.8 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>0.15.11</h2>
<p>Released on 2026-04-16.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>ruff</code>] Ignore <code>RUF029</code> when function is
decorated with <code>asynccontextmanager</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24642">#24642</a>)</li>
<li>[<code>airflow</code>] Implement
<code>airflow-xcom-pull-in-template-string</code> (<code>AIR201</code>)
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/23583">#23583</a>)</li>
<li>[<code>flake8-bandit</code>] Fix <code>S103</code> false positives
and negatives in mask analysis (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24424">#24424</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/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.8...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `mypy` from 1.20.0 to 1.20.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's
changelog</a>.</em></p>
<blockquote>
<h3>Mypy 1.20.2</h3>
<ul>
<li>Use WAL with SQLite cache and fix close (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21154">21154</a>)</li>
<li>Adjust SQLite journal mode (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21217">21217</a>)</li>
<li>Correctly aggregate narrowing information on parent expressions
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21206">21206</a>)</li>
<li>Fix regression related to generic callables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21208">21208</a>)</li>
<li>Fix regression by avoiding widening types in some contexts
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21242">21242</a>)</li>
<li>Fix slicing in non-strict optional mode (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21282">21282</a>)</li>
<li>mypyc: Fix match statement semantics for &quot;or&quot; pattern
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21156">21156</a>)</li>
<li>mypyc: Fix issue with module dunder attributes (Piotr Sawicki, PR <a
href="https://redirect.github.com/python/mypy/pull/21275">21275</a>)</li>
<li>Initial support for Python 3.15.0a8 (Marc Mueller, PR <a
href="https://redirect.github.com/python/mypy/pull/21255">21255</a>)</li>
</ul>
<h3>Acknowledgements</h3>
<p>Thanks to all mypy contributors who contributed to this release:</p>
<ul>
<li>A5rocks</li>
<li>Aaron Wieczorek</li>
<li>Adam Turner</li>
<li>Ali Hamdan</li>
<li>asce</li>
<li>BobTheBuidler</li>
<li>Brent Westbrook</li>
<li>Brian Schubert</li>
<li>bzoracler</li>
<li>Chris Burroughs</li>
<li>Christoph Tyralla</li>
<li>Colin Watson</li>
<li>Donghoon Nam</li>
<li>E. M. Bray</li>
<li>Emma Smith</li>
<li>Ethan Sarp</li>
<li>George Ogden</li>
<li>getzze</li>
<li>grayjk</li>
<li>Gregor Riepl</li>
<li>Ivan Levkivskyi</li>
<li>James Hilliard</li>
<li>James Le Cuirot</li>
<li>Jeremy Nimmer</li>
<li>Joren Hammudoglu</li>
<li>Kai (Kazuya Ito)</li>
<li>kaushal trivedi</li>
<li>Kevin Kannammalil</li>
<li>Lukas Geiger</li>
<li>Łukasz Langa</li>
<li>Marc Mueller</li>
<li>Michael R. Crusoe</li>
<li>michaelm-openai</li>
<li>Neil Schemenauer</li>
<li>Piotr Sawicki</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/python/mypy/commit/145a062651b5f9996b75ef32b7040bd2e885ed82"><code>145a062</code></a>
Bump version to 1.20.2</li>
<li><a
href="https://github.com/python/mypy/commit/81cd49215c288eacb987de066f02daff2553b7c7"><code>81cd492</code></a>
Fix slicing with nonstrict optional (<a
href="https://redirect.github.com/python/mypy/issues/21282">#21282</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/908d3441eecbaa2a6193165317177db834d7ca1a"><code>908d344</code></a>
[mypyc] Set dunder attrs when adding module to sys.modules (<a
href="https://redirect.github.com/python/mypy/issues/21275">#21275</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/ba28610fac9d2b33be210ca8dcfe4bc47b7af424"><code>ba28610</code></a>
Initial support for Python 3.15.0a8 (<a
href="https://redirect.github.com/python/mypy/issues/21255">#21255</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/7b0e09f48dbd3717ed008a273cd17e8e960c2037"><code>7b0e09f</code></a>
Fix match statement semantics for &quot;or&quot; pattern (<a
href="https://redirect.github.com/python/mypy/issues/21156">#21156</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/92b74f226de62f7505f5ef5cb158e8ec9c58b8b7"><code>92b74f2</code></a>
Avoid widening types in conditional_types (<a
href="https://redirect.github.com/python/mypy/issues/21242">#21242</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/0dcbfaa40b0e360a16baea9cf851955375d91b54"><code>0dcbfaa</code></a>
Fix is_overlapping_types for generic callables (<a
href="https://redirect.github.com/python/mypy/issues/21208">#21208</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/210f518dede35292033ef0d387847406a0ccef8f"><code>210f518</code></a>
Correctly aggregate narrowing information on parent expressions (<a
href="https://redirect.github.com/python/mypy/issues/21206">#21206</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/c34530e53a10e385d8b0f1af4baa88a596b5ceaa"><code>c34530e</code></a>
Only set journal mode in coordinator (<a
href="https://redirect.github.com/python/mypy/issues/21217">#21217</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/79a3ec6d01b56a27c00e9b3320c2b1d4d73a77f9"><code>79a3ec6</code></a>
Use WAL with SQLite cache, fix close (<a
href="https://redirect.github.com/python/mypy/issues/21154">#21154</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python/mypy/compare/v1.20.0...v1.20.2">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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:16:12 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8593efe0ad chore(deps): bump the minor-and-patch group in /libs/checkpoint-postgres with 2 updates (#7665)
Bumps the minor-and-patch group in /libs/checkpoint-postgres with 2
updates: [ruff](https://github.com/astral-sh/ruff) and
[mypy](https://github.com/python/mypy).

Updates `ruff` from 0.15.8 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>0.15.11</h2>
<p>Released on 2026-04-16.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>ruff</code>] Ignore <code>RUF029</code> when function is
decorated with <code>asynccontextmanager</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24642">#24642</a>)</li>
<li>[<code>airflow</code>] Implement
<code>airflow-xcom-pull-in-template-string</code> (<code>AIR201</code>)
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/23583">#23583</a>)</li>
<li>[<code>flake8-bandit</code>] Fix <code>S103</code> false positives
and negatives in mask analysis (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24424">#24424</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/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.8...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `mypy` from 1.20.0 to 1.20.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's
changelog</a>.</em></p>
<blockquote>
<h3>Mypy 1.20.2</h3>
<ul>
<li>Use WAL with SQLite cache and fix close (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21154">21154</a>)</li>
<li>Adjust SQLite journal mode (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21217">21217</a>)</li>
<li>Correctly aggregate narrowing information on parent expressions
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21206">21206</a>)</li>
<li>Fix regression related to generic callables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21208">21208</a>)</li>
<li>Fix regression by avoiding widening types in some contexts
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21242">21242</a>)</li>
<li>Fix slicing in non-strict optional mode (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21282">21282</a>)</li>
<li>mypyc: Fix match statement semantics for &quot;or&quot; pattern
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21156">21156</a>)</li>
<li>mypyc: Fix issue with module dunder attributes (Piotr Sawicki, PR <a
href="https://redirect.github.com/python/mypy/pull/21275">21275</a>)</li>
<li>Initial support for Python 3.15.0a8 (Marc Mueller, PR <a
href="https://redirect.github.com/python/mypy/pull/21255">21255</a>)</li>
</ul>
<h3>Acknowledgements</h3>
<p>Thanks to all mypy contributors who contributed to this release:</p>
<ul>
<li>A5rocks</li>
<li>Aaron Wieczorek</li>
<li>Adam Turner</li>
<li>Ali Hamdan</li>
<li>asce</li>
<li>BobTheBuidler</li>
<li>Brent Westbrook</li>
<li>Brian Schubert</li>
<li>bzoracler</li>
<li>Chris Burroughs</li>
<li>Christoph Tyralla</li>
<li>Colin Watson</li>
<li>Donghoon Nam</li>
<li>E. M. Bray</li>
<li>Emma Smith</li>
<li>Ethan Sarp</li>
<li>George Ogden</li>
<li>getzze</li>
<li>grayjk</li>
<li>Gregor Riepl</li>
<li>Ivan Levkivskyi</li>
<li>James Hilliard</li>
<li>James Le Cuirot</li>
<li>Jeremy Nimmer</li>
<li>Joren Hammudoglu</li>
<li>Kai (Kazuya Ito)</li>
<li>kaushal trivedi</li>
<li>Kevin Kannammalil</li>
<li>Lukas Geiger</li>
<li>Łukasz Langa</li>
<li>Marc Mueller</li>
<li>Michael R. Crusoe</li>
<li>michaelm-openai</li>
<li>Neil Schemenauer</li>
<li>Piotr Sawicki</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/python/mypy/commit/145a062651b5f9996b75ef32b7040bd2e885ed82"><code>145a062</code></a>
Bump version to 1.20.2</li>
<li><a
href="https://github.com/python/mypy/commit/81cd49215c288eacb987de066f02daff2553b7c7"><code>81cd492</code></a>
Fix slicing with nonstrict optional (<a
href="https://redirect.github.com/python/mypy/issues/21282">#21282</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/908d3441eecbaa2a6193165317177db834d7ca1a"><code>908d344</code></a>
[mypyc] Set dunder attrs when adding module to sys.modules (<a
href="https://redirect.github.com/python/mypy/issues/21275">#21275</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/ba28610fac9d2b33be210ca8dcfe4bc47b7af424"><code>ba28610</code></a>
Initial support for Python 3.15.0a8 (<a
href="https://redirect.github.com/python/mypy/issues/21255">#21255</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/7b0e09f48dbd3717ed008a273cd17e8e960c2037"><code>7b0e09f</code></a>
Fix match statement semantics for &quot;or&quot; pattern (<a
href="https://redirect.github.com/python/mypy/issues/21156">#21156</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/92b74f226de62f7505f5ef5cb158e8ec9c58b8b7"><code>92b74f2</code></a>
Avoid widening types in conditional_types (<a
href="https://redirect.github.com/python/mypy/issues/21242">#21242</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/0dcbfaa40b0e360a16baea9cf851955375d91b54"><code>0dcbfaa</code></a>
Fix is_overlapping_types for generic callables (<a
href="https://redirect.github.com/python/mypy/issues/21208">#21208</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/210f518dede35292033ef0d387847406a0ccef8f"><code>210f518</code></a>
Correctly aggregate narrowing information on parent expressions (<a
href="https://redirect.github.com/python/mypy/issues/21206">#21206</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/c34530e53a10e385d8b0f1af4baa88a596b5ceaa"><code>c34530e</code></a>
Only set journal mode in coordinator (<a
href="https://redirect.github.com/python/mypy/issues/21217">#21217</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/79a3ec6d01b56a27c00e9b3320c2b1d4d73a77f9"><code>79a3ec6</code></a>
Use WAL with SQLite cache, fix close (<a
href="https://redirect.github.com/python/mypy/issues/21154">#21154</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python/mypy/compare/v1.20.0...v1.20.2">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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:15:49 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
402d767229 chore(deps): bump actions/upload-pages-artifact from 4 to 5 (#7664)
Bumps
[actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact)
from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/upload-pages-artifact/releases">actions/upload-pages-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h1>Changelog</h1>
<ul>
<li>Update upload-artifact action to version 7 <a
href="https://github.com/Tom-van-Woudenberg"><code>@​Tom-van-Woudenberg</code></a>
(<a
href="https://redirect.github.com/actions/upload-pages-artifact/issues/139">#139</a>)</li>
<li>feat: add <code>include-hidden-files</code> input <a
href="https://github.com/jonchurch"><code>@​jonchurch</code></a> (<a
href="https://redirect.github.com/actions/upload-pages-artifact/issues/137">#137</a>)</li>
</ul>
<p>See details of <a
href="https://github.com/actions/upload-pages-artifact/compare/v4.0.0...v4.0.1">all
code changes</a> since previous release.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/upload-pages-artifact/commit/fc324d3547104276b827a68afc52ff2a11cc49c9"><code>fc324d3</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-pages-artifact/issues/139">#139</a>
from Tom-van-Woudenberg/patch-1</li>
<li><a
href="https://github.com/actions/upload-pages-artifact/commit/fe9d4b7d84090e1d8d9c53a0236f810d4e00d2c3"><code>fe9d4b7</code></a>
Merge branch 'main' into patch-1</li>
<li><a
href="https://github.com/actions/upload-pages-artifact/commit/0ca16172ca884f0a37117fed41734f29784cc980"><code>0ca1617</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-pages-artifact/issues/137">#137</a>
from jonchurch/include-hidden-files</li>
<li><a
href="https://github.com/actions/upload-pages-artifact/commit/57f0e8492b437b7818227931fef2faa1a379839b"><code>57f0e84</code></a>
Update action.yml</li>
<li><a
href="https://github.com/actions/upload-pages-artifact/commit/4a90348b2933470dc78cec55534259872a6d3c0d"><code>4a90348</code></a>
v7 --&gt; hash</li>
<li><a
href="https://github.com/actions/upload-pages-artifact/commit/56f665a6f297fa95f8d735b314187fb2d7764569"><code>56f665a</code></a>
Update upload-artifact action to version 7</li>
<li><a
href="https://github.com/actions/upload-pages-artifact/commit/f7615f5917213b24245d49ba96693d0f5375a414"><code>f7615f5</code></a>
Add <code>include-hidden-files</code> input</li>
<li>See full diff in <a
href="https://github.com/actions/upload-pages-artifact/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/upload-pages-artifact&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:14:40 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
95541cbf08 chore(deps): bump actions/github-script from 8 to 9 (#7663)
Bumps [actions/github-script](https://github.com/actions/github-script)
from 8 to 9.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/github-script/releases">actions/github-script's
releases</a>.</em></p>
<blockquote>
<h2>v9.0.0</h2>
<p><strong>New features:</strong></p>
<ul>
<li><strong><code>getOctokit</code> factory function</strong> —
Available directly in the script context. Create additional
authenticated Octokit clients with different tokens for multi-token
workflows, GitHub App tokens, and cross-org access. See <a
href="https://github.com/actions/github-script#creating-additional-clients-with-getoctokit">Creating
additional clients with <code>getOctokit</code></a> for details and
examples.</li>
<li><strong>Orchestration ID in user-agent</strong> — The
<code>ACTIONS_ORCHESTRATION_ID</code> environment variable is
automatically appended to the user-agent string for request
tracing.</li>
</ul>
<p><strong>Breaking changes:</strong></p>
<ul>
<li><strong><code>require('@actions/github')</code> no longer works in
scripts.</strong> The upgrade to <code>@actions/github</code> v9
(ESM-only) means <code>require('@actions/github')</code> will fail at
runtime. If you previously used patterns like <code>const { getOctokit }
= require('@actions/github')</code> to create secondary clients, use the
new injected <code>getOctokit</code> function instead — it's available
directly in the script context with no imports needed.</li>
<li><code>getOctokit</code> is now an injected function parameter.
Scripts that declare <code>const getOctokit = ...</code> or <code>let
getOctokit = ...</code> will get a <code>SyntaxError</code> because
JavaScript does not allow <code>const</code>/<code>let</code>
redeclaration of function parameters. Use the injected
<code>getOctokit</code> directly, or use <code>var getOctokit =
...</code> if you need to redeclare it.</li>
<li>If your script accesses other <code>@actions/github</code> internals
beyond the standard <code>github</code>/<code>octokit</code> client, you
may need to update those references for v9 compatibility.</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Add ACTIONS_ORCHESTRATION_ID to user-agent string by <a
href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/695">actions/github-script#695</a></li>
<li>ci: use deployment: false for integration test environments by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/712">actions/github-script#712</a></li>
<li>feat!: add getOctokit to script context, upgrade
<code>@​actions/github</code> v9, <code>@​octokit/core</code> v7, and
related packages by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/700">actions/github-script#700</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Copilot"><code>@​Copilot</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/695">actions/github-script#695</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/github-script/compare/v8.0.0...v9.0.0">https://github.com/actions/github-script/compare/v8.0.0...v9.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/github-script/commit/3a2844b7e9c422d3c10d287c895573f7108da1b3"><code>3a2844b</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/github-script/issues/700">#700</a>
from actions/salmanmkc/expose-getoctokit + prepare re...</li>
<li><a
href="https://github.com/actions/github-script/commit/ca10bbdd1a7739de09e99a200c7a59f5d73a4079"><code>ca10bbd</code></a>
fix: use <code>@​octokit/core/</code>types import for v7
compatibility</li>
<li><a
href="https://github.com/actions/github-script/commit/86e48e20ac85c970ed1f96e718fd068173948b7b"><code>86e48e2</code></a>
merge: incorporate main branch changes</li>
<li><a
href="https://github.com/actions/github-script/commit/c1084728b5b935ec4ddc1e4cee877b01797b3ff9"><code>c108472</code></a>
chore: rebuild dist for v9 upgrade and getOctokit factory</li>
<li><a
href="https://github.com/actions/github-script/commit/afff112e4f8b57c718168af75b89ce00bc8d091d"><code>afff112</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/github-script/issues/712">#712</a>
from actions/salmanmkc/deployment-false + fix user-ag...</li>
<li><a
href="https://github.com/actions/github-script/commit/ff8117e5b78c415f814f39ad6998f424fee7b817"><code>ff8117e</code></a>
ci: fix user-agent test to handle orchestration ID</li>
<li><a
href="https://github.com/actions/github-script/commit/81c6b7876079abe10ff715951c9fc7b3e1ab389d"><code>81c6b78</code></a>
ci: use deployment: false to suppress deployment noise from integration
tests</li>
<li><a
href="https://github.com/actions/github-script/commit/3953caf8858d318f37b6cc53a9f5708859b5a7b7"><code>3953caf</code></a>
docs: update README examples from <a
href="https://github.com/v8"><code>@​v8</code></a> to <a
href="https://github.com/v9"><code>@​v9</code></a>, add getOctokit docs
and v9 brea...</li>
<li><a
href="https://github.com/actions/github-script/commit/c17d55b90dcdb3d554d0027a6c180a7adc2daf78"><code>c17d55b</code></a>
ci: add getOctokit integration test job</li>
<li><a
href="https://github.com/actions/github-script/commit/a047196d9a02fe92098771cafbb98c2f1814e408"><code>a047196</code></a>
test: add getOctokit integration tests via callAsyncFunction</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/github-script/compare/v8...v9">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/github-script&package-manager=github_actions&previous-version=8&new-version=9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:14:30 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
30a1251dcc chore(deps): bump the minor-and-patch group with 3 updates (#7662)
Bumps the minor-and-patch group with 3 updates:
[docker/login-action](https://github.com/docker/login-action),
[pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish)
and
[peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request).

Updates `docker/login-action` from 4.0.0 to 4.1.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/docker/login-action/releases">docker/login-action's
releases</a>.</em></p>
<blockquote>
<h2>v4.1.0</h2>
<ul>
<li>Fix scoped Docker Hub cleanup path when registry is omitted by <a
href="https://github.com/crazy-max"><code>@​crazy-max</code></a> in <a
href="https://redirect.github.com/docker/login-action/pull/945">docker/login-action#945</a></li>
<li>Bump <code>@​aws-sdk/client-ecr</code> and
<code>@​aws-sdk/client-ecr-public</code> to 3.1020.0 in <a
href="https://redirect.github.com/docker/login-action/pull/930">docker/login-action#930</a></li>
<li>Bump <code>@​docker/actions-toolkit</code> from 0.77.0 to 0.86.0 in
<a
href="https://redirect.github.com/docker/login-action/pull/932">docker/login-action#932</a>
<a
href="https://redirect.github.com/docker/login-action/pull/936">docker/login-action#936</a></li>
<li>Bump brace-expansion from 1.1.12 to 1.1.13 in <a
href="https://redirect.github.com/docker/login-action/pull/952">docker/login-action#952</a></li>
<li>Bump fast-xml-parser from 5.3.4 to 5.3.6 in <a
href="https://redirect.github.com/docker/login-action/pull/942">docker/login-action#942</a></li>
<li>Bump flatted from 3.3.3 to 3.4.2 in <a
href="https://redirect.github.com/docker/login-action/pull/944">docker/login-action#944</a></li>
<li>Bump glob from 10.3.12 to 10.5.0 in <a
href="https://redirect.github.com/docker/login-action/pull/940">docker/login-action#940</a></li>
<li>Bump handlebars from 4.7.8 to 4.7.9 in <a
href="https://redirect.github.com/docker/login-action/pull/949">docker/login-action#949</a></li>
<li>Bump http-proxy-agent and https-proxy-agent to 8.0.0 in <a
href="https://redirect.github.com/docker/login-action/pull/937">docker/login-action#937</a></li>
<li>Bump lodash from 4.17.23 to 4.18.1 in <a
href="https://redirect.github.com/docker/login-action/pull/958">docker/login-action#958</a></li>
<li>Bump minimatch from 3.1.2 to 3.1.5 in <a
href="https://redirect.github.com/docker/login-action/pull/941">docker/login-action#941</a></li>
<li>Bump picomatch from 4.0.3 to 4.0.4 in <a
href="https://redirect.github.com/docker/login-action/pull/948">docker/login-action#948</a></li>
<li>Bump undici from 6.23.0 to 6.24.1 in <a
href="https://redirect.github.com/docker/login-action/pull/938">docker/login-action#938</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/docker/login-action/compare/v4.0.0...v4.1.0">https://github.com/docker/login-action/compare/v4.0.0...v4.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/docker/login-action/commit/4907a6ddec9925e35a0a9e82d7399ccc52663121"><code>4907a6d</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/930">#930</a>
from docker/dependabot/npm_and_yarn/aws-sdk-dependenc...</li>
<li><a
href="https://github.com/docker/login-action/commit/1e233e691a8881d7f35ca7c2d5dfaaed80b39636"><code>1e233e6</code></a>
chore: update generated content</li>
<li><a
href="https://github.com/docker/login-action/commit/6c24ead68057f18c30c808a431f0b85dc25663cb"><code>6c24ead</code></a>
build(deps): bump the aws-sdk-dependencies group with 2 updates</li>
<li><a
href="https://github.com/docker/login-action/commit/ee034d70944e3546349cd24295914f139342f1e6"><code>ee034d7</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/958">#958</a>
from docker/dependabot/npm_and_yarn/lodash-4.18.1</li>
<li><a
href="https://github.com/docker/login-action/commit/1527209db9734bd2352a2dc1a63d79c9aa5358bb"><code>1527209</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/937">#937</a>
from docker/dependabot/npm_and_yarn/proxy-agent-depen...</li>
<li><a
href="https://github.com/docker/login-action/commit/d39362aba4d72f8d9d93e0962119840690133e1b"><code>d39362a</code></a>
build(deps): bump lodash from 4.17.23 to 4.18.1</li>
<li><a
href="https://github.com/docker/login-action/commit/a6f092b568105cbb6d9deb7e55e0a4c5c1025fce"><code>a6f092b</code></a>
chore: update generated content</li>
<li><a
href="https://github.com/docker/login-action/commit/60953f0bed2120ec69659d271fe18d34bc069779"><code>60953f0</code></a>
build(deps): bump the proxy-agent-dependencies group with 2 updates</li>
<li><a
href="https://github.com/docker/login-action/commit/62c688590fb4ab6c6e89a217ced0a7b2ddcf1340"><code>62c6885</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/936">#936</a>
from docker/dependabot/npm_and_yarn/docker/actions-to...</li>
<li><a
href="https://github.com/docker/login-action/commit/102c0e672992d2e992c89b6f4808d65a353b5a1a"><code>102c0e6</code></a>
chore: update generated content</li>
<li>Additional commits viewable in <a
href="https://github.com/docker/login-action/compare/b45d80f862d83dbcd57f89517bcf500b2ab88fb2...4907a6ddec9925e35a0a9e82d7399ccc52663121">compare
view</a></li>
</ul>
</details>
<br />

Updates `pypa/gh-action-pypi-publish` from 1.13.0 to 1.14.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pypa/gh-action-pypi-publish/releases">pypa/gh-action-pypi-publish's
releases</a>.</em></p>
<blockquote>
<h2>v1.14.0</h2>
<!-- raw HTML omitted -->
<h2> What's Changed</h2>
<p>The main change in this release is that <code>verbose</code> and
<code>print-hash</code> inputs are now on by default. This was
contributed by <a
href="https://github.com/whitequark"><code>@​whitequark</code></a><a
href="https://github.com/sponsors/whitequark">💰</a> in <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/397">#397</a>.</p>
<h2>📝 Docs</h2>
<p><a href="https://github.com/woodruffw"><code>@​woodruffw</code></a><a
href="https://github.com/sponsors/woodruffw">💰</a> updated the mentions
of PEP 740 to stop implying that it might be experimental (it hasn't
been for quite a while!) in <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/388">#388</a>
and <a href="https://github.com/him2him2"><code>@​him2him2</code></a><a
href="https://github.com/sponsors/him2him2">💰</a> brushed up some
grammar in the README and SECURITY docs via <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/395">#395</a>.</p>
<h2>🛠️ Internal Updates</h2>
<p><a href="https://github.com/woodruffw"><code>@​woodruffw</code></a><a
href="https://github.com/sponsors/woodruffw">💰</a> bumped
<code>sigstore</code> and <code>pypi-attestations</code> in the lock
file (<a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/391">#391</a>)
and <a href="https://github.com/webknjaz"><code>@​webknjaz</code></a><a
href="https://github.com/sponsors/webknjaz">💰</a> added infra for using
type annotations in the project (<a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/381">#381</a>).</p>
<h2>💪 New Contributors</h2>
<ul>
<li><a href="https://github.com/him2him2"><code>@​him2him2</code></a>
made their first contribution in <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/395">#395</a></li>
<li><a
href="https://github.com/whitequark"><code>@​whitequark</code></a> made
their first contribution in <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/397">#397</a></li>
</ul>
<p><strong>🪞 Full Diff</strong>: <a
href="https://github.com/pypa/gh-action-pypi-publish/compare/v1.13.0...v1.14.0">https://github.com/pypa/gh-action-pypi-publish/compare/v1.13.0...v1.14.0</a></p>
<p><strong>🧔‍♂️ Release Manager:</strong> <a
href="https://github.com/sponsors/webknjaz"><code>@​webknjaz</code></a>
<a href="https://stand-with-ukraine.pp.ua">🇺🇦</a></p>
<p><strong>🙏 Special Thanks</strong> to <a
href="https://github.com/facutuesca"><code>@​facutuesca</code></a><a
href="https://github.com/sponsors/facutuesca">💰</a> and <a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a><a
href="https://github.com/sponsors/woodruffw">💰</a> for helping maintain
this project when <a href="https://github.com/sponsors/webknjaz">I</a>
can't!</p>
<p><strong>💬 Discuss</strong> <a
href="https://bsky.app/profile/webknjaz.me/post/3mivwsz3qzk2e">on
Bluesky 🦋</a>, <a
href="https://mastodon.social/@webknjaz/116363779997051422">on Mastodon
🐘</a> and <a
href="https://github.com/pypa/gh-action-pypi-publish/discussions/404">on
GitHub</a>.</p>
<p><a href="https://github.com/sponsors/webknjaz"><img
src="https://img.shields.io/badge/%40webknjaz-transparent?logo=githubsponsors&amp;logoColor=%23EA4AAA&amp;label=Sponsor&amp;color=2a313c"
alt="GH Sponsors badge" /></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/cef221092ed1bacb1cc03d23a2d87d1d172e277b"><code>cef2210</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/397">#397</a>
from whitequark/patch-1</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/b4595e2555a031e2fd6f0bbded4e7918eaa2724e"><code>b4595e2</code></a>
Enable <code>verbose</code> and <code>print-hash</code> by default.</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/e2bab26859796ee5c3bf97b8f394ce1e6570e906"><code>e2bab26</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/395">#395</a>
from him2him2/docs/fix-typos-and-grammar</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/7495c384ec7a0240a28e568e7ffc60af1629585d"><code>7495c38</code></a>
docs: fix typos and grammar in README and SECURITY</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/03f86fee9ac21f854951f5c6e2a02c2a1324aec7"><code>03f86fe</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/388">#388</a>
from woodruffw-forks/ww/rm-experimental</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/4c78f1c53c55c528d8abd83df933ae92bd4c1d8c"><code>4c78f1c</code></a>
Merge branch 'unstable/v1' into ww/rm-experimental</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/b5a6e8ba2611ad0c810f383eed9e6629eb0b3b2f"><code>b5a6e8b</code></a>
deps: bump sigstore and pypi-attestations</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/a48a03e758da35722b0d159dae23e0440d0fcce2"><code>a48a03e</code></a>
remove another experimental mention</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/8087a88a46924f78608905d7841a170e749524ce"><code>8087a88</code></a>
action: remove a lingering mention of PEP 740 being experimental</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/3317ede93a4981d0fc490510c6fcf8bf0e92ed05"><code>3317ede</code></a>
🧪 Integrate actionlint via pre-commit framework</li>
<li>Additional commits viewable in <a
href="https://github.com/pypa/gh-action-pypi-publish/compare/ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e...cef221092ed1bacb1cc03d23a2d87d1d172e277b">compare
view</a></li>
</ul>
</details>
<br />

Updates `peter-evans/create-pull-request` from 8.1.0 to 8.1.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/peter-evans/create-pull-request/releases">peter-evans/create-pull-request's
releases</a>.</em></p>
<blockquote>
<h2>Create Pull Request v8.1.1</h2>
<h2>What's Changed</h2>
<ul>
<li>build(deps-dev): bump the npm group with 2 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4305">peter-evans/create-pull-request#4305</a></li>
<li>build(deps): bump minimatch by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4311">peter-evans/create-pull-request#4311</a></li>
<li>build(deps): bump the github-actions group with 2 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4316">peter-evans/create-pull-request#4316</a></li>
<li>build(deps): bump <code>@​tootallnate/once</code> and
jest-environment-jsdom by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4323">peter-evans/create-pull-request#4323</a></li>
<li>build(deps-dev): bump undici from 6.23.0 to 6.24.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4328">peter-evans/create-pull-request#4328</a></li>
<li>build(deps-dev): bump flatted from 3.3.1 to 3.4.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4334">peter-evans/create-pull-request#4334</a></li>
<li>build(deps): bump picomatch by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4339">peter-evans/create-pull-request#4339</a></li>
<li>build(deps-dev): bump handlebars from 4.7.8 to 4.7.9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4344">peter-evans/create-pull-request#4344</a></li>
<li>build(deps-dev): bump the npm group with 3 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4349">peter-evans/create-pull-request#4349</a></li>
<li>fix: retry post-creation API calls on 422 eventual consistency
errors by <a
href="https://github.com/peter-evans"><code>@​peter-evans</code></a> in
<a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4356">peter-evans/create-pull-request#4356</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/peter-evans/create-pull-request/compare/v8.1.0...v8.1.1">https://github.com/peter-evans/create-pull-request/compare/v8.1.0...v8.1.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/5f6978faf089d4d20b00c7766989d076bb2fc7f1"><code>5f6978f</code></a>
fix: retry post-creation API calls on 422 eventual consistency errors
(<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4356">#4356</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/d32e88dac789dcc7906e7d26f69f24116fa9c97d"><code>d32e88d</code></a>
build(deps-dev): bump the npm group with 3 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4349">#4349</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/8170bccad11c0df62542c04dcaefe36d342dfd39"><code>8170bcc</code></a>
build(deps-dev): bump handlebars from 4.7.8 to 4.7.9 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4344">#4344</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/00418193b417f888dbf1d993c5c0d31d27fdc7de"><code>0041819</code></a>
build(deps): bump picomatch (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4339">#4339</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/b993918c8536b6d44706130734d5456879762b27"><code>b993918</code></a>
build(deps-dev): bump flatted from 3.3.1 to 3.4.2 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4334">#4334</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/36d7c8468b48f9c2f8f29e260e82f10d4b90d2bd"><code>36d7c84</code></a>
build(deps-dev): bump undici from 6.23.0 to 6.24.0 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4328">#4328</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/a45d1fb447fcaf601166e405fd4f335cde1a8aa8"><code>a45d1fb</code></a>
build(deps): bump <code>@​tootallnate/once</code> and
jest-environment-jsdom (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4323">#4323</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/3499eb61835cc0015c0b786e203d74b1e8f55e43"><code>3499eb6</code></a>
build(deps): bump the github-actions group with 2 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4316">#4316</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/3f3b473b8c148f5a7520efb4d1f9a70eea3d9d1f"><code>3f3b473</code></a>
build(deps): bump minimatch (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4311">#4311</a>)</li>
<li><a
href="https://github.com/peter-evans/create-pull-request/commit/6699836a213cf8b28c4f0408a404a6ac79d4458a"><code>6699836</code></a>
build(deps-dev): bump the npm group with 2 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4305">#4305</a>)</li>
<li>See full diff in <a
href="https://github.com/peter-evans/create-pull-request/compare/c0f553fe549906ede9cf27b5156039d195d2ece0...5f6978faf089d4d20b00c7766989d076bb2fc7f1">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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:14:22 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1a80cb3eab chore(deps): bump the minor-and-patch group in /libs/cli with 4 updates (#7674)
Bumps the minor-and-patch group in /libs/cli with 4 updates:
[click](https://github.com/pallets/click),
[pathspec](https://github.com/cpburnz/python-pathspec),
[ruff](https://github.com/astral-sh/ruff) and
[mypy](https://github.com/python/mypy).

Updates `click` from 8.3.2 to 8.3.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pallets/click/releases">click's
releases</a>.</em></p>
<blockquote>
<h2>8.3.3</h2>
<p>This is the Click 8.3.3 fix release, which fixes bugs but does not
otherwise change behavior and should not result in breaking changes
compared to the latest feature release.</p>
<p>PyPI: <a
href="https://pypi.org/project/click/8.3.3/">https://pypi.org/project/click/8.3.3/</a>
Changes: <a
href="https://click.palletsprojects.com/page/changes/#version-8-3-3">https://click.palletsprojects.com/page/changes/#version-8-3-3</a>
Milestone: <a
href="https://github.com/pallets/click/milestone/30">https://github.com/pallets/click/milestone/30</a></p>
<ul>
<li>Use :func:<code>shlex.split</code> to split pager and editor
commands into <code>argv</code>
lists for :class:<code>subprocess.Popen</code>, removing
<code>shell=True</code>.
<a
href="https://redirect.github.com/pallets/click/issues/1026">#1026</a>
<a
href="https://redirect.github.com/pallets/click/issues/1477">#1477</a>
<a
href="https://redirect.github.com/pallets/click/issues/2775">#2775</a></li>
<li>Fix <code>TypeError</code> when rendering help for an option whose
default value is
an object that doesn't support equality comparison with strings, such as
<code>semver.Version</code>. <a
href="https://redirect.github.com/pallets/click/issues/3298">#3298</a>
<a
href="https://redirect.github.com/pallets/click/issues/3299">#3299</a></li>
<li>Fix pager test pollution under parallel execution by using pytest's
<code>tmp_path</code> fixture instead of a shared temporary file path.
<a
href="https://redirect.github.com/pallets/click/issues/3238">#3238</a></li>
<li>Treat <code>Sentinel.UNSET</code> values in a
<code>default_map</code> as absent, so they fall
through to the next default source instead of being used as the value.
<a
href="https://redirect.github.com/pallets/click/issues/3224">#3224</a>
<a
href="https://redirect.github.com/pallets/click/issues/3240">#3240</a></li>
<li>Patch <code>pdb.Pdb</code> in <code>CliRunner</code> isolation so
<code>pdb.set_trace()</code>,
<code>breakpoint()</code>, and debuggers subclassing
<code>pdb.Pdb</code> (ipdb, pdbpp) can
interact with the real terminal instead of the captured I/O streams.
<a href="https://redirect.github.com/pallets/click/issues/654">#654</a>
<a href="https://redirect.github.com/pallets/click/issues/824">#824</a>
<a href="https://redirect.github.com/pallets/click/issues/843">#843</a>
<a href="https://redirect.github.com/pallets/click/issues/951">#951</a>
<a
href="https://redirect.github.com/pallets/click/issues/3235">#3235</a></li>
<li>Add optional randomized parallel test execution using
<code>pytest-randomly</code> and
<code>pytest-xdist</code> to detect test pollution and race conditions.
<a
href="https://redirect.github.com/pallets/click/issues/3151">#3151</a></li>
<li>Add contributor documentation for running stress tests, randomized
parallel tests, and Flask smoke tests. <a
href="https://redirect.github.com/pallets/click/issues/3151">#3151</a>
<a
href="https://redirect.github.com/pallets/click/issues/3177">#3177</a></li>
<li>Show custom <code>show_default</code> string in prompts, matching
the existing
help text behavior. <a
href="https://redirect.github.com/pallets/click/issues/2836">#2836</a>
<a
href="https://redirect.github.com/pallets/click/issues/2837">#2837</a>
<a
href="https://redirect.github.com/pallets/click/issues/3165">#3165</a>
<a
href="https://redirect.github.com/pallets/click/issues/3262">#3262</a>
<a
href="https://redirect.github.com/pallets/click/issues/3280">#3280</a>
<a
href="https://redirect.github.com/pallets/click/issues/3328">#3328</a></li>
<li>Fix <code>default=True</code> with boolean <code>flag_value</code>
always returning the
<code>flag_value</code> instead of <code>True</code>. The
<code>default=True</code> to <code>flag_value</code>
substitution now only applies to non-boolean flags, where
<code>True</code> acts as a
sentinel meaning &quot;activate this flag by default&quot;. For boolean
flags,
<code>default=True</code> is returned as a literal value. <a
href="https://redirect.github.com/pallets/click/issues/3111">#3111</a>
<a
href="https://redirect.github.com/pallets/click/issues/3239">#3239</a></li>
<li>Mark <code>make_default_short_help</code> as private API. <a
href="https://redirect.github.com/pallets/click/issues/3189">#3189</a>
<a
href="https://redirect.github.com/pallets/click/issues/3250">#3250</a></li>
<li><code>CliRunner</code>'s redirected streams now expose the original
file descriptor
via <code>fileno()</code>, so that <code>faulthandler</code>,
<code>subprocess</code>, and other
C-level consumers no longer crash with
<code>io.UnsupportedOperation</code>.
<a
href="https://redirect.github.com/pallets/click/issues/2865">#2865</a></li>
<li>Change :class:<code>ParameterSource</code> to an
:class:<code>~enum.IntEnum</code> and reorder
its members from most to least explicit, so values can be compared to
check whether a parameter was explicitly provided. <a
href="https://redirect.github.com/pallets/click/issues/2879">#2879</a>
<a
href="https://redirect.github.com/pallets/click/issues/3248">#3248</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pallets/click/blob/main/CHANGES.rst">click's
changelog</a>.</em></p>
<blockquote>
<h2>Version 8.3.3</h2>
<p>Released 2026-04-20</p>
<ul>
<li>Use :func:<code>shlex.split</code> to split pager and editor
commands into <code>argv</code>
lists for :class:<code>subprocess.Popen</code>, removing
<code>shell=True</code>.
:issue:<code>1026</code> :pr:<code>1477</code>
:pr:<code>2775</code></li>
<li>Fix <code>TypeError</code> when rendering help for an option whose
default value is
an object that doesn't support equality comparison with strings, such as
<code>semver.Version</code>. :issue:<code>3298</code>
:pr:<code>3299</code></li>
<li>Fix pager test pollution under parallel execution by using pytest's
<code>tmp_path</code> fixture instead of a shared temporary file path.
:pr:<code>3238</code></li>
<li>Treat <code>Sentinel.UNSET</code> values in a
<code>default_map</code> as absent, so they fall
through to the next default source instead of being used as the value.
:issue:<code>3224</code> :pr:<code>3240</code></li>
<li>Patch <code>pdb.Pdb</code> in <code>CliRunner</code> isolation so
<code>pdb.set_trace()</code>,
<code>breakpoint()</code>, and debuggers subclassing
<code>pdb.Pdb</code> (ipdb, pdbpp) can
interact with the real terminal instead of the captured I/O streams.
:issue:<code>654</code> :issue:<code>824</code> :issue:<code>843</code>
:pr:<code>951</code> :pr:<code>3235</code></li>
<li>Add optional randomized parallel test execution using
<code>pytest-randomly</code> and
<code>pytest-xdist</code> to detect test pollution and race conditions.
:pr:<code>3151</code></li>
<li>Add contributor documentation for running stress tests, randomized
parallel tests, and Flask smoke tests. :pr:<code>3151</code>
:pr:<code>3177</code></li>
<li>Show custom <code>show_default</code> string in prompts, matching
the existing
help text behavior. :issue:<code>2836</code> :pr:<code>2837</code>
:pr:<code>3165</code> :pr:<code>3262</code> :pr:<code>3280</code>
:pr:<code>3328</code></li>
<li>Fix <code>default=True</code> with boolean <code>flag_value</code>
always returning the
<code>flag_value</code> instead of <code>True</code>. The
<code>default=True</code> to <code>flag_value</code>
substitution now only applies to non-boolean flags, where
<code>True</code> acts as a
sentinel meaning &quot;activate this flag by default&quot;. For boolean
flags,
<code>default=True</code> is returned as a literal value.
:issue:<code>3111</code> :pr:<code>3239</code></li>
<li>Mark <code>make_default_short_help</code> as private API.
:issue:<code>3189</code> :pr:<code>3250</code></li>
<li><code>CliRunner</code>'s redirected streams now expose the original
file descriptor
via <code>fileno()</code>, so that <code>faulthandler</code>,
<code>subprocess</code>, and other
C-level consumers no longer crash with
<code>io.UnsupportedOperation</code>.
:issue:<code>2865</code></li>
<li>Change :class:<code>ParameterSource</code> to an
:class:<code>~enum.IntEnum</code> and reorder
its members from most to least explicit, so values can be compared to
check whether a parameter was explicitly provided.
:issue:<code>2879</code> :pr:<code>3248</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pallets/click/commit/c06d2d0a6aee6bcc50bd8257be2a4a592f4e75d0"><code>c06d2d0</code></a>
Release 8.3.3</li>
<li><a
href="https://github.com/pallets/click/commit/f1f191ecd2c790b161187c78e7c88440e9524e5c"><code>f1f191e</code></a>
Apply format guidelines to commits since latest 8.3.2 release (<a
href="https://redirect.github.com/pallets/click/issues/3343">#3343</a>)</li>
<li><a
href="https://github.com/pallets/click/commit/bb59ba0fd279ca085d1113f0499b6a602ca31081"><code>bb59ba0</code></a>
Apply format guidelines to commits since latest 8.3.2 release</li>
<li><a
href="https://github.com/pallets/click/commit/4a352253c9ff013e36d11e4a6820d36d00ff2cd4"><code>4a35225</code></a>
Reduce blast-radius of <code>UNSET</code> in <code>default_map</code>
(<a
href="https://redirect.github.com/pallets/click/issues/3240">#3240</a>)</li>
<li><a
href="https://github.com/pallets/click/commit/c07bb936de43fd303f9cfbefe248ab23fd2199c8"><code>c07bb93</code></a>
Merge branch 'stable' into unset-in-default-map</li>
<li><a
href="https://github.com/pallets/click/commit/c7e1ba8448cbcb2cdd9c1c7f4a592e863dcc3995"><code>c7e1ba8</code></a>
Reorder <code>ParameterSource</code> (<a
href="https://redirect.github.com/pallets/click/issues/3248">#3248</a>)</li>
<li><a
href="https://github.com/pallets/click/commit/76552ff1e8c85837f911fc34037e702ae4327eda"><code>76552ff</code></a>
Show default string in prompt (<a
href="https://redirect.github.com/pallets/click/issues/3328">#3328</a>)</li>
<li><a
href="https://github.com/pallets/click/commit/ac5cec5fe54e5a691e7bac17f441ce9498e0744c"><code>ac5cec5</code></a>
Reorder ParameterSource from most to least explicit</li>
<li><a
href="https://github.com/pallets/click/commit/8c452e00e6772931b7071d9316b82b77e5b8f280"><code>8c452e0</code></a>
Merge branch 'stable' into show-default-string-in-prompt</li>
<li><a
href="https://github.com/pallets/click/commit/8c95c73bd5ef89eac638f85f1904a104ba4b1a32"><code>8c95c73</code></a>
Reconcile default value passing and default activation (<a
href="https://redirect.github.com/pallets/click/issues/3239">#3239</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pallets/click/compare/8.3.2...8.3.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `pathspec` from 1.0.4 to 1.1.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/cpburnz/python-pathspec/releases">pathspec's
releases</a>.</em></p>
<blockquote>
<h2>v1.1.1</h2>
<p>Release v1.1.1. See <a
href="https://github.com/cpburnz/python-pathspec/blob/v1.1.1/CHANGES.rst">CHANGES.rst</a>.</p>
<h2>v1.1.0</h2>
<p>Release v1.1.0. See <a
href="https://github.com/cpburnz/python-pathspec/blob/v1.1.0/CHANGES.rst">CHANGES.rst</a>.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/cpburnz/python-pathspec/blob/master/CHANGES.rst">pathspec's
changelog</a>.</em></p>
<blockquote>
<h2>1.1.1 (2026-04-26)</h2>
<p>Improvements:</p>
<ul>
<li>Improved type checking with mypy and pyright.</li>
</ul>
<p>Bug fixes:</p>
<ul>
<li>Fixed typing on <code>PathSpec[TPattern]</code> to
<code>PathSpec[TPattern_co]</code>.</li>
<li>Added missing variant type-hint <code>type[Pattern]</code> to
<code>PathSpec.from_lines()</code> parameter
<code>pattern_factory</code>.</li>
<li>Fixed possible type error when using <code>+</code> and
<code>+=</code> operators on <code>PathSpec</code>.</li>
</ul>
<h2>1.1.0 (2026-04-22)</h2>
<p>New features:</p>
<ul>
<li><code>Issue
[#108](https://github.com/cpburnz/python-pathspec/issues/108)</code>_:
Specialize pattern type for <code>PathSpec</code> as
<code>PathSpec[TPattern]</code> for better debugging of
<code>PathSpec().patterns</code>.</li>
</ul>
<p>Bug fixes:</p>
<ul>
<li><code>Issue
[#93](https://github.com/cpburnz/python-pathspec/issues/93)</code>_: Git
discards invalid range notation. <code>GitIgnoreSpecPattern</code> now
discards patterns with invalid range notation like Git.</li>
<li><code>Pull
[#106](https://github.com/cpburnz/python-pathspec/issues/106)</code>_:
Fix escape() not escaping backslash characters.</li>
</ul>
<p>Improvements:</p>
<ul>
<li><code>Pull
[#110](https://github.com/cpburnz/python-pathspec/issues/110)</code>_:
Nicer debug print outs (and str for regex pattern).</li>
</ul>
<p>.. _<code>Pull
[#106](https://github.com/cpburnz/python-pathspec/issues/106)</code>: <a
href="https://redirect.github.com/cpburnz/python-pathspec/pull/106">cpburnz/python-pathspec#106</a>
.. _<code>Issue
[#108](https://github.com/cpburnz/python-pathspec/issues/108)</code>: <a
href="https://redirect.github.com/cpburnz/python-pathspec/issues/108">cpburnz/python-pathspec#108</a>
.. _<code>Pull
[#110](https://github.com/cpburnz/python-pathspec/issues/110)</code>: <a
href="https://redirect.github.com/cpburnz/python-pathspec/pull/110">cpburnz/python-pathspec#110</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/ecf71a99ca739479d450b9830f43416ea0c519c7"><code>ecf71a9</code></a>
Release v1.1.1</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/6727491ff877e570e450b078c345d9346db7e531"><code>6727491</code></a>
Improve type checking with mypy and pyright</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/c9249c8b4ca165ca8c5eea191cea4c0e6f3aa827"><code>c9249c8</code></a>
Release v1.1.0</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/a1abeba97f1fdbc3bc0e64e6c4d7ee9b63c4cf77"><code>a1abeba</code></a>
Test Iron Proxy for CI</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/0b04daeafaea8c82a6fa3e86090061dc47c61ea6"><code>0b04dae</code></a>
Test Iron Proxy for CI</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/ccaedca31c5cd904c5bb55df0f0045c675f77b7f"><code>ccaedca</code></a>
Test Iron Proxy for CI</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/06391d861d68ba4763e8c377c8bb1b9392bcc76a"><code>06391d8</code></a>
Test Iron Proxy for CI</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/45907bf50a5cabe525306b99e85779639d9ca55e"><code>45907bf</code></a>
Test Iron Proxy for CI</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/0d7c7deb138050c8586000682134d820a176bc10"><code>0d7c7de</code></a>
Pin all Github actions</li>
<li><a
href="https://github.com/cpburnz/python-pathspec/commit/36faddae807a997d04ccfc8cf00931819464260c"><code>36fadda</code></a>
Specialize patterns</li>
<li>Additional commits viewable in <a
href="https://github.com/cpburnz/python-pathspec/compare/v1.0.4...v1.1.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.15.11 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/astral-sh/ruff/commit/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.11...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `mypy` from 1.20.1 to 1.20.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's
changelog</a>.</em></p>
<blockquote>
<h3>Mypy 1.20.1</h3>
<ul>
<li>Always disable sync in SQLite cache (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21184">21184</a>)</li>
<li>Temporarily skip few base64 tests (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21193">21193</a>)</li>
<li>Revert <code>dict.__or__</code> typeshed change (Ivan Levkivskyi, PR
<a
href="https://redirect.github.com/python/mypy/pull/21186">21186</a>)</li>
<li>Fix narrowing for match case with variadic tuples (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21192">21192</a>)</li>
<li>Avoid narrowing <code>type[T]</code> in type calls (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21174">21174</a>)</li>
<li>Fix regression for catching empty tuple in except (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21153">21153</a>)</li>
<li>Fix reachability for frozenset and dict view narrowing (Shantanu, PR
<a
href="https://redirect.github.com/python/mypy/pull/21151">21151</a>)</li>
<li>Fix narrowing with chained comparison (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21150">21150</a>)</li>
<li>Avoid narrowing to unreachable at module level (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21144">21144</a>)</li>
<li>Allow dangerous identity comparisons to <code>Any</code> typed
variables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21142">21142</a>)</li>
<li><code>--warn-unused-config</code> should not be a strict flag (Ivan
Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21139">21139</a>)</li>
</ul>
<h3>Mypy 1.20.2</h3>
<ul>
<li>Use WAL with SQLite cache and fix close (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21154">21154</a>)</li>
<li>Adjust SQLite journal mode (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21217">21217</a>)</li>
<li>Correctly aggregate narrowing information on parent expressions
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21206">21206</a>)</li>
<li>Fix regression related to generic callables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21208">21208</a>)</li>
<li>Fix regression by avoiding widening types in some contexts
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21242">21242</a>)</li>
<li>Fix slicing in non-strict optional mode (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21282">21282</a>)</li>
<li>mypyc: Fix match statement semantics for &quot;or&quot; pattern
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21156">21156</a>)</li>
<li>mypyc: Fix issue with module dunder attributes (Piotr Sawicki, PR <a
href="https://redirect.github.com/python/mypy/pull/21275">21275</a>)</li>
<li>Initial support for Python 3.15.0a8 (Marc Mueller, PR <a
href="https://redirect.github.com/python/mypy/pull/21255">21255</a>)</li>
</ul>
<h3>Acknowledgements</h3>
<p>Thanks to all mypy contributors who contributed to this release:</p>
<ul>
<li>A5rocks</li>
<li>Aaron Wieczorek</li>
<li>Adam Turner</li>
<li>Ali Hamdan</li>
<li>asce</li>
<li>BobTheBuidler</li>
<li>Brent Westbrook</li>
<li>Brian Schubert</li>
<li>bzoracler</li>
<li>Chris Burroughs</li>
<li>Christoph Tyralla</li>
<li>Colin Watson</li>
<li>Donghoon Nam</li>
<li>E. M. Bray</li>
<li>Emma Smith</li>
<li>Ethan Sarp</li>
<li>George Ogden</li>
<li>getzze</li>
<li>grayjk</li>
<li>Gregor Riepl</li>
<li>Ivan Levkivskyi</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/python/mypy/commit/145a062651b5f9996b75ef32b7040bd2e885ed82"><code>145a062</code></a>
Bump version to 1.20.2</li>
<li><a
href="https://github.com/python/mypy/commit/81cd49215c288eacb987de066f02daff2553b7c7"><code>81cd492</code></a>
Fix slicing with nonstrict optional (<a
href="https://redirect.github.com/python/mypy/issues/21282">#21282</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/908d3441eecbaa2a6193165317177db834d7ca1a"><code>908d344</code></a>
[mypyc] Set dunder attrs when adding module to sys.modules (<a
href="https://redirect.github.com/python/mypy/issues/21275">#21275</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/ba28610fac9d2b33be210ca8dcfe4bc47b7af424"><code>ba28610</code></a>
Initial support for Python 3.15.0a8 (<a
href="https://redirect.github.com/python/mypy/issues/21255">#21255</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/7b0e09f48dbd3717ed008a273cd17e8e960c2037"><code>7b0e09f</code></a>
Fix match statement semantics for &quot;or&quot; pattern (<a
href="https://redirect.github.com/python/mypy/issues/21156">#21156</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/92b74f226de62f7505f5ef5cb158e8ec9c58b8b7"><code>92b74f2</code></a>
Avoid widening types in conditional_types (<a
href="https://redirect.github.com/python/mypy/issues/21242">#21242</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/0dcbfaa40b0e360a16baea9cf851955375d91b54"><code>0dcbfaa</code></a>
Fix is_overlapping_types for generic callables (<a
href="https://redirect.github.com/python/mypy/issues/21208">#21208</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/210f518dede35292033ef0d387847406a0ccef8f"><code>210f518</code></a>
Correctly aggregate narrowing information on parent expressions (<a
href="https://redirect.github.com/python/mypy/issues/21206">#21206</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/c34530e53a10e385d8b0f1af4baa88a596b5ceaa"><code>c34530e</code></a>
Only set journal mode in coordinator (<a
href="https://redirect.github.com/python/mypy/issues/21217">#21217</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/79a3ec6d01b56a27c00e9b3320c2b1d4d73a77f9"><code>79a3ec6</code></a>
Use WAL with SQLite cache, fix close (<a
href="https://redirect.github.com/python/mypy/issues/21154">#21154</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python/mypy/compare/v1.20.1...v1.20.2">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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:14:02 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
60135677dd chore(deps): bump the minor-and-patch group in /libs/cli/js-examples with 8 updates (#7673)
Bumps the minor-and-patch group in /libs/cli/js-examples with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@langchain/core](https://github.com/langchain-ai/langchainjs) |
`1.1.38` | `1.1.42` |
|
[@langchain/langgraph](https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core)
| `1.2.6` | `1.2.9` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.58.0` | `8.59.1` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.58.0` | `8.59.1` |
| [dotenv](https://github.com/motdotla/dotenv) | `17.3.1` | `17.4.2` |
| [eslint](https://github.com/eslint/eslint) | `10.1.0` | `10.2.1` |
| [prettier](https://github.com/prettier/prettier) | `3.8.1` | `3.8.3` |
| [ts-jest](https://github.com/kulshekhar/ts-jest) | `29.4.6` | `29.4.9`
|

Updates `@langchain/core` from 1.1.38 to 1.1.42
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchainjs/releases"><code>@​langchain/core</code>'s
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.1.41</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10733">#10733</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/589f29ce844eb252c2d5e6b0f8d26de37763a0d7"><code>589f29c</code></a>
Thanks <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>! -
fix(core): Update inheritance behavior for tracer metadata for special
keys</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10711">#10711</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/2e9e6969e248a53ede0659a41d0ac8dbaf291ab4"><code>2e9e696</code></a>
Thanks <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>! -
feat(core): Add chat model and llm invocation params to traced
metadata</p>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.1.40</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10694">#10694</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d3e080995bb267bf3797067ab53c96bc2a6c8e3f"><code>d3e0809</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
refactor(core): decouple tracer-only metadata defaults from runnable
metadata
<ul>
<li>Add tracer-scoped inheritable metadata/tag options in callback
manager while keeping backward-compatible aliases.</li>
<li>Move configurable-to-tracing metadata derivation into a tracer-only
path and keep <code>ensureConfig</code> metadata mirroring limited to
<code>model</code>.</li>
<li>Update <code>LangChainTracer</code> default metadata/tag handling
and add regression tests for stream events metadata behavior.</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.1.39</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10430">#10430</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d3d0922c24afcd3006fb94dcadd3ebe08fbf2383"><code>d3d0922</code></a>
Thanks <a
href="https://github.com/christian-bromann"><code>@​christian-bromann</code></a>!
- feat(langchain): support for browser tools</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/langchain-ai/langchainjs/commits">compare
view</a></li>
</ul>
</details>
<br />

Updates `@langchain/langgraph` from 1.2.6 to 1.2.9
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraphjs/releases"><code>@​langchain/langgraph</code>'s
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/langgraph</code><a
href="https://github.com/1"><code>@​1</code></a>.2.9</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2315">#2315</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/9102d526c858a4cdbe9b47dcdd062b93da93e49f"><code>9102d52</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
propagate tracer metadata defaults from configurable</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2311">#2311</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/b7c196b2142fb888dfcd9ceb1dfb4365d803c8b6"><code>b7c196b</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
fix: export missing types for typescript 6.0 declaration file
compatibility</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/458d66bf665468854abb8133594d4d4f966054ed"><code>458d66b</code></a>]:</p>
<ul>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.8.9</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/langgraph</code><a
href="https://github.com/1"><code>@​1</code></a>.2.8</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2275">#2275</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/e42c2c8836e0b7e36067fea6cc51842e1eb2c60f"><code>e42c2c8</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
enhance runtime with executionInfo and serverInfo</li>
</ul>
<h2><code>@​langchain/langgraph</code><a
href="https://github.com/1"><code>@​1</code></a>.2.7</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2281">#2281</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/2b626107101bddb13cf662e1583ea1a828c6e0cd"><code>2b62610</code></a>
Thanks <a
href="https://github.com/christian-bromann"><code>@​christian-bromann</code></a>!
- feat(sdk): support for headless tools</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraphjs/blob/main/libs/langgraph-core/CHANGELOG.md"><code>@​langchain/langgraph</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>1.2.9</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2315">#2315</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/9102d526c858a4cdbe9b47dcdd062b93da93e49f"><code>9102d52</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
propagate tracer metadata defaults from configurable</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2311">#2311</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/b7c196b2142fb888dfcd9ceb1dfb4365d803c8b6"><code>b7c196b</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
fix: export missing types for typescript 6.0 declaration file
compatibility</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/458d66bf665468854abb8133594d4d4f966054ed"><code>458d66b</code></a>]:</p>
<ul>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.8.9</li>
</ul>
</li>
</ul>
<h2>1.2.8</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2275">#2275</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/e42c2c8836e0b7e36067fea6cc51842e1eb2c60f"><code>e42c2c8</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
enhance runtime with executionInfo and serverInfo</li>
</ul>
<h2>1.2.7</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2281">#2281</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/2b626107101bddb13cf662e1583ea1a828c6e0cd"><code>2b62610</code></a>
Thanks <a
href="https://github.com/christian-bromann"><code>@​christian-bromann</code></a>!
- feat(sdk): support for headless tools</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/aba243a403ffc0795ea95693b88741465e78abec"><code>aba243a</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2313">#2313</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/9102d526c858a4cdbe9b47dcdd062b93da93e49f"><code>9102d52</code></a>
fix(langgraph): propagate tracer metadata defaults from configurable (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2315">#2315</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/b7c196b2142fb888dfcd9ceb1dfb4365d803c8b6"><code>b7c196b</code></a>
fix: export missing types for typescript 6.0 declaration file
compatibility (...</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/9e9807523f751ddcbad8023706563a24806ef50e"><code>9e98075</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2294">#2294</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/e42c2c8836e0b7e36067fea6cc51842e1eb2c60f"><code>e42c2c8</code></a>
feat: enhance runtime with executionInfo and serverInfo (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2275">#2275</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/ed1107df54760e7c8186911064b740d3231398a4"><code>ed1107d</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2283">#2283</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/2b626107101bddb13cf662e1583ea1a828c6e0cd"><code>2b62610</code></a>
feat(sdk): support for headless tools (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2281">#2281</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/05810fe087e1c23d41d3cc495985c8eb91542c83"><code>05810fe</code></a>
Fix Vue subagent grouping during streaming (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2274">#2274</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/271f9750f0aaaac9ead036d997797b174ec9743c"><code>271f975</code></a>
chore(deps): bump the langchain group across 1 directory with 7 updates
(<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2270">#2270</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/d88f29ba2558579781e60307947dba47290d58a1"><code>d88f29b</code></a>
chore(repo): migrate linting and formatting from ESLint/Prettier to
oxlint/ox...</li>
<li>See full diff in <a
href="https://github.com/langchain-ai/langgraphjs/commits/@langchain/langgraph@1.2.9/libs/langgraph-core">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.58.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/eslint-plugin</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.59.0</h2>
<h2>8.59.0 (2026-04-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
report more cases based on assignability (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11789">#11789</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Ulrich Stark</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.58.2</h2>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] use
assignability checks in checkTypePredicates (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12147">#12147</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
<li>송재욱</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md"><code>@​typescript-eslint/eslint-plugin</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.59.0 (2026-04-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
report more cases based on assignability (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11789">#11789</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Ulrich Stark</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] use
assignability checks in checkTypePredicates (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12147">#12147</a>)</li>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.2">GitHub
Releases</a> for more information.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/52457932e5507b5ca01e720a541f3f8d01e09b9d"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/3cef12471659c42c9d4b179f4113a080b1fa2d66"><code>3cef124</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in dot...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/27c507b6e532abcd1efa51055f045b00f22055c3"><code>27c507b</code></a>
test: make sort-type-constituents tests fully static (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12262">#12262</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a03b31d422be35d8ddc979f48f9317e3f33dd1ce"><code>a03b31d</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a7099a789535b5c4cae0d6435ae9441e40da12b6"><code>a7099a7</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/bfbd4a5433deaa82c2e2a8eb746ab5cd97eaecf8"><code>bfbd4a5</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/b49d4b15cdd2e6fefcdaf7a50641b46c651231f2"><code>b49d4b1</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/3097e727474ef15a776071c07fb0752ab0e9d56d"><code>3097e72</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in nam...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/676191bbd7d0601492c536c88b24ca7cd3fe7d78"><code>676191b</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in mem...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/e9dce8bdc6a7460d10654b3ec834982f96b5ea8a"><code>e9dce8b</code></a>
fix(eslint-plugin): [no-unnecessary-condition] treat void as nullish in
no-un...</li>
<li>Additional commits viewable in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.58.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/parser</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.59.0</h2>
<h2>8.59.0 (2026-04-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
report more cases based on assignability (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11789">#11789</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Ulrich Stark</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.58.2</h2>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] use
assignability checks in checkTypePredicates (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12147">#12147</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
<li>송재욱</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md"><code>@​typescript-eslint/parser</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.59.0 (2026-04-20)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.2">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.58.1 (2026-04-08)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/52457932e5507b5ca01e720a541f3f8d01e09b9d"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/ea9ae4f8817873480e3501145059f63e39e8d8a1"><code>ea9ae4f</code></a>
chore(release): publish 8.59.0</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/90c2803a4c250e0343598d41e973f95e743bf4ce"><code>90c2803</code></a>
chore(release): publish 8.58.2</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/be6b49a02d68db9bdc8985e7e9e1598700fda2fa"><code>be6b49a</code></a>
fix: remove tsbuildinfo cache file from published packages (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/12187">#12187</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/5311ed312eadf4e238324f2726ae0b1f3f2206e6"><code>5311ed3</code></a>
chore(release): publish 8.58.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `dotenv` from 17.3.1 to 17.4.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md">dotenv's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.4.1...v17.4.2">17.4.2</a>
(2026-04-12)</h2>
<h3>Changed</h3>
<ul>
<li>Improved skill files - tightened up details (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1009">#1009</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.4.0...v17.4.1">17.4.1</a>
(2026-04-05)</h2>
<h3>Changed</h3>
<ul>
<li>Change text <code>injecting</code> to <code>injected</code> (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1005">#1005</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.3.1...v17.4.0">17.4.0</a>
(2026-04-01)</h2>
<h3>Added</h3>
<ul>
<li>Add <code>skills/</code> folder with focused agent skills:
<code>skills/dotenv/SKILL.md</code> (core usage) and
<code>skills/dotenvx/SKILL.md</code> (encryption, multiple environments,
variable expansion) for AI coding agent discovery via the skills.sh
ecosystem (<code>npx skills add motdotla/dotenv</code>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Tighten up logs: <code>◇ injecting env (14) from .env</code> (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1003">#1003</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/motdotla/dotenv/commit/f116f70310abab44fbfddbaeb833698b5bf84a9b"><code>f116f70</code></a>
17.4.2</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/3a8161274fdd745239b86e604f4a7e972a1d3902"><code>3a81612</code></a>
fix visual order of faq</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/13f55a89e136b2024e68d277b836dd5260fc16cf"><code>13f55a8</code></a>
Merge branch 'skill'</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/4bbbf73f0906bd69975c48bf310a84b686e5b1b4"><code>4bbbf73</code></a>
reorganize faq</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/c3da64bb2ba1d0e02f8b9b2b7ccb7e6f7a51d56c"><code>c3da64b</code></a>
Merge pull request <a
href="https://redirect.github.com/motdotla/dotenv/issues/1009">#1009</a>
from motdotla/skill</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/6f743b173fbd6c26f7eab7040d251f9a6c8b977d"><code>6f743b1</code></a>
update source</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/fc2c6247e858a32d4024cb06a5b0c79aa35851f5"><code>fc2c624</code></a>
update skill</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/972315ba74bb2bbba4483d112e853fd26006ef8a"><code>972315b</code></a>
Tighten up skill</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/2795fce3d1ed07b4c570f1e06ab1c0d533c86997"><code>2795fce</code></a>
reorganize faq</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/d5495d4ae8e4e41ef9a682c9e00c81552794274e"><code>d5495d4</code></a>
adjust skill</li>
<li>Additional commits viewable in <a
href="https://github.com/motdotla/dotenv/compare/v17.3.1...v17.4.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.1.0 to 10.2.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/eslint/releases">eslint's
releases</a>.</em></p>
<blockquote>
<h2>v10.2.1</h2>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/14be92b6d1fa0923b8923830f2208e5e2705b002"><code>14be92b</code></a>
fix: model generator yield resumption paths in code path analysis (<a
href="https://redirect.github.com/eslint/eslint/issues/20665">#20665</a>)
(sethamus)</li>
<li><a
href="https://github.com/eslint/eslint/commit/84a19d2c32255db6b9cfc08644a607aae6d5cb62"><code>84a19d2</code></a>
fix: no-async-promise-executor false positives for shadowed Promise (<a
href="https://redirect.github.com/eslint/eslint/issues/20740">#20740</a>)
(xbinaryx)</li>
<li><a
href="https://github.com/eslint/eslint/commit/af764af0ec38225755fbf8a6f207f0c77b595a8d"><code>af764af</code></a>
fix: clarify language and processor validation errors (<a
href="https://redirect.github.com/eslint/eslint/issues/20729">#20729</a>)
(Pixel998)</li>
<li><a
href="https://github.com/eslint/eslint/commit/e251b89a38280973e468a4a9386c138f4f55d10d"><code>e251b89</code></a>
fix: update eslint (<a
href="https://redirect.github.com/eslint/eslint/issues/20715">#20715</a>)
(renovate[bot])</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/ca92ca0fb4599e8de1e2fb914e695fe7397cbe63"><code>ca92ca0</code></a>
docs: reuse markdown-it instance for markdown filter (<a
href="https://redirect.github.com/eslint/eslint/issues/20768">#20768</a>)
(Amaresh S M)</li>
<li><a
href="https://github.com/eslint/eslint/commit/57d2ee213305cee0cb55ef08e0480b57396269a9"><code>57d2ee2</code></a>
docs: Enable Eleventy incremental mode for watch (<a
href="https://redirect.github.com/eslint/eslint/issues/20767">#20767</a>)
(Amaresh S M)</li>
<li><a
href="https://github.com/eslint/eslint/commit/c1621b915742276e5f4b25efe790ca62296330dc"><code>c1621b9</code></a>
docs: fix typos in code-path-analyzer.js (<a
href="https://redirect.github.com/eslint/eslint/issues/20700">#20700</a>)
(Ayush Shukla)</li>
<li><a
href="https://github.com/eslint/eslint/commit/1418d522d10bde1960f4942afb548bc7160ec49e"><code>1418d52</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/39771e6e600f0b0617fdeafff6dd07e4211ffde6"><code>39771e6</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/71e04693def2df57268f08f3072a2749df6bf438"><code>71e0469</code></a>
docs: fix incomplete JSDoc param description in no-shadow rule (<a
href="https://redirect.github.com/eslint/eslint/issues/20728">#20728</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/22119ceb93e28f62262fc1d98ff1b1442d6e2dbf"><code>22119ce</code></a>
docs: clarify scope of for-direction rule with dead code examples (<a
href="https://redirect.github.com/eslint/eslint/issues/20723">#20723</a>)
(Amaresh S M)</li>
<li><a
href="https://github.com/eslint/eslint/commit/8f3fb77f122a5641d1833cad5d93f3f54fa3be0b"><code>8f3fb77</code></a>
docs: document <code>meta.docs.dialects</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/20718">#20718</a>)
(Pixel998)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/7ddfea9c4f62add1588c5c0b0da568c299246383"><code>7ddfea9</code></a>
chore: update dependency prettier to v3.8.2 (<a
href="https://redirect.github.com/eslint/eslint/issues/20770">#20770</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/fac40e1de2ba7646cc7cd2d3f93fbdd1f8819001"><code>fac40e1</code></a>
ci: bump pnpm/action-setup from 5.0.0 to 6.0.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20763">#20763</a>)
(dependabot[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/7246f923332522d8b3d46b6ee646fce88535f3fb"><code>7246f92</code></a>
test: add tests for SuppressionsService.load() error handling (<a
href="https://redirect.github.com/eslint/eslint/issues/20734">#20734</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4f34b1e592b0f63d766d9903998e8e36eb49d3aa"><code>4f34b1e</code></a>
chore: update pnpm/action-setup action to v5 (<a
href="https://redirect.github.com/eslint/eslint/issues/20762">#20762</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/51080eb5c98d619434e4835dbe9f1c6654aca3b8"><code>51080eb</code></a>
test: processor service (<a
href="https://redirect.github.com/eslint/eslint/issues/20731">#20731</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/e7e1889fca9b6044e08f41b38df20a1ce45808c8"><code>e7e1889</code></a>
chore: remove stale babel-eslint10 fixture and test (<a
href="https://redirect.github.com/eslint/eslint/issues/20727">#20727</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4e1a87cb8fb90e309524bc36bc5f31b9f9cfaa76"><code>4e1a87c</code></a>
test: remove redundant async/await in flat config array tests (<a
href="https://redirect.github.com/eslint/eslint/issues/20722">#20722</a>)
(Pixel998)</li>
<li><a
href="https://github.com/eslint/eslint/commit/066eabb3643b12931f991594969bcc0028f71a5f"><code>066eabb</code></a>
test: add rule metadata coverage for <code>languages</code> and
<code>docs.dialects</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/20717">#20717</a>)
(Pixel998)</li>
</ul>
<h2>v10.2.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/586ec2f43092779acc957866db4abe999112d1e1"><code>586ec2f</code></a>
feat: Add <code>meta.languages</code> support to rules (<a
href="https://redirect.github.com/eslint/eslint/issues/20571">#20571</a>)
(Copilot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/14207dee3939dc87cfa8b2fcfc271fff2cfd6471"><code>14207de</code></a>
feat: add <code>Temporal</code> to <code>no-obj-calls</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/20675">#20675</a>)
(Pixel998)</li>
<li><a
href="https://github.com/eslint/eslint/commit/bbb2c93a2b31bd30924f32fe69a9acf41f9dfe35"><code>bbb2c93</code></a>
feat: add Temporal to ES2026 globals (<a
href="https://redirect.github.com/eslint/eslint/issues/20672">#20672</a>)
(Pixel998)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/542cb3e6442a4e6ee3457c799e2a0ee23bef0c6a"><code>542cb3e</code></a>
fix: update first-party dependencies (<a
href="https://redirect.github.com/eslint/eslint/issues/20714">#20714</a>)
(Francesco Trotta)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/a2af743ea60f683d0e0de9d98267c1e7e4f5e412"><code>a2af743</code></a>
docs: add <code>language</code> to configuration objects (<a
href="https://redirect.github.com/eslint/eslint/issues/20712">#20712</a>)
(Francesco Trotta)</li>
<li><a
href="https://github.com/eslint/eslint/commit/845f23f1370892bf07d819497ac518c9e65090d6"><code>845f23f</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/5fbcf5958b897cc4df5d652924d18428db37f7ee"><code>5fbcf59</code></a>
docs: remove <code>sourceType</code> from ts playground link (<a
href="https://redirect.github.com/eslint/eslint/issues/20477">#20477</a>)
(Tanuj Kanti)</li>
<li><a
href="https://github.com/eslint/eslint/commit/8702a474659be786b6b1392e5e7c0c56355ae4a4"><code>8702a47</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/ddeaded2ab36951383ff67c60fb64ec68d29a46a"><code>ddeaded</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/2b4496691266547784a7f7ad1989ce53381bab91"><code>2b44966</code></a>
docs: add Major Releases section to Manage Releases (<a
href="https://redirect.github.com/eslint/eslint/issues/20269">#20269</a>)
(Milos Djermanovic)</li>
<li><a
href="https://github.com/eslint/eslint/commit/eab65c700ebb16a6e790910c720450c9908961fd"><code>eab65c7</code></a>
docs: update <code>eslint</code> versions in examples (<a
href="https://redirect.github.com/eslint/eslint/issues/20664">#20664</a>)
(루밀LuMir)</li>
<li><a
href="https://github.com/eslint/eslint/commit/3e4a29903bf31f0998e45ad9128a265bce1edc56"><code>3e4a299</code></a>
docs: update ESM Dependencies policies with note for own-usage packages
(<a
href="https://redirect.github.com/eslint/eslint/issues/20660">#20660</a>)
(Milos Djermanovic)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/8120e30f833474f47acc061d24d164e9f022264f"><code>8120e30</code></a>
refactor: extract no unmodified loop condition (<a
href="https://redirect.github.com/eslint/eslint/issues/20679">#20679</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/46e8469786be1b2bbb522100e1d44624d98d3745"><code>46e8469</code></a>
chore: update dependency markdownlint-cli2 to ^0.22.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20697">#20697</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/01ed3aa68477f81a7188e1498cf4906e02015b7c"><code>01ed3aa</code></a>
test: add unit tests for unicode utilities (<a
href="https://redirect.github.com/eslint/eslint/issues/20622">#20622</a>)
(Manish chaudhary)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/4d1d8f9737236603f64bbe83d5bb8001627b5611"><code>4d1d8f9</code></a>
10.2.1</li>
<li><a
href="https://github.com/eslint/eslint/commit/3e33105b05d09b5a4eb894ed75a9811fb40d65e6"><code>3e33105</code></a>
Build: changelog update for 10.2.1</li>
<li><a
href="https://github.com/eslint/eslint/commit/ca92ca0fb4599e8de1e2fb914e695fe7397cbe63"><code>ca92ca0</code></a>
docs: reuse markdown-it instance for markdown filter (<a
href="https://redirect.github.com/eslint/eslint/issues/20768">#20768</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/7ddfea9c4f62add1588c5c0b0da568c299246383"><code>7ddfea9</code></a>
chore: update dependency prettier to v3.8.2 (<a
href="https://redirect.github.com/eslint/eslint/issues/20770">#20770</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/57d2ee213305cee0cb55ef08e0480b57396269a9"><code>57d2ee2</code></a>
docs: Enable Eleventy incremental mode for watch (<a
href="https://redirect.github.com/eslint/eslint/issues/20767">#20767</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/c1621b915742276e5f4b25efe790ca62296330dc"><code>c1621b9</code></a>
docs: fix typos in code-path-analyzer.js (<a
href="https://redirect.github.com/eslint/eslint/issues/20700">#20700</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/fac40e1de2ba7646cc7cd2d3f93fbdd1f8819001"><code>fac40e1</code></a>
ci: bump pnpm/action-setup from 5.0.0 to 6.0.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20763">#20763</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/7246f923332522d8b3d46b6ee646fce88535f3fb"><code>7246f92</code></a>
test: add tests for SuppressionsService.load() error handling (<a
href="https://redirect.github.com/eslint/eslint/issues/20734">#20734</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4f34b1e592b0f63d766d9903998e8e36eb49d3aa"><code>4f34b1e</code></a>
chore: update pnpm/action-setup action to v5 (<a
href="https://redirect.github.com/eslint/eslint/issues/20762">#20762</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/1418d522d10bde1960f4942afb548bc7160ec49e"><code>1418d52</code></a>
docs: Update README</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.1.0...v10.2.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `prettier` from 3.8.1 to 3.8.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.8.3</h2>
<ul>
<li>SCSS: Prevent trailing comma in <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/pull/18471">prettier/prettier#18471</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.8.3/CHANGELOG.md#383">Changelog</a></p>
<h2>3.8.2</h2>
<ul>
<li>Support Angular v21.2</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#382">Changelog</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.8.3</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.8.2...3.8.3">diff</a></p>
<h4>SCSS: Prevent trailing comma in <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/pull/18471">#18471</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="scss"><code>// Input
$value: if(sass(false): 1; else: -1);
<p>// Prettier 3.8.2
$value: if(
sass(false): 1; else: -1,
);</p>
<p>// Prettier 3.8.3
$value: if(sass(false): 1; else: -1);
</code></pre></p>
<h1>3.8.2</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.8.1...3.8.2">diff</a></p>
<h4>Angular: Support Angular v21.2 (<a
href="https://redirect.github.com/prettier/prettier/pull/18722">#18722</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/19034">#19034</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<p>Exhaustive typechecking with <code>@default never;</code></p>
<!-- raw HTML omitted -->
<pre lang="html"><code>&lt;!-- Input --&gt;
@switch (foo) {
  @case (1) {}
  @default never;
}
<p>&lt;!-- Prettier 3.8.1 --&gt;
SyntaxError: Incomplete block &quot;default never&quot;. If you meant to
write the @ character, you should use the &quot;&amp;<a
href="https://redirect.github.com/prettier/prettier/issues/64">#64</a>;&quot;
HTML entity instead. (3:3)</p>
<p>&lt;!-- Prettier 3.8.2 --&gt;
<a href="https://github.com/switch"><code>@​switch</code></a> (foo) {
<a href="https://github.com/case"><code>@​case</code></a> (1) {}
<a href="https://github.com/default"><code>@​default</code></a> never;
}
</code></pre></p>
<p><code>arrow function</code> and <code>instanceof</code>
expressions.</p>
<!-- raw HTML omitted -->
<pre lang="html"><code>&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/prettier/commit/d7108a79ec745c04292aabf22c4c1adbd690b191"><code>d7108a7</code></a>
Release 3.8.3</li>
<li><a
href="https://github.com/prettier/prettier/commit/177f90898170d363ef64fde663e4d13170688bfe"><code>177f908</code></a>
Prevent trailing comma in SCSS <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/issues/18471">#18471</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/1cd40668c3d6f2f4cf9d87bbc9096d92361b2606"><code>1cd4066</code></a>
Release <code>@​prettier/plugin-oxc</code><a
href="https://github.com/0"><code>@​0</code></a>.1.4</li>
<li><a
href="https://github.com/prettier/prettier/commit/a8700e245038cd8cc0cf28ef06ffedbcb3fc2dfc"><code>a8700e2</code></a>
Update oxc-parser to v0.125.0</li>
<li><a
href="https://github.com/prettier/prettier/commit/752157c78eca6f0a30e5d5cb513b682c5ecfa01e"><code>752157c</code></a>
Fix tests</li>
<li><a
href="https://github.com/prettier/prettier/commit/053fd418e180b12fa2014260212fae831f5fc5ec"><code>053fd41</code></a>
Bump Prettier dependency to 3.8.2</li>
<li><a
href="https://github.com/prettier/prettier/commit/904c6365ec46726fd0e21021c52ae934b7e5abc6"><code>904c636</code></a>
Clean changelog_unreleased</li>
<li><a
href="https://github.com/prettier/prettier/commit/dc1f7fcc508d116cbf1644d69a1f0eb93e40d4a4"><code>dc1f7fc</code></a>
Update dependents count</li>
<li><a
href="https://github.com/prettier/prettier/commit/b31557cf331a02acf83e7e29d1001b070189a0d9"><code>b31557c</code></a>
Release 3.8.2</li>
<li><a
href="https://github.com/prettier/prettier/commit/96bbaeda0525bf758e464aed2f939d739a85c315"><code>96bbaed</code></a>
Support Angular v21.2 (<a
href="https://redirect.github.com/prettier/prettier/issues/18722">#18722</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/prettier/compare/3.8.1...3.8.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `ts-jest` from 29.4.6 to 29.4.9
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/kulshekhar/ts-jest/releases">ts-jest's
releases</a>.</em></p>
<blockquote>
<h2>v29.4.9</h2>
<p>Please refer to <a
href="https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v29.4.8</h2>
<p>No release notes provided.</p>
<h2>v29.4.7</h2>
<p>Please refer to <a
href="https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md">ts-jest's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/kulshekhar/ts-jest/compare/v29.4.6...v29.4.7">29.4.7</a>
(2026-04-01)</h2>
<h3>Features</h3>
<ul>
<li>support TypeScript v6 (<a
href="https://github.com/kulshekhar/ts-jest/commit/eda517d226389317d99572887d3c1aa93c81be87">eda517d</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/bac2e77231418c35cb9b9946d7d871c0ee2322a1"><code>bac2e77</code></a>
chore(release): bump version to 29.4.9</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/f8a9cc98924bd1846aac96d6c544e6626f031f81"><code>f8a9cc9</code></a>
fix: use correct registry for npm OIDC trusted publishing</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/e2eec26aa127e284719b4754937d17bd57b13518"><code>e2eec26</code></a>
fix: npm permissions</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/263f2ac73e4a0c4163f8766c3ba9d0a13ca78812"><code>263f2ac</code></a>
chore: remove npm auth token</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/5df0e45f409994b34cae7d22db60946a8158d975"><code>5df0e45</code></a>
OIDC</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/f82c1444d4af0d9f27883510acc8178c18ee8f8a"><code>f82c144</code></a>
Merge pull request <a
href="https://redirect.github.com/kulshekhar/ts-jest/issues/5250">#5250</a>
from kulshekhar/copilot/bump-patch-version</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/e6ec5aed410a803c2f972b3287cf233ce4c998f9"><code>e6ec5ae</code></a>
Update CHANGELOG.md</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/62c3199a355d3ba483f0e976c868ddc6f20d53fe"><code>62c3199</code></a>
Update CHANGELOG.md</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/052e751f124d059e1363b827f1709ed7607daded"><code>052e751</code></a>
Bump patch version to 29.4.7</li>
<li><a
href="https://github.com/kulshekhar/ts-jest/commit/f79e77b64f4a807605e277b1ea05f10abe9c94f0"><code>f79e77b</code></a>
Merge pull request <a
href="https://redirect.github.com/kulshekhar/ts-jest/issues/5249">#5249</a>
from ext/feature/ts6-peer</li>
<li>Additional commits viewable in <a
href="https://github.com/kulshekhar/ts-jest/compare/v29.4.6...v29.4.9">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for ts-jest since your current version.</p>
</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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:13:48 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4e833ceabe chore(deps): bump the minor-and-patch group in /libs/cli/js-monorepo-example with 7 updates (#7671)
Bumps the minor-and-patch group in /libs/cli/js-monorepo-example with 7
updates:

| Package | From | To |
| --- | --- | --- |
| [turbo](https://github.com/vercel/turborepo) | `2.9.3` | `2.9.7` |
| [eslint](https://github.com/eslint/eslint) | `10.1.0` | `10.2.1` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.58.0` | `8.59.1` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.58.0` | `8.59.1` |
| [prettier](https://github.com/prettier/prettier) | `3.8.1` | `3.8.3` |
| [@langchain/core](https://github.com/langchain-ai/langchainjs) |
`1.1.38` | `1.1.42` |
|
[@langchain/langgraph](https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core)
| `1.2.6` | `1.2.9` |

Updates `turbo` from 2.9.3 to 2.9.7
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vercel/turborepo/releases">turbo's
releases</a>.</em></p>
<blockquote>
<h2>Turborepo v2.9.7</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>eslint</h3>
<ul>
<li>chore: Upgrade dependencies to resolve their known vulnerabilities
by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12604">vercel/turborepo#12604</a></li>
</ul>
<h3>Examples</h3>
<ul>
<li>feat(sandbox): Bump <code>@​vercel/sandbox</code> from v1 to beta by
<a href="https://github.com/marc-vercel"><code>@​marc-vercel</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/12595">vercel/turborepo#12595</a></li>
<li>chore: Update examples to Turbo 2.9.6 by <a
href="https://github.com/cursor"><code>@​cursor</code></a>[bot] in <a
href="https://redirect.github.com/vercel/turborepo/pull/12600">vercel/turborepo#12600</a></li>
<li>examples: Add Ultracite example by <a
href="https://github.com/haydenbleasel"><code>@​haydenbleasel</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/12615">vercel/turborepo#12615</a></li>
</ul>
<h3>Changelog</h3>
<ul>
<li>fix: Align Markdown docs routing with docs/md endpoints by <a
href="https://github.com/molebox"><code>@​molebox</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/12596">vercel/turborepo#12596</a></li>
<li>fix: Support two-dot git ranges in filter selectors by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12599">vercel/turborepo#12599</a></li>
<li>feat: Graceful shutdown by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12607">vercel/turborepo#12607</a></li>
<li>test: Add stdin EOF startup regression coverage by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12609">vercel/turborepo#12609</a></li>
<li>fix: Ignore <code>SIGINT</code> in shim after spawning local
<code>turbo</code> by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12612">vercel/turborepo#12612</a></li>
<li>fix: Support pnpm v11 multi-document lockfiles by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12616">vercel/turborepo#12616</a></li>
<li>fix: Preserve graceful shutdown exit code by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12620">vercel/turborepo#12620</a></li>
<li>fix: Keep Node wrapper alive during graceful shutdown by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12622">vercel/turborepo#12622</a></li>
<li>fix: Preserve PTY graceful shutdown semantics by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12624">vercel/turborepo#12624</a></li>
<li>feat: Move Vercel auth to standard OAuth/device flows by <a
href="https://github.com/markandrus"><code>@​markandrus</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/12526">vercel/turborepo#12526</a></li>
<li>fix: Preserve legacy Vercel auth compatibility by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12629">vercel/turborepo#12629</a></li>
<li>fix: Recover Vercel auth tokens across login flows by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12631">vercel/turborepo#12631</a></li>
<li>docs: Fix TURBO_PLATFORM_ENV_DISABLED value in docs (true, not
false) by <a
href="https://github.com/sleitor"><code>@​sleitor</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/12633">vercel/turborepo#12633</a></li>
<li>chore: Update flags SDK by <a
href="https://github.com/AndyBitz"><code>@​AndyBitz</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/12646">vercel/turborepo#12646</a></li>
<li>ci: Disable AWS-backed sccache by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12663">vercel/turborepo#12663</a></li>
<li>fix: Prevent prune from overmatching gitignore entries by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12662">vercel/turborepo#12662</a></li>
<li>ci: Harden release API commits by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12664">vercel/turborepo#12664</a></li>
<li>ci: Fix release API commit paths by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12665">vercel/turborepo#12665</a></li>
<li>release(turborepo): 2.9.7-canary.14 by <a
href="https://github.com/github-actions"><code>@​github-actions</code></a>[bot]
in <a
href="https://redirect.github.com/vercel/turborepo/pull/12666">vercel/turborepo#12666</a></li>
<li>chore: Add <code>tbx</code> sandbox helper by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12668">vercel/turborepo#12668</a></li>
<li>fix: Allow npm registry in tbx sandboxes by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12669">vercel/turborepo#12669</a></li>
<li>fix: Install turbo globally in tbx base by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12670">vercel/turborepo#12670</a></li>
<li>docs: Clarify package hash file inputs by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12671">vercel/turborepo#12671</a></li>
<li>fix: Allow tbx sandboxes to use stale bases by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12672">vercel/turborepo#12672</a></li>
<li>fix: Install dotfiles during <code>tbx base refresh</code> by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12673">vercel/turborepo#12673</a></li>
<li>fix: Improve tbx sandbox startup by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12674">vercel/turborepo#12674</a></li>
<li>fix: Improve tbx sandbox startup defaults by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12675">vercel/turborepo#12675</a></li>
<li>fix: Support pnpm 11 flat patch lockfiles by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12676">vercel/turborepo#12676</a></li>
<li>docs: Fix link to passthrough variables source code by <a
href="https://github.com/Wartijn"><code>@​Wartijn</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/12643">vercel/turborepo#12643</a></li>
<li>release(turborepo): 2.9.7-canary.15 by <a
href="https://github.com/github-actions"><code>@​github-actions</code></a>[bot]
in <a
href="https://redirect.github.com/vercel/turborepo/pull/12677">vercel/turborepo#12677</a></li>
<li>fix: Avoid rerunning non-cacheable watch dependencies by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/12678">vercel/turborepo#12678</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/marc-vercel"><code>@​marc-vercel</code></a>
made their first contribution in <a
href="https://redirect.github.com/vercel/turborepo/pull/12595">vercel/turborepo#12595</a></li>
<li><a href="https://github.com/cursor"><code>@​cursor</code></a>[bot]
made their first contribution in <a
href="https://redirect.github.com/vercel/turborepo/pull/12600">vercel/turborepo#12600</a></li>
<li><a
href="https://github.com/markandrus"><code>@​markandrus</code></a> made
their first contribution in <a
href="https://redirect.github.com/vercel/turborepo/pull/12526">vercel/turborepo#12526</a></li>
<li><a href="https://github.com/AndyBitz"><code>@​AndyBitz</code></a>
made their first contribution in <a
href="https://redirect.github.com/vercel/turborepo/pull/12646">vercel/turborepo#12646</a></li>
<li><a href="https://github.com/Wartijn"><code>@​Wartijn</code></a> made
their first contribution in <a
href="https://redirect.github.com/vercel/turborepo/pull/12643">vercel/turborepo#12643</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vercel/turborepo/commit/93d74d0a6a06eeb6d7561077e9a7a68f69b8261a"><code>93d74d0</code></a>
publish 2.9.7 to registry</li>
<li><a
href="https://github.com/vercel/turborepo/commit/94775710c0eb6a0511365b49eb2c79d2265d863a"><code>9477571</code></a>
fix: Avoid rerunning non-cacheable watch dependencies (<a
href="https://redirect.github.com/vercel/turborepo/issues/12678">#12678</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/bca27f077a73a5b293c3bda312e0ef140bd952bc"><code>bca27f0</code></a>
release(turborepo): 2.9.7-canary.15 (<a
href="https://redirect.github.com/vercel/turborepo/issues/12677">#12677</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/7c64d46c9aea1066abb077310583697855533988"><code>7c64d46</code></a>
docs: Fix link to passthrough variables source code (<a
href="https://redirect.github.com/vercel/turborepo/issues/12643">#12643</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/976bdce05ea4512734522ff1e8d7ec78d9788b8a"><code>976bdce</code></a>
fix: Support pnpm 11 flat patch lockfiles (<a
href="https://redirect.github.com/vercel/turborepo/issues/12676">#12676</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/409b6d82f5a20543af2be5c798a1a019ffd8d9f5"><code>409b6d8</code></a>
fix: Improve tbx sandbox startup defaults (<a
href="https://redirect.github.com/vercel/turborepo/issues/12675">#12675</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/aab6e38816713b92af5b7bd69462c504ad009f7d"><code>aab6e38</code></a>
fix: Improve tbx sandbox startup (<a
href="https://redirect.github.com/vercel/turborepo/issues/12674">#12674</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/7a3023dc96f80ea9479d285d739f8691e19b63ec"><code>7a3023d</code></a>
fix: Install dotfiles during <code>tbx base refresh</code> (<a
href="https://redirect.github.com/vercel/turborepo/issues/12673">#12673</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/ce65df51ced5272f30b68157b5e3e5cb275a9c06"><code>ce65df5</code></a>
fix: Allow tbx sandboxes to use stale bases (<a
href="https://redirect.github.com/vercel/turborepo/issues/12672">#12672</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/fdfed0368c448af6e0e72c9c97a9ff688b17632f"><code>fdfed03</code></a>
docs: Clarify package hash file inputs (<a
href="https://redirect.github.com/vercel/turborepo/issues/12671">#12671</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vercel/turborepo/compare/v2.9.3...v2.9.7">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.1.0 to 10.2.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/eslint/releases">eslint's
releases</a>.</em></p>
<blockquote>
<h2>v10.2.1</h2>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/14be92b6d1fa0923b8923830f2208e5e2705b002"><code>14be92b</code></a>
fix: model generator yield resumption paths in code path analysis (<a
href="https://redirect.github.com/eslint/eslint/issues/20665">#20665</a>)
(sethamus)</li>
<li><a
href="https://github.com/eslint/eslint/commit/84a19d2c32255db6b9cfc08644a607aae6d5cb62"><code>84a19d2</code></a>
fix: no-async-promise-executor false positives for shadowed Promise (<a
href="https://redirect.github.com/eslint/eslint/issues/20740">#20740</a>)
(xbinaryx)</li>
<li><a
href="https://github.com/eslint/eslint/commit/af764af0ec38225755fbf8a6f207f0c77b595a8d"><code>af764af</code></a>
fix: clarify language and processor validation errors (<a
href="https://redirect.github.com/eslint/eslint/issues/20729">#20729</a>)
(Pixel998)</li>
<li><a
href="https://github.com/eslint/eslint/commit/e251b89a38280973e468a4a9386c138f4f55d10d"><code>e251b89</code></a>
fix: update eslint (<a
href="https://redirect.github.com/eslint/eslint/issues/20715">#20715</a>)
(renovate[bot])</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/ca92ca0fb4599e8de1e2fb914e695fe7397cbe63"><code>ca92ca0</code></a>
docs: reuse markdown-it instance for markdown filter (<a
href="https://redirect.github.com/eslint/eslint/issues/20768">#20768</a>)
(Amaresh S M)</li>
<li><a
href="https://github.com/eslint/eslint/commit/57d2ee213305cee0cb55ef08e0480b57396269a9"><code>57d2ee2</code></a>
docs: Enable Eleventy incremental mode for watch (<a
href="https://redirect.github.com/eslint/eslint/issues/20767">#20767</a>)
(Amaresh S M)</li>
<li><a
href="https://github.com/eslint/eslint/commit/c1621b915742276e5f4b25efe790ca62296330dc"><code>c1621b9</code></a>
docs: fix typos in code-path-analyzer.js (<a
href="https://redirect.github.com/eslint/eslint/issues/20700">#20700</a>)
(Ayush Shukla)</li>
<li><a
href="https://github.com/eslint/eslint/commit/1418d522d10bde1960f4942afb548bc7160ec49e"><code>1418d52</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/39771e6e600f0b0617fdeafff6dd07e4211ffde6"><code>39771e6</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/71e04693def2df57268f08f3072a2749df6bf438"><code>71e0469</code></a>
docs: fix incomplete JSDoc param description in no-shadow rule (<a
href="https://redirect.github.com/eslint/eslint/issues/20728">#20728</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/22119ceb93e28f62262fc1d98ff1b1442d6e2dbf"><code>22119ce</code></a>
docs: clarify scope of for-direction rule with dead code examples (<a
href="https://redirect.github.com/eslint/eslint/issues/20723">#20723</a>)
(Amaresh S M)</li>
<li><a
href="https://github.com/eslint/eslint/commit/8f3fb77f122a5641d1833cad5d93f3f54fa3be0b"><code>8f3fb77</code></a>
docs: document <code>meta.docs.dialects</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/20718">#20718</a>)
(Pixel998)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/7ddfea9c4f62add1588c5c0b0da568c299246383"><code>7ddfea9</code></a>
chore: update dependency prettier to v3.8.2 (<a
href="https://redirect.github.com/eslint/eslint/issues/20770">#20770</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/fac40e1de2ba7646cc7cd2d3f93fbdd1f8819001"><code>fac40e1</code></a>
ci: bump pnpm/action-setup from 5.0.0 to 6.0.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20763">#20763</a>)
(dependabot[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/7246f923332522d8b3d46b6ee646fce88535f3fb"><code>7246f92</code></a>
test: add tests for SuppressionsService.load() error handling (<a
href="https://redirect.github.com/eslint/eslint/issues/20734">#20734</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4f34b1e592b0f63d766d9903998e8e36eb49d3aa"><code>4f34b1e</code></a>
chore: update pnpm/action-setup action to v5 (<a
href="https://redirect.github.com/eslint/eslint/issues/20762">#20762</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/51080eb5c98d619434e4835dbe9f1c6654aca3b8"><code>51080eb</code></a>
test: processor service (<a
href="https://redirect.github.com/eslint/eslint/issues/20731">#20731</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/e7e1889fca9b6044e08f41b38df20a1ce45808c8"><code>e7e1889</code></a>
chore: remove stale babel-eslint10 fixture and test (<a
href="https://redirect.github.com/eslint/eslint/issues/20727">#20727</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4e1a87cb8fb90e309524bc36bc5f31b9f9cfaa76"><code>4e1a87c</code></a>
test: remove redundant async/await in flat config array tests (<a
href="https://redirect.github.com/eslint/eslint/issues/20722">#20722</a>)
(Pixel998)</li>
<li><a
href="https://github.com/eslint/eslint/commit/066eabb3643b12931f991594969bcc0028f71a5f"><code>066eabb</code></a>
test: add rule metadata coverage for <code>languages</code> and
<code>docs.dialects</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/20717">#20717</a>)
(Pixel998)</li>
</ul>
<h2>v10.2.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/586ec2f43092779acc957866db4abe999112d1e1"><code>586ec2f</code></a>
feat: Add <code>meta.languages</code> support to rules (<a
href="https://redirect.github.com/eslint/eslint/issues/20571">#20571</a>)
(Copilot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/14207dee3939dc87cfa8b2fcfc271fff2cfd6471"><code>14207de</code></a>
feat: add <code>Temporal</code> to <code>no-obj-calls</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/20675">#20675</a>)
(Pixel998)</li>
<li><a
href="https://github.com/eslint/eslint/commit/bbb2c93a2b31bd30924f32fe69a9acf41f9dfe35"><code>bbb2c93</code></a>
feat: add Temporal to ES2026 globals (<a
href="https://redirect.github.com/eslint/eslint/issues/20672">#20672</a>)
(Pixel998)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/542cb3e6442a4e6ee3457c799e2a0ee23bef0c6a"><code>542cb3e</code></a>
fix: update first-party dependencies (<a
href="https://redirect.github.com/eslint/eslint/issues/20714">#20714</a>)
(Francesco Trotta)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/a2af743ea60f683d0e0de9d98267c1e7e4f5e412"><code>a2af743</code></a>
docs: add <code>language</code> to configuration objects (<a
href="https://redirect.github.com/eslint/eslint/issues/20712">#20712</a>)
(Francesco Trotta)</li>
<li><a
href="https://github.com/eslint/eslint/commit/845f23f1370892bf07d819497ac518c9e65090d6"><code>845f23f</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/5fbcf5958b897cc4df5d652924d18428db37f7ee"><code>5fbcf59</code></a>
docs: remove <code>sourceType</code> from ts playground link (<a
href="https://redirect.github.com/eslint/eslint/issues/20477">#20477</a>)
(Tanuj Kanti)</li>
<li><a
href="https://github.com/eslint/eslint/commit/8702a474659be786b6b1392e5e7c0c56355ae4a4"><code>8702a47</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/ddeaded2ab36951383ff67c60fb64ec68d29a46a"><code>ddeaded</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/2b4496691266547784a7f7ad1989ce53381bab91"><code>2b44966</code></a>
docs: add Major Releases section to Manage Releases (<a
href="https://redirect.github.com/eslint/eslint/issues/20269">#20269</a>)
(Milos Djermanovic)</li>
<li><a
href="https://github.com/eslint/eslint/commit/eab65c700ebb16a6e790910c720450c9908961fd"><code>eab65c7</code></a>
docs: update <code>eslint</code> versions in examples (<a
href="https://redirect.github.com/eslint/eslint/issues/20664">#20664</a>)
(루밀LuMir)</li>
<li><a
href="https://github.com/eslint/eslint/commit/3e4a29903bf31f0998e45ad9128a265bce1edc56"><code>3e4a299</code></a>
docs: update ESM Dependencies policies with note for own-usage packages
(<a
href="https://redirect.github.com/eslint/eslint/issues/20660">#20660</a>)
(Milos Djermanovic)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/8120e30f833474f47acc061d24d164e9f022264f"><code>8120e30</code></a>
refactor: extract no unmodified loop condition (<a
href="https://redirect.github.com/eslint/eslint/issues/20679">#20679</a>)
(kuldeep kumar)</li>
<li><a
href="https://github.com/eslint/eslint/commit/46e8469786be1b2bbb522100e1d44624d98d3745"><code>46e8469</code></a>
chore: update dependency markdownlint-cli2 to ^0.22.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20697">#20697</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/01ed3aa68477f81a7188e1498cf4906e02015b7c"><code>01ed3aa</code></a>
test: add unit tests for unicode utilities (<a
href="https://redirect.github.com/eslint/eslint/issues/20622">#20622</a>)
(Manish chaudhary)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/4d1d8f9737236603f64bbe83d5bb8001627b5611"><code>4d1d8f9</code></a>
10.2.1</li>
<li><a
href="https://github.com/eslint/eslint/commit/3e33105b05d09b5a4eb894ed75a9811fb40d65e6"><code>3e33105</code></a>
Build: changelog update for 10.2.1</li>
<li><a
href="https://github.com/eslint/eslint/commit/ca92ca0fb4599e8de1e2fb914e695fe7397cbe63"><code>ca92ca0</code></a>
docs: reuse markdown-it instance for markdown filter (<a
href="https://redirect.github.com/eslint/eslint/issues/20768">#20768</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/7ddfea9c4f62add1588c5c0b0da568c299246383"><code>7ddfea9</code></a>
chore: update dependency prettier to v3.8.2 (<a
href="https://redirect.github.com/eslint/eslint/issues/20770">#20770</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/57d2ee213305cee0cb55ef08e0480b57396269a9"><code>57d2ee2</code></a>
docs: Enable Eleventy incremental mode for watch (<a
href="https://redirect.github.com/eslint/eslint/issues/20767">#20767</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/c1621b915742276e5f4b25efe790ca62296330dc"><code>c1621b9</code></a>
docs: fix typos in code-path-analyzer.js (<a
href="https://redirect.github.com/eslint/eslint/issues/20700">#20700</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/fac40e1de2ba7646cc7cd2d3f93fbdd1f8819001"><code>fac40e1</code></a>
ci: bump pnpm/action-setup from 5.0.0 to 6.0.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20763">#20763</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/7246f923332522d8b3d46b6ee646fce88535f3fb"><code>7246f92</code></a>
test: add tests for SuppressionsService.load() error handling (<a
href="https://redirect.github.com/eslint/eslint/issues/20734">#20734</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4f34b1e592b0f63d766d9903998e8e36eb49d3aa"><code>4f34b1e</code></a>
chore: update pnpm/action-setup action to v5 (<a
href="https://redirect.github.com/eslint/eslint/issues/20762">#20762</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/1418d522d10bde1960f4942afb548bc7160ec49e"><code>1418d52</code></a>
docs: Update README</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.1.0...v10.2.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.58.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/eslint-plugin</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.59.0</h2>
<h2>8.59.0 (2026-04-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
report more cases based on assignability (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11789">#11789</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Ulrich Stark</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.58.2</h2>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] use
assignability checks in checkTypePredicates (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12147">#12147</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
<li>송재욱</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md"><code>@​typescript-eslint/eslint-plugin</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.59.0 (2026-04-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
report more cases based on assignability (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11789">#11789</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Ulrich Stark</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] use
assignability checks in checkTypePredicates (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12147">#12147</a>)</li>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.2">GitHub
Releases</a> for more information.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/52457932e5507b5ca01e720a541f3f8d01e09b9d"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/3cef12471659c42c9d4b179f4113a080b1fa2d66"><code>3cef124</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in dot...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/27c507b6e532abcd1efa51055f045b00f22055c3"><code>27c507b</code></a>
test: make sort-type-constituents tests fully static (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12262">#12262</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a03b31d422be35d8ddc979f48f9317e3f33dd1ce"><code>a03b31d</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a7099a789535b5c4cae0d6435ae9441e40da12b6"><code>a7099a7</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/bfbd4a5433deaa82c2e2a8eb746ab5cd97eaecf8"><code>bfbd4a5</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/b49d4b15cdd2e6fefcdaf7a50641b46c651231f2"><code>b49d4b1</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/3097e727474ef15a776071c07fb0752ab0e9d56d"><code>3097e72</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in nam...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/676191bbd7d0601492c536c88b24ca7cd3fe7d78"><code>676191b</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in mem...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/e9dce8bdc6a7460d10654b3ec834982f96b5ea8a"><code>e9dce8b</code></a>
fix(eslint-plugin): [no-unnecessary-condition] treat void as nullish in
no-un...</li>
<li>Additional commits viewable in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.58.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/parser</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.59.0</h2>
<h2>8.59.0 (2026-04-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
report more cases based on assignability (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11789">#11789</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Ulrich Stark</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>v8.58.2</h2>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] use
assignability checks in checkTypePredicates (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12147">#12147</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
<li>송재욱</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md"><code>@​typescript-eslint/parser</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.59.0 (2026-04-20)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.58.2 (2026-04-13)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li>remove tsbuildinfo cache file from published packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12187">#12187</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Abhijeet Singh <a
href="https://github.com/cseas"><code>@​cseas</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.2">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
<h2>8.58.1 (2026-04-08)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.1">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/52457932e5507b5ca01e720a541f3f8d01e09b9d"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/ea9ae4f8817873480e3501145059f63e39e8d8a1"><code>ea9ae4f</code></a>
chore(release): publish 8.59.0</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/90c2803a4c250e0343598d41e973f95e743bf4ce"><code>90c2803</code></a>
chore(release): publish 8.58.2</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/be6b49a02d68db9bdc8985e7e9e1598700fda2fa"><code>be6b49a</code></a>
fix: remove tsbuildinfo cache file from published packages (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/12187">#12187</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/5311ed312eadf4e238324f2726ae0b1f3f2206e6"><code>5311ed3</code></a>
chore(release): publish 8.58.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `prettier` from 3.8.1 to 3.8.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.8.3</h2>
<ul>
<li>SCSS: Prevent trailing comma in <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/pull/18471">prettier/prettier#18471</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.8.3/CHANGELOG.md#383">Changelog</a></p>
<h2>3.8.2</h2>
<ul>
<li>Support Angular v21.2</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#382">Changelog</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.8.3</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.8.2...3.8.3">diff</a></p>
<h4>SCSS: Prevent trailing comma in <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/pull/18471">#18471</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="scss"><code>// Input
$value: if(sass(false): 1; else: -1);
<p>// Prettier 3.8.2
$value: if(
sass(false): 1; else: -1,
);</p>
<p>// Prettier 3.8.3
$value: if(sass(false): 1; else: -1);
</code></pre></p>
<h1>3.8.2</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.8.1...3.8.2">diff</a></p>
<h4>Angular: Support Angular v21.2 (<a
href="https://redirect.github.com/prettier/prettier/pull/18722">#18722</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/19034">#19034</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<p>Exhaustive typechecking with <code>@default never;</code></p>
<!-- raw HTML omitted -->
<pre lang="html"><code>&lt;!-- Input --&gt;
@switch (foo) {
  @case (1) {}
  @default never;
}
<p>&lt;!-- Prettier 3.8.1 --&gt;
SyntaxError: Incomplete block &quot;default never&quot;. If you meant to
write the @ character, you should use the &quot;&amp;<a
href="https://redirect.github.com/prettier/prettier/issues/64">#64</a>;&quot;
HTML entity instead. (3:3)</p>
<p>&lt;!-- Prettier 3.8.2 --&gt;
<a href="https://github.com/switch"><code>@​switch</code></a> (foo) {
<a href="https://github.com/case"><code>@​case</code></a> (1) {}
<a href="https://github.com/default"><code>@​default</code></a> never;
}
</code></pre></p>
<p><code>arrow function</code> and <code>instanceof</code>
expressions.</p>
<!-- raw HTML omitted -->
<pre lang="html"><code>&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/prettier/commit/d7108a79ec745c04292aabf22c4c1adbd690b191"><code>d7108a7</code></a>
Release 3.8.3</li>
<li><a
href="https://github.com/prettier/prettier/commit/177f90898170d363ef64fde663e4d13170688bfe"><code>177f908</code></a>
Prevent trailing comma in SCSS <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/issues/18471">#18471</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/1cd40668c3d6f2f4cf9d87bbc9096d92361b2606"><code>1cd4066</code></a>
Release <code>@​prettier/plugin-oxc</code><a
href="https://github.com/0"><code>@​0</code></a>.1.4</li>
<li><a
href="https://github.com/prettier/prettier/commit/a8700e245038cd8cc0cf28ef06ffedbcb3fc2dfc"><code>a8700e2</code></a>
Update oxc-parser to v0.125.0</li>
<li><a
href="https://github.com/prettier/prettier/commit/752157c78eca6f0a30e5d5cb513b682c5ecfa01e"><code>752157c</code></a>
Fix tests</li>
<li><a
href="https://github.com/prettier/prettier/commit/053fd418e180b12fa2014260212fae831f5fc5ec"><code>053fd41</code></a>
Bump Prettier dependency to 3.8.2</li>
<li><a
href="https://github.com/prettier/prettier/commit/904c6365ec46726fd0e21021c52ae934b7e5abc6"><code>904c636</code></a>
Clean changelog_unreleased</li>
<li><a
href="https://github.com/prettier/prettier/commit/dc1f7fcc508d116cbf1644d69a1f0eb93e40d4a4"><code>dc1f7fc</code></a>
Update dependents count</li>
<li><a
href="https://github.com/prettier/prettier/commit/b31557cf331a02acf83e7e29d1001b070189a0d9"><code>b31557c</code></a>
Release 3.8.2</li>
<li><a
href="https://github.com/prettier/prettier/commit/96bbaeda0525bf758e464aed2f939d739a85c315"><code>96bbaed</code></a>
Support Angular v21.2 (<a
href="https://redirect.github.com/prettier/prettier/issues/18722">#18722</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/prettier/compare/3.8.1...3.8.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `@langchain/core` from 1.1.38 to 1.1.42
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchainjs/releases"><code>@​langchain/core</code>'s
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.1.41</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10733">#10733</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/589f29ce844eb252c2d5e6b0f8d26de37763a0d7"><code>589f29c</code></a>
Thanks <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>! -
fix(core): Update inheritance behavior for tracer metadata for special
keys</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10711">#10711</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/2e9e6969e248a53ede0659a41d0ac8dbaf291ab4"><code>2e9e696</code></a>
Thanks <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>! -
feat(core): Add chat model and llm invocation params to traced
metadata</p>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.1.40</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10694">#10694</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d3e080995bb267bf3797067ab53c96bc2a6c8e3f"><code>d3e0809</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
refactor(core): decouple tracer-only metadata defaults from runnable
metadata
<ul>
<li>Add tracer-scoped inheritable metadata/tag options in callback
manager while keeping backward-compatible aliases.</li>
<li>Move configurable-to-tracing metadata derivation into a tracer-only
path and keep <code>ensureConfig</code> metadata mirroring limited to
<code>model</code>.</li>
<li>Update <code>LangChainTracer</code> default metadata/tag handling
and add regression tests for stream events metadata behavior.</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.1.39</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/10430">#10430</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d3d0922c24afcd3006fb94dcadd3ebe08fbf2383"><code>d3d0922</code></a>
Thanks <a
href="https://github.com/christian-bromann"><code>@​christian-bromann</code></a>!
- feat(langchain): support for browser tools</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/langchain-ai/langchainjs/commits">compare
view</a></li>
</ul>
</details>
<br />

Updates `@langchain/langgraph` from 1.2.6 to 1.2.9
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraphjs/releases"><code>@​langchain/langgraph</code>'s
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/langgraph</code><a
href="https://github.com/1"><code>@​1</code></a>.2.9</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2315">#2315</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/9102d526c858a4cdbe9b47dcdd062b93da93e49f"><code>9102d52</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
propagate tracer metadata defaults from configurable</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2311">#2311</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/b7c196b2142fb888dfcd9ceb1dfb4365d803c8b6"><code>b7c196b</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
fix: export missing types for typescript 6.0 declaration file
compatibility</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/458d66bf665468854abb8133594d4d4f966054ed"><code>458d66b</code></a>]:</p>
<ul>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.8.9</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/langgraph</code><a
href="https://github.com/1"><code>@​1</code></a>.2.8</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2275">#2275</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/e42c2c8836e0b7e36067fea6cc51842e1eb2c60f"><code>e42c2c8</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
enhance runtime with executionInfo and serverInfo</li>
</ul>
<h2><code>@​langchain/langgraph</code><a
href="https://github.com/1"><code>@​1</code></a>.2.7</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2281">#2281</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/2b626107101bddb13cf662e1583ea1a828c6e0cd"><code>2b62610</code></a>
Thanks <a
href="https://github.com/christian-bromann"><code>@​christian-bromann</code></a>!
- feat(sdk): support for headless tools</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraphjs/blob/main/libs/langgraph-core/CHANGELOG.md"><code>@​langchain/langgraph</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>1.2.9</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2315">#2315</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/9102d526c858a4cdbe9b47dcdd062b93da93e49f"><code>9102d52</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
propagate tracer metadata defaults from configurable</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2311">#2311</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/b7c196b2142fb888dfcd9ceb1dfb4365d803c8b6"><code>b7c196b</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
fix: export missing types for typescript 6.0 declaration file
compatibility</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/458d66bf665468854abb8133594d4d4f966054ed"><code>458d66b</code></a>]:</p>
<ul>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.8.9</li>
</ul>
</li>
</ul>
<h2>1.2.8</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2275">#2275</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/e42c2c8836e0b7e36067fea6cc51842e1eb2c60f"><code>e42c2c8</code></a>
Thanks <a
href="https://github.com/apps/open-swe"><code>@​open-swe</code></a>! -
enhance runtime with executionInfo and serverInfo</li>
</ul>
<h2>1.2.7</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2281">#2281</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/2b626107101bddb13cf662e1583ea1a828c6e0cd"><code>2b62610</code></a>
Thanks <a
href="https://github.com/christian-bromann"><code>@​christian-bromann</code></a>!
- feat(sdk): support for headless tools</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/aba243a403ffc0795ea95693b88741465e78abec"><code>aba243a</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2313">#2313</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/9102d526c858a4cdbe9b47dcdd062b93da93e49f"><code>9102d52</code></a>
fix(langgraph): propagate tracer metadata defaults from configurable (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2315">#2315</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/b7c196b2142fb888dfcd9ceb1dfb4365d803c8b6"><code>b7c196b</code></a>
fix: export missing types for typescript 6.0 declaration file
compatibility (...</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/9e9807523f751ddcbad8023706563a24806ef50e"><code>9e98075</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2294">#2294</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/e42c2c8836e0b7e36067fea6cc51842e1eb2c60f"><code>e42c2c8</code></a>
feat: enhance runtime with executionInfo and serverInfo (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2275">#2275</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/ed1107df54760e7c8186911064b740d3231398a4"><code>ed1107d</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2283">#2283</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/2b626107101bddb13cf662e1583ea1a828c6e0cd"><code>2b62610</code></a>
feat(sdk): support for headless tools (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2281">#2281</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/05810fe087e1c23d41d3cc495985c8eb91542c83"><code>05810fe</code></a>
Fix Vue subagent grouping during streaming (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2274">#2274</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/271f9750f0aaaac9ead036d997797b174ec9743c"><code>271f975</code></a>
chore(deps): bump the langchain group across 1 directory with 7 updates
(<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2270">#2270</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/d88f29ba2558579781e60307947dba47290d58a1"><code>d88f29b</code></a>
chore(repo): migrate linting and formatting from ESLint/Prettier to
oxlint/ox...</li>
<li>See full diff in <a
href="https://github.com/langchain-ai/langgraphjs/commits/@langchain/langgraph@1.2.9/libs/langgraph-core">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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:13:35 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
aa2a370fe4 chore(deps): bump the minor-and-patch group in /libs/checkpoint-conformance with 3 updates (#7667)
Bumps the minor-and-patch group in /libs/checkpoint-conformance with 3
updates:
[langgraph-checkpoint](https://github.com/langchain-ai/langgraph),
[ruff](https://github.com/astral-sh/ruff) and
[ty](https://github.com/astral-sh/ty).

Updates `langgraph-checkpoint` from 4.0.1 to 4.0.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraph/releases">langgraph-checkpoint's
releases</a>.</em></p>
<blockquote>
<h2>langgraph-checkpoint==4.0.3</h2>
<p>Changes since checkpoint==4.0.2</p>
<ul>
<li>release(checkpoint): 4.0.3 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7625">#7625</a>)</li>
<li>fix(checkpoint): revive lc=2 JSON blobs for safe types without
allowlist (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7582">#7582</a>)</li>
<li>chore: dedup warnings (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7257">#7257</a>)</li>
<li>chore(deps): bump langsmith from 0.6.4 to 0.7.31 in /libs/checkpoint
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7525">#7525</a>)</li>
</ul>
<h2>langgraph-checkpoint==4.0.2</h2>
<p>Changes since checkpoint==4.0.1</p>
<ul>
<li>release(checkpoint): 4.0.2 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7518">#7518</a>)</li>
<li>docs: document LANGGRAPH_STRICT_MSGPACK for checkpoint security (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7517">#7517</a>)</li>
<li>chore(deps): bump pytest from 9.0.2 to 9.0.3 in /libs/checkpoint (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7506">#7506</a>)</li>
<li>chore(deps): bump langchain-core from 1.2.23 to 1.2.28 in
/libs/checkpoint (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7453">#7453</a>)</li>
<li>chore(deps): bump the minor-and-patch group in /libs/checkpoint with
3 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7373">#7373</a>)</li>
<li>chore(deps): bump pygments from 2.19.2 to 2.20.0 in /libs/checkpoint
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7348">#7348</a>)</li>
<li>chore(deps): bump the minor-and-patch group in /libs/checkpoint with
2 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7292">#7292</a>)</li>
<li>chore(deps): bump requests from 2.32.5 to 2.33.0 in /libs/checkpoint
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7285">#7285</a>)</li>
<li>chore(deps): bump the all-dependencies group in /libs/checkpoint
with 2 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7250">#7250</a>)</li>
<li>chore(deps): bump the all-dependencies group in /libs/checkpoint
with 2 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7198">#7198</a>)</li>
<li>chore(deps): bump orjson from 3.11.5 to 3.11.6 in /libs/checkpoint
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7146">#7146</a>)</li>
<li>fix(checkpoint): don't add the task to the checkpoint batch if it
was… (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7168">#7168</a>)</li>
<li>chore(deps): bump the all-dependencies group across 1 directory with
4 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7074">#7074</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/372d54dc4ff16b8b32ae3e1534b5e8d90822e2d2"><code>372d54d</code></a>
release(checkpoint): 4.0.3 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7625">#7625</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/f4aee546add9aef6a8f72c8a94f55ec5c824da7d"><code>f4aee54</code></a>
fix(prebuilt): hydrate ToolNode state from channels via pregel helpers
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7594">#7594</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/85cd64ed69e6f4d794804dd3a8feec2d3ead6304"><code>85cd64e</code></a>
fix(checkpoint): revive lc=2 JSON blobs for safe types without allowlist
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7582">#7582</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/53a9806e659552f91adf5bb0f2de5820b87a55a0"><code>53a9806</code></a>
chore(deps): bump nbconvert from 7.17.0 to 7.17.1 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7573">#7573</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/219fbbe8d01823b790000a809266df24d794709a"><code>219fbbe</code></a>
chore(deps): bump python-dotenv from 1.2.1 to 1.2.2 in /libs/langgraph
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7574">#7574</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/aeff9549c29c5b6017e6bd6dfe19c1365baace70"><code>aeff954</code></a>
chore: node-level timeouts (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7599">#7599</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/1a248cba45377cd8f8cd02977c85725b6d3c6a5b"><code>1a248cb</code></a>
release(prebuilt): 1.0.11 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7610">#7610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/45246f6c741f677a405f03e119d7f43466cc2a0b"><code>45246f6</code></a>
feat(prebuilt): allow ToolNode tools to return list[Command |
ToolMessage] (#...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/8657df80f36625490b05a0bfa14e63ac747a8243"><code>8657df8</code></a>
chore: mixup cli formatting (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7585">#7585</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/a529b9bede1f1b6ecada4f0cc0e5c4b83526e563"><code>a529b9b</code></a>
chore(langgraph): bump version 1.1.8 -&gt; 1.1.9 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/7563">#7563</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langgraph/compare/checkpoint==4.0.1...checkpoint==4.0.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.15.8 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>0.15.11</h2>
<p>Released on 2026-04-16.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>ruff</code>] Ignore <code>RUF029</code> when function is
decorated with <code>asynccontextmanager</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24642">#24642</a>)</li>
<li>[<code>airflow</code>] Implement
<code>airflow-xcom-pull-in-template-string</code> (<code>AIR201</code>)
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/23583">#23583</a>)</li>
<li>[<code>flake8-bandit</code>] Fix <code>S103</code> false positives
and negatives in mask analysis (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24424">#24424</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/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.8...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `ty` from 0.0.27 to 0.0.33
<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.33</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-28.</p>
<h3>Notable changes</h3>
<ul>
<li>
<p>ty now prefers the declared type of an annotated assignment in more
situations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24802">#24802</a>).
Consider this example:</p>
<pre lang="py"><code>from some_library import untyped_function
<p>threshold: int | None = 0
result: str = untyped_function()
</code></pre></p>
<p>ty previously favored the <em>inferred</em> type of the right hand
side expression when <code>threshold</code> and <code>result</code> were
used. This is useful for <code>threshold</code>, as it allows something
like <code>threshold += 1</code> to work without an error: we know that
<code>threshold</code> could later become <code>None</code>, but
<em>right now</em>, we see that it is an <code>int</code>. However, for
<code>result</code>, the inferred type is <code>Unknown</code>. This is
<em>not</em> a useful type and it can lead to false negatives. Starting
with this release, ty will therefore prefer
the declared type <em>if the inferred and declared types are mutually
assignable</em>. In the above example, <code>threshold</code> will still
be inferred as <code>int</code> (or rather <code>Literal[1]</code>), but
<code>result</code> will now be inferred as <code>str</code>. If you
previously added <code>cast</code>s to work around this behavior, you
should be able to remove them after upgrading.</p>
</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Fix reporting of annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24811">#24811</a>)</li>
<li>Fix project and workspace selection (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24824">#24824</a>)</li>
<li>Fix go-to definition for generic classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24714">#24714</a>)</li>
<li>Fix receiver coloring for aliased decorators (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24884">#24884</a>)</li>
</ul>
<h3>LSP server</h3>
<ul>
<li>Add support for go-to definition in literal enum member inlay hints
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24792">#24792</a>)</li>
<li>Add support for &quot;baking&quot; keyword argument inlay hints into
the source code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24667">#24667</a>)</li>
<li>Don't allow inlay hint edits when introducing a non global scope
symbol (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24797">#24797</a>)</li>
<li>Omit semantic highlighting for unresolved symbols (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24718">#24718</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Support narrowing with aliased conditional expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24302">#24302</a>)</li>
<li>Model short-circuiting control flow in Boolean expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24458">#24458</a>)</li>
<li>Handle <code>finally</code> blocks where all
<code>try</code>/<code>except</code> blocks are terminal (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24882">#24882</a>)</li>
<li>Detect invalid <code>ClassVar</code> vs instance-attribute overrides
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24767">#24767</a>)</li>
<li>Emit diagnostic for invalid uses of <code>Unpack[...]</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24868">#24868</a>)</li>
<li>Infer lambda parameter types with <code>Callable</code> type context
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24317">#24317</a>)</li>
<li>Support <code>**</code> unpacking of <code>TypedDict</code> in
dict-literal assignments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24703">#24703</a>)</li>
<li>Support <code>Unpack[TypedDict]</code> in <code>**kwargs</code>
signatures (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24653">#24653</a>)</li>
<li>Treat <code>[*xs]</code> as an irrefutable pattern when matching on
<code>Sequence</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24787">#24787</a>)</li>
<li>Improve generics solving for unions in invariant positions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24698">#24698</a>)</li>
<li>Improve generics solving for unions when matching against protocols
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24837">#24837</a>)</li>
</ul>
<h3>Diagnostics</h3>
<!-- 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.33</h2>
<p>Released on 2026-04-28.</p>
<h3>Notable changes</h3>
<ul>
<li>
<p>ty now prefers the declared type of an annotated assignment in more
situations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24802">#24802</a>).
Consider this example:</p>
<pre lang="py"><code>from some_library import untyped_function
<p>threshold: int | None = 0
result: str = untyped_function()
</code></pre></p>
<p>ty previously favored the <em>inferred</em> type of the right hand
side expression when <code>threshold</code> and <code>result</code> were
used. This is useful for <code>threshold</code>, as it allows something
like <code>threshold += 1</code> to work without an error: we know that
<code>threshold</code> could later become <code>None</code>, but
<em>right now</em>, we see that it is an <code>int</code>. However, for
<code>result</code>, the inferred type is <code>Unknown</code>. This is
<em>not</em> a useful type and it can lead to false negatives. Starting
with this release, ty will therefore prefer
the declared type <em>if the inferred and declared types are mutually
assignable</em>. In the above example, <code>threshold</code> will still
be inferred as <code>int</code> (or rather <code>Literal[1]</code>), but
<code>result</code> will now be inferred as <code>str</code>. If you
previously added <code>cast</code>s to work around this behavior, you
should be able to remove them after upgrading.</p>
</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Fix reporting of annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24811">#24811</a>)</li>
<li>Fix project and workspace selection (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24824">#24824</a>)</li>
<li>Fix go-to definition for generic classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24714">#24714</a>)</li>
<li>Fix receiver coloring for aliased decorators (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24884">#24884</a>)</li>
</ul>
<h3>LSP server</h3>
<ul>
<li>Add support for go-to definition in literal enum member inlay hints
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24792">#24792</a>)</li>
<li>Add support for &quot;baking&quot; keyword argument inlay hints into
the source code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24667">#24667</a>)</li>
<li>Don't allow inlay hint edits when introducing a non global scope
symbol (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24797">#24797</a>)</li>
<li>Omit semantic highlighting for unresolved symbols (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24718">#24718</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Support narrowing with aliased conditional expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24302">#24302</a>)</li>
<li>Model short-circuiting control flow in Boolean expressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24458">#24458</a>)</li>
<li>Handle <code>finally</code> blocks where all
<code>try</code>/<code>except</code> blocks are terminal (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24882">#24882</a>)</li>
<li>Detect invalid <code>ClassVar</code> vs instance-attribute overrides
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24767">#24767</a>)</li>
<li>Emit diagnostic for invalid uses of <code>Unpack[...]</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24868">#24868</a>)</li>
<li>Infer lambda parameter types with <code>Callable</code> type context
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24317">#24317</a>)</li>
<li>Support <code>**</code> unpacking of <code>TypedDict</code> in
dict-literal assignments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24703">#24703</a>)</li>
<li>Support <code>Unpack[TypedDict]</code> in <code>**kwargs</code>
signatures (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24653">#24653</a>)</li>
<li>Treat <code>[*xs]</code> as an irrefutable pattern when matching on
<code>Sequence</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24787">#24787</a>)</li>
<li>Improve generics solving for unions in invariant positions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24698">#24698</a>)</li>
<li>Improve generics solving for unions when matching against protocols
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24837">#24837</a>)</li>
</ul>
<h3>Diagnostics</h3>
<ul>
<li>Add error context to <code>invalid-return-type</code> diagnostics,
<code>invalid-yield</code> diagnostics, attribute assignment diagnostics
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24770">#24770</a>,
<a
href="https://redirect.github.com/astral-sh/ruff/pull/24771">#24771</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/c512d8425418a2170e92aa7fbbd70952d4e04118"><code>c512d84</code></a>
Bump version to 0.0.33 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3368">#3368</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/4cd7b334b90eba09042700e7b654044c2d6bcd15"><code>4cd7b33</code></a>
Upgrade Depot runners from macOS 14 to 15 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3363">#3363</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/c78b8324515bf15a662f61e1d74fd85484d67e8c"><code>c78b832</code></a>
Update rui314/setup-mold digest to 9c9c13b (<a
href="https://redirect.github.com/astral-sh/ty/issues/3342">#3342</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/dea338134aef96d741a48886e414f622dbf10426"><code>dea3381</code></a>
Update actions/cache action to v5.0.5 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3343">#3343</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/d451af477bd5517e132609c3b016799d697f4182"><code>d451af4</code></a>
update typing-features and faqs (<a
href="https://redirect.github.com/astral-sh/ty/issues/3335">#3335</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/052d70bc1a7457f50c5acbafe8de7a846f7d5e77"><code>052d70b</code></a>
Update prek dependencies (<a
href="https://redirect.github.com/astral-sh/ty/issues/3344">#3344</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/66b5e878163ce4ab4810a45a2679a98011932b8b"><code>66b5e87</code></a>
Update astral-sh/setup-uv action to v8.1.0 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3345">#3345</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/7ec6712a6f02d0255de4c7f0066d5bb23987a9bb"><code>7ec6712</code></a>
Add a 'Diagnostics improvements' section to the changelogs (<a
href="https://redirect.github.com/astral-sh/ty/issues/3309">#3309</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/978dfdb38dfb568943f73779d769a965c1d5a397"><code>978dfdb</code></a>
Add version metadata publishing to the release process (<a
href="https://redirect.github.com/astral-sh/ty/issues/3292">#3292</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/4d1e1fc57ca8bfdcbcee513ba92135d2932eb279"><code>4d1e1fc</code></a>
Bump version to 0.0.32 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3302">#3302</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ty/compare/0.0.27...0.0.33">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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:12:31 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
67cd5b7718 chore(deps): bump notebook from 7.5.2 to 7.5.6 in /libs/langgraph (#7657)
Bumps [notebook](https://github.com/jupyter/notebook) from 7.5.2 to
7.5.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jupyter/notebook/releases">notebook's
releases</a>.</em></p>
<blockquote>
<h2>v7.5.6</h2>
<h2>7.5.6</h2>
<p>(<a
href="https://github.com/jupyter/notebook/compare/@jupyter-notebook/application-extension@7.5.5...2e642f0cb10be314ba5d97d709cffe41bf992d9e">Full
Changelog</a>)</p>
<h3>Security patches</h3>
<ul>
<li>CVE-2026-42557 <a
href="https://github.com/jupyterlab/jupyterlab/security/advisories/GHSA-mqcg-5x36-vfcg">https://github.com/jupyterlab/jupyterlab/security/advisories/GHSA-mqcg-5x36-vfcg</a></li>
<li>CVE-2026-40171 <a
href="https://github.com/jupyter/notebook/security/advisories/GHSA-rch3-82jr-f9w9">https://github.com/jupyter/notebook/security/advisories/GHSA-rch3-82jr-f9w9</a></li>
</ul>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Update to JupyterLab v4.5.7 <a
href="https://redirect.github.com/jupyter/notebook/pull/7902">#7902</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
</ul>
<h3>Documentation improvements</h3>
<ul>
<li>docs: Fix broken links in troubleshooting and migration docs <a
href="https://redirect.github.com/jupyter/notebook/pull/7824">#7824</a>
(<a
href="https://github.com/RamiNoodle733"><code>@​RamiNoodle733</code></a>)</li>
</ul>
<h3>Contributors to this release</h3>
<p>The following people contributed discussions, new ideas, code and
documentation contributions, and review.
See <a
href="https://github-activity.readthedocs.io/en/latest/use/#how-does-this-tool-define-contributions-in-the-reports">our
definition of contributors</a>.</p>
<p>(<a
href="https://github.com/jupyter/notebook/graphs/contributors?from=2026-03-11&amp;to=2026-04-30&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/jtpio"><code>@​jtpio</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnotebook+involves%3Ajtpio+updated%3A2026-03-11..2026-04-30&amp;type=Issues">activity</a>)
| <a
href="https://github.com/RamiNoodle733"><code>@​RamiNoodle733</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnotebook+involves%3ARamiNoodle733+updated%3A2026-03-11..2026-04-30&amp;type=Issues">activity</a>)</p>
<h2>v7.5.5</h2>
<h2>7.5.5</h2>
<p>(<a
href="https://github.com/jupyter/notebook/compare/@jupyter-notebook/application-extension@7.5.4...4f8438b0c67dc4f010bf8cd052da4f16e2ed3828">Full
Changelog</a>)</p>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Update to JupyterLab v4.5.6 <a
href="https://redirect.github.com/jupyter/notebook/pull/7861">#7861</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
<li>[7.5.x] Drop Python 3.9 on CI <a
href="https://redirect.github.com/jupyter/notebook/pull/7860">#7860</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
<li>Fix check links <a
href="https://redirect.github.com/jupyter/notebook/pull/7857">#7857</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
</ul>
<h3>Contributors to this release</h3>
<p>The following people contributed discussions, new ideas, code and
documentation contributions, and review.
See <a
href="https://github-activity.readthedocs.io/en/latest/use/#how-does-this-tool-define-contributions-in-the-reports">our
definition of contributors</a>.</p>
<p>(<a
href="https://github.com/jupyter/notebook/graphs/contributors?from=2026-02-24&amp;to=2026-03-11&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/jtpio"><code>@​jtpio</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnotebook+involves%3Ajtpio+updated%3A2026-02-24..2026-03-11&amp;type=Issues">activity</a>)</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/jupyter/notebook/blob/@jupyter-notebook/tree@7.5.6/CHANGELOG.md">notebook's
changelog</a>.</em></p>
<blockquote>
<h2>7.5.6</h2>
<p>(<a
href="https://github.com/jupyter/notebook/compare/@jupyter-notebook/application-extension@7.5.5...2e642f0cb10be314ba5d97d709cffe41bf992d9e">Full
Changelog</a>)</p>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Update to JupyterLab v4.5.7 <a
href="https://redirect.github.com/jupyter/notebook/pull/7902">#7902</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
</ul>
<h3>Documentation improvements</h3>
<ul>
<li>docs: Fix broken links in troubleshooting and migration docs <a
href="https://redirect.github.com/jupyter/notebook/pull/7824">#7824</a>
(<a
href="https://github.com/RamiNoodle733"><code>@​RamiNoodle733</code></a>)</li>
</ul>
<h3>Contributors to this release</h3>
<p>The following people contributed discussions, new ideas, code and
documentation contributions, and review.
See <a
href="https://github-activity.readthedocs.io/en/latest/use/#how-does-this-tool-define-contributions-in-the-reports">our
definition of contributors</a>.</p>
<p>(<a
href="https://github.com/jupyter/notebook/graphs/contributors?from=2026-03-11&amp;to=2026-04-30&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/jtpio"><code>@​jtpio</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnotebook+involves%3Ajtpio+updated%3A2026-03-11..2026-04-30&amp;type=Issues">activity</a>)
| <a
href="https://github.com/RamiNoodle733"><code>@​RamiNoodle733</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnotebook+involves%3ARamiNoodle733+updated%3A2026-03-11..2026-04-30&amp;type=Issues">activity</a>)</p>
<!-- raw HTML omitted -->
<h2>7.5.5</h2>
<p>(<a
href="https://github.com/jupyter/notebook/compare/@jupyter-notebook/application-extension@7.5.4...4f8438b0c67dc4f010bf8cd052da4f16e2ed3828">Full
Changelog</a>)</p>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Update to JupyterLab v4.5.6 <a
href="https://redirect.github.com/jupyter/notebook/pull/7861">#7861</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
<li>[7.5.x] Drop Python 3.9 on CI <a
href="https://redirect.github.com/jupyter/notebook/pull/7860">#7860</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
<li>Fix check links <a
href="https://redirect.github.com/jupyter/notebook/pull/7857">#7857</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
</ul>
<h3>Contributors to this release</h3>
<p>The following people contributed discussions, new ideas, code and
documentation contributions, and review.
See <a
href="https://github-activity.readthedocs.io/en/latest/use/#how-does-this-tool-define-contributions-in-the-reports">our
definition of contributors</a>.</p>
<p>(<a
href="https://github.com/jupyter/notebook/graphs/contributors?from=2026-02-24&amp;to=2026-03-11&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/jtpio"><code>@​jtpio</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnotebook+involves%3Ajtpio+updated%3A2026-02-24..2026-03-11&amp;type=Issues">activity</a>)</p>
<h2>7.5.4</h2>
<p>(<a
href="https://github.com/jupyter/notebook/compare/@jupyter-notebook/application-extension@7.5.3...e5d8418b706fcefd4208bb61c22399dd3123555b">Full
Changelog</a>)</p>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Update to JupyterLab v4.5.5 <a
href="https://redirect.github.com/jupyter/notebook/pull/7842">#7842</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
<li>Fix PyO3 CI failure with Python 3.15 <a
href="https://redirect.github.com/jupyter/notebook/pull/7836">#7836</a>
(<a href="https://github.com/jtpio"><code>@​jtpio</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/jupyter/notebook/commit/1ab2d2b99261996e94069ca53dd3d74b8b2ee1ba"><code>1ab2d2b</code></a>
Publish 7.5.6</li>
<li><a
href="https://github.com/jupyter/notebook/commit/50e5222c9670121c3369900c7dce01aae53823fc"><code>50e5222</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/jupyter/notebook/commit/2e642f0cb10be314ba5d97d709cffe41bf992d9e"><code>2e642f0</code></a>
Update to JupyterLab v4.5.7 (<a
href="https://redirect.github.com/jupyter/notebook/issues/7902">#7902</a>)</li>
<li><a
href="https://github.com/jupyter/notebook/commit/4b93f98b5a6e57027a2e1d58694b56e2ebd793a3"><code>4b93f98</code></a>
Backport PR <a
href="https://redirect.github.com/jupyter/notebook/issues/7824">#7824</a>:
docs: Fix broken links in troubleshooting and migration do...</li>
<li><a
href="https://github.com/jupyter/notebook/commit/9a2c88fe646bac05b39dbe53e3e0ce95cafee016"><code>9a2c88f</code></a>
Publish 7.5.5</li>
<li><a
href="https://github.com/jupyter/notebook/commit/4f8438b0c67dc4f010bf8cd052da4f16e2ed3828"><code>4f8438b</code></a>
Update to JupyterLab v4.5.6 (<a
href="https://redirect.github.com/jupyter/notebook/issues/7861">#7861</a>)</li>
<li><a
href="https://github.com/jupyter/notebook/commit/f78fcfada85f9e4b46003bc1b831c83e6f4c30b3"><code>f78fcfa</code></a>
Backport PR <a
href="https://redirect.github.com/jupyter/notebook/issues/7857">#7857</a>:
Fix check links (<a
href="https://redirect.github.com/jupyter/notebook/issues/7858">#7858</a>)</li>
<li><a
href="https://github.com/jupyter/notebook/commit/9e4cf2a44594e650e1ae3da49f81ae420135f32f"><code>9e4cf2a</code></a>
[7.5.x] Drop Python 3.9 on CI (<a
href="https://redirect.github.com/jupyter/notebook/issues/7860">#7860</a>)</li>
<li><a
href="https://github.com/jupyter/notebook/commit/ecc3aaf1bbf8f9cbec9c5d85df79db0f62b6d1e6"><code>ecc3aaf</code></a>
Publish 7.5.4</li>
<li><a
href="https://github.com/jupyter/notebook/commit/e5d8418b706fcefd4208bb61c22399dd3123555b"><code>e5d8418</code></a>
Update to JupyterLab v4.5.5 (<a
href="https://redirect.github.com/jupyter/notebook/issues/7842">#7842</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/jupyter/notebook/compare/@jupyter-notebook/tree@7.5.2...@jupyter-notebook/tree@7.5.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=notebook&package-manager=uv&previous-version=7.5.2&new-version=7.5.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 23:11:51 -07:00
Nick HollonandGitHub b82d380634 release: alpha bump prebuilt 1.1.0a2, langgraph 1.2.0a5 (#7682) 2026-05-01 13:56:21 -04:00
afdfbd55da fix(langgraph): coerce dict/str writes in _messages_delta_reducer (#7680)
## Problem

`_messages_delta_reducer` assumed writes always contain pre-typed
`BaseMessage` objects (as noted in its docstring). In practice,
HTTP-driven graphs always receive message input as JSON dicts — the same
way `add_messages` receives them. Using
`DeltaChannel(_messages_delta_reducer)` with any HTTP input would crash
with:

```
AttributeError: 'dict' object has no attribute 'id'
```

This makes `_messages_delta_reducer` unusable for the primary motivating
use-case (replacing `add_messages` in production LLM graphs).

## Fix

Mirror the coercion contract of `add_messages`:
- Regular message dicts (`{"role": "human", "content": "..."}`) →
`convert_to_messages`
- `RemoveMessage` dicts (`{"type": "remove", "id": "..."}`) →
`RemoveMessage` directly (langchain_core's `convert_to_messages` doesn't
support this format)
- `BaseMessage` objects → pass through unchanged
- Lists/sequences → element-wise coercion of the above

The fix is a small `_coerce_one` + `_to_msgs` helper pair that replaces
the previous `[w] if isinstance(w, BaseMessage) else w` generator.

## Tests

Added `test_delta_channel_dict_coercion` in `test_channels.py` covering:
- dict append via `{"role": "human", "content": ..., "id": ...}`
- dict update-in-place (same ID)
- `{"type": "remove", "id": ...}` tombstoning

All 23 existing delta-channel tests still pass.

Release Notes: None

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 13:56:01 -04:00
Nick HollonandGitHub 15113c0f60 fix(prebuilt): scope ToolCallTransformer projection to its own namespace (#7681) 2026-05-01 13:29:42 -04:00
Nick HollonandGitHub 85bca24635 release: alpha bump prebuilt 1.1.0a1, langgraph 1.2.0a4 (#7679) 2026-05-01 11:55:05 -04:00
Nick HollonandGitHub f2bd3224f0 feat(langgraph): dispatch stream_events(version='v3') on Pregel (#7677) 2026-05-01 11:30:32 -04:00
Sydney RunkleandGitHub 530fcabfc3 release: alpha bump (a3) for langgraph, checkpoint, checkpoint-postgres (#7678)
## Summary
- Bumps `langgraph` 1.2.0a1 → 1.2.0a3
- Bumps `langgraph-checkpoint` 4.1.0a1 → 4.1.0a3
- Bumps `langgraph-checkpoint-postgres` 3.1.0a1 → 3.1.0a3
- Bumps min `langgraph-checkpoint` constraint in `langgraph` to
`>=4.1.0a3`
- Refreshes uv locks across the workspace

(Note: `a2` was already cut from another branch.)

## Test plan
- [ ] CI passes
2026-05-01 11:18:52 -04:00
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>
2026-05-01 11:06:54 -04:00
Quanzheng LongandGitHub c8c58a0768 fix(langgraph): make NodeTimeoutError retryable by default (#7659)
## Summary

`NodeTimeoutError` previously inherited from `TimeoutError`, which is a
subclass of `OSError`. Since `OSError` is in the default `RetryPolicy`
blocklist, timeout errors from `TimeoutPolicy` were silently **not
retried** unless the user explicitly set `retry_on=NodeTimeoutError`.

This PR changes `NodeTimeoutError` to inherit from `Exception` directly,
so that the default `RetryPolicy` treats it as retryable — matching user
expectations when both `RetryPolicy` and `TimeoutPolicy` are configured
together.

- Change `NodeTimeoutError(TimeoutError)` →
`NodeTimeoutError(Exception)`
- Add test asserting `NodeTimeoutError` is retryable with the default
policy
- Add observer-ordering tests pinning down `finish=error` emission
timing relative to retry backoff, error handler start, and retry
exhaustion

## Breaking change

Code that catches `NodeTimeoutError` via `except TimeoutError` or
`except OSError` will no longer match. Use `except NodeTimeoutError`
instead.

## Test plan

- [x] `test_should_retry_default_retry_on` — asserts `NodeTimeoutError`
is retryable with default `RetryPolicy()`
- [x] Existing timeout+retry tests continue to pass (`test_retry.py`)
2026-04-30 12:17:13 -07:00
Nick HollonandGitHub de9b7c61c3 fix(langgraph): arrival-ordered interleave for StreamChannel projections (#7643) 2026-04-30 10:41:43 -04:00
Quanzheng LongandGitHub 63d861165f feat(langgraph): add node-level error handlers (#7233) 2026-04-29 21:27:06 -07:00
Eugene YurtsevandGitHub 9c1d65695e fix(prebuilt): default ToolRuntime tools to empty list (#7650)
Makes `ToolRuntime.tools` default to an empty list when not provided,
which avoids requiring callers and tests to pass it explicitly. Adds a
focused regression test covering direct `ToolRuntime` construction
without `tools`.

Created with [Deep Agents
CLI](https://docs.langchain.com/oss/python/deepagents/cli/overview)
using gpt-5.4 (provider: openai).
2026-04-30 01:07:00 +00:00
40ab009c62 feat: allow graph to graceful shutdown/drain by request (#7274)
## Summary

Adds cooperative drain support for Pregel runs so a graph can be asked
to stop at the next superstep boundary, persist its checkpoint, and
surface a resumable terminal exception.

- New `RunControl` (in `langgraph.runtime`) — a thread-safe handle whose
`request_drain(reason="shutdown")` sets a single flag.
- New `GraphDrained(GraphBubbleUp)` exception (in `langgraph.errors`)
raised when a run exits early due to drain. Carries the `reason` string.
- New `control: RunControl | None` kwarg on `invoke` / `ainvoke` /
`stream` / `astream` / `stream_v2` / `astream_v2`. Wired through to
`Runtime.control`, so nodes can read `runtime.control.drain_requested` /
`drain_reason` and even call `request_drain()` from inside a node.
- Stream transformers learn `"drained"` as a terminal `SubgraphStatus`.

The intended use is hooking SIGTERM (or any external supervisor signal)
to `control.request_drain("sigterm")` so an in-flight graph run can stop
cleanly and be resumed later from the saved checkpoint.

## Semantics: cooperative, between-superstep

`request_drain()` flips a flag. The Pregel loop checks it at the top of
each `tick()`, **after** the previous superstep's writes have been
applied and checkpointed. It never preempts work that is already
running.

| Scenario | Behavior |
|---|---|
| Node mid-execution (blocking I/O, sleeps, etc.) | Runs to completion.
Drain takes effect on the next superstep. |
| Node with a retry policy currently retrying | Retry loop runs to
exhaustion or success (drain is not checked between retries). Drain
takes effect on the next superstep. |
| Functional API: `@entrypoint` with pending `@task` futures |
Entrypoint and all dispatched tasks complete; drain takes effect after
the entrypoint returns. |
| Graph naturally finishes on the same tick where drain was requested
(no more tasks) | Treated as `done`; returns normally. **No
`GraphDrained` is raised.** The caller can inspect
`control.drain_requested` afterwards to distinguish a
drained-but-completed run from a normal one. |
| More tasks remain | Raises `GraphDrained(reason)`. The checkpoint of
the last completed superstep is saved (also under `durability="exit"`).
Resume with `invoke(None, config)` / `ainvoke(None, config)`. |
| Subgraph requests drain | `GraphDrained` bubbles up through the parent
loop and stops it at its own next superstep boundary; the parent's
checkpoint is saved and resumable. |

Drain does **not** cancel asyncio tasks or kill threads. Pair it with a
graceful timeout + `task.cancel()` (or process exit) if you need a hard
upper bound — see `test_drain_then_cancel_after_graceful_timeout` for
the recommended pattern.

## Usage

```python
from langgraph.runtime import RunControl
from langgraph.errors import GraphDrained

control = RunControl()

# In a signal handler, supervisor, etc.:
# control.request_drain("sigterm")

try:
    result = graph.invoke(input, config, control=control)
    if control.drain_requested:
        # finished naturally on the same tick where drain was requested
        ...
except GraphDrained as e:
    # checkpoint saved; resume later with the same config
    log.info("graph drained: %s", e.reason)
```

## Test plan

- [x] Sync + async drain stops the next superstep
(`test_run_control_request_drain_stops_future_steps[_async]`)
- [x] Drain on the terminal step finishes normally
(`test_drain_requested_in_terminal_step_finishes_normally[_async]`)
- [x] `durability=\"exit\"` persists a resumable checkpoint on drain
(`test_drain_with_exit_durability_persists_resume_checkpoint`)
- [x] Subgraph drain bubbles up and parent resumes correctly
(`test_drain_from_subgraph_can_resume_parent`)
- [x] External thread / task triggering drain mid-run
(`test_external_drain_concurrent_sync` / `_async`)
- [x] Drain + hard cancel after graceful timeout
(`test_drain_then_cancel_after_graceful_timeout`)
- [x] Functional API: in-flight `@task` futures still resolve after
`request_drain()`
(`test_request_drain_allows_inflight_[a]call_scheduling`)
- [x] `control` kwarg wired through `stream_v2`
(`test_stream_v2_accepts_control_for_drain`)
- [x] `Runtime.merge` preserves `control`
(`test_merge_runtime_preserves_run_control`)

---------

Co-authored-by: Quanzheng Long <long@langchain.dev>
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 15:23:31 -07:00
Sydney RunkleandGitHub f95d2309f9 release(checkpoint-postgres): pin to checkpoint 4.1.0a1 (#7648)
## Summary
- Bumps `langgraph-checkpoint-postgres`'s pin on `langgraph-checkpoint`
from `>=4.0.3,<5.0.0` to `>=4.1.0a1,<5.0.0` so the postgres alpha
(3.1.0a1) requires the matching checkpoint alpha released in #7647.
- Mirrors the same pin update applied to `langgraph` (1.2.0a1) on
`wfh/releases/timers`.

## Why
The three alphas (checkpoint 4.1.0a1, checkpoint-postgres 3.1.0a1,
langgraph 1.2.0a1) are meant to be tested as a coherent set. `langgraph`
already pins `>=4.1.0a1`; checkpoint-postgres was missed in that release
and is still letting resolvers fall back to 4.0.x.

## Test plan
- [ ] CI green
- [ ] `uv lock` resolves cleanly across libs (verified locally via `make
lock` — no lock file changes since editable paths already resolved to
4.1.0a1)
2026-04-29 18:00:47 -04:00
William FHGitHubWill Fu-HinthornSydney Runklecopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
4a5765dd23 release: alpha for timers (#7647)
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
Co-authored-by: Sydney Runkle <sydneymarierunkle@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-04-29 17:49:01 -04:00
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>
2026-04-29 17:26:17 -04:00
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>
2026-04-29 12:37:19 -07:00
open-swe[bot]GitHubopen-swe[bot] <open-swe@users.noreply.github.com>Mason DaughertyMason Daugherty
168674dd2a chore: update x links to langchain_oss (#7645)
## Description
Updates the X/Twitter social links to point to the new `@langchain_oss`
account across README badges and Python package metadata.

## Test Plan
- [ ] Verify README badges and package metadata point to
`@langchain_oss` on X

_Opened collaboratively by Mason Daugherty and open-swe._

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Mason Daugherty <61371264+mdrxy@users.noreply.github.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2026-04-29 14:02:51 -04:00
William FHGitHubWill Fu-HinthornClaude Opus 4.7copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
800071d0d4 chore: idle timeout (#7631)
## Summary

Adds per-node `timeout` support to async StateGraph/Pregel nodes and to
the functional API (`@task` / `@entrypoint`). A timeout caps how long a
single node attempt may run, either as a hard wall-clock budget
(`run_timeout`), or as an idle window that resets on observable progress
(`idle_timeout`), or both. When exceeded, LangGraph raises
`NodeTimeoutError`, clears writes from the failed attempt, and lets the
existing retry policy decide whether to retry.

## Public API

A single `timeout=` kwarg on `add_node`, `@task`, `@entrypoint`, and
`NodeBuilder.set_timeout`. Pass a number/`timedelta` for the simple case
(treated as a hard wall-clock cap), or a `TimeoutPolicy` (in
`langgraph.types`) for finer control:

```python
from datetime import timedelta
from langgraph.types import TimeoutPolicy

# simple: hard wall-clock cap on each attempt
builder.add_node("call_model", call_model, timeout=60)
builder.add_node("call_model", call_model, timeout=timedelta(minutes=2))

# full control
builder.add_node(
    "call_model",
    call_model,
    timeout=TimeoutPolicy(
        run_timeout=120,                # hard wall-clock cap in seconds, never refreshed
        idle_timeout=30,                # cap on time without observable progress, units in seconds
        refresh_on="auto",              # "auto" | "heartbeat"
    ),
)
```

- `run_timeout`: hard wall-clock cap on a single attempt; never
refreshed.
- `idle_timeout`: progress-resetting cap. Refreshed by writes, stream
output, yielded async stream chunks, child-task scheduling, runtime
stream-writer calls, and any LangChain callback event from descendants
of the node's run. `runtime.heartbeat()` is a manual signal for work
that doesn't naturally emit any of these.
- `refresh_on="heartbeat"` narrows the refresh source to explicit
`runtime.heartbeat()` only — useful when you want a strict idle
definition that isn't reset by chatty subordinates.

For long-running async work that doesn't naturally emit progress:

```python
async def call_model(state: State, runtime: Runtime) -> State:
    while still_working:
        ...
        runtime.heartbeat()
    return {"messages": [response]}
```

`NodeTimeoutError` subclasses `TimeoutError` and carries `node`,
`timeout`, `run_timeout`, `idle_timeout`, `elapsed`, and `kind` (`"run"`
or `"idle"`). If the node's `retry_policy` permits `TimeoutError` it'll
retry; the timer resets per attempt.

## Why async-only

Sync Python code cannot be safely cancelled in-process, so timeouts only
apply to async nodes/tasks. Sync nodes with a `timeout` are rejected at
compile time (covers direct nodes, wrapped runnables, sequences, and
`RunnableParallel` branches); `run_with_retry` rejects them at runtime
as a safety net.

## What gets cancelled and what doesn't

When a watchdog fires:

1. The attempt scope is closed under a lock so any in-flight
`CONFIG_KEY_SEND` / stream / child-task scheduling that races with the
timeout is dropped atomically.
2. Buffered `task.writes` are cleared so pre-timeout writes from the
failed attempt don't leak into the checkpoint after a retry succeeds.
3. The background `asyncio.Task` is cancelled; its eventual exception is
drained via a done-callback so asyncio doesn't log it.

Only the watchdog's own `TimeoutError` converts to `NodeTimeoutError`,
so user-raised `asyncio.TimeoutError`, built-in `TimeoutError`, and
`NodeTimeoutError` from a child node continue to propagate unchanged.

Child tasks already scheduled before the timeout fired still complete —
they aren't part of the cancelled task's structured cancellation
surface. This is intentional and tested.

## External-watchdog hook

WARNING: THIS API IS IN ALPHA AND SUBJECT TO CHANGE.

`CONFIG_KEY_TIMED_ATTEMPT_OBSERVER` is a per-config callback that
receives lifecycle events for each timed attempt:

- `start` — fired before the proc runs, with `task_id`, `task_name`,
`attempt`, `run_id`, `thread_id`, `checkpoint_ns`, `started_at`, and the
configured `run_timeout_secs` / `idle_timeout_secs` / `refresh_on`.
- `progress` — fired on each progress signal that resets the idle clock,
rate-limited to ~4 events per `idle_timeout` window so token-rate
callbacks don't flood the observer. Carries the same context plus
`progress_at`.
- `finish` — fired with `finished_at`, `status` (`"success"`/`"error"`),
`error_type`, `error_message`. `ParentCommand` and `GraphBubbleUp` are
treated as control flow, not errors.

This lets an orchestrating process listen to `start` + rolling
`progress` to compute its own kill deadline (`progress_at +
idle_timeout_secs`) and hard-kill a worker process if the in-process
cancellation deadlocks. Observer callbacks run in whatever thread fires
them, and any exception they raise is logged and swallowed.

## Implementation Notes

`runtime.heartbeat()` updates the progress timestamp without taking the
guarded write lock. This avoids lock overhead on high-frequency
callback/token paths and accepts a small timestamp race window, which is
negligible for expected coarse idle-timeout configurations.

## Testing

Covers timeout retry behavior, user-raised timeout propagation,
stale-write suppression, stream / callback / heartbeat progress resets,
sync-node rejection, `RunnableParallel` branch validation,
`StateGraph.add_node` behavior, lower-level Pregel behavior, observer
start/progress/finish events, and functional API compatibility.

---------

Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-04-29 10:12:28 -07:00
Nick HollonandGitHub 3eb73e8ad2 feat(langgraph): native v2 projections for custom, updates, checkpoints, debug, tasks (#7640) 2026-04-29 09:43:49 -04:00
Nick HollonandGitHub 08666353fc fix(langgraph): decouple run.output/interrupted/interrupts from ValuesTransformer (#7639) 2026-04-29 09:08:21 -04:00
Nick HollonandGitHub 5af4c5addf refactor(langgraph,prebuilt): merge EventLog into StreamChannel with optional name (#7637) 2026-04-28 18:43:51 -04:00
Nick HollonandGitHub f4388df77f feat(langgraph): add streaming transformer infrastructure and tests (#7519) 2026-04-28 20:29:21 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>jkennedyvzClaude Opus 4.7
521b4842d3 chore(deps): bump the minor-and-patch group across 1 directory with 4 updates (ty held back) (#7635)
Bumps the minor-and-patch group with 5 updates in the /libs/sdk-py
directory:

| Package | From | To |
| --- | --- | --- |
| [orjson](https://github.com/ijl/orjson) | `3.11.7` | `3.11.8` |
| [ruff](https://github.com/astral-sh/ruff) | `0.15.6` | `0.15.12` |
| [mypy](https://github.com/python/mypy) | `1.19.1` | `1.20.2` |
| [ty](https://github.com/astral-sh/ty) | `0.0.23` | `0.0.32` |
| [pydantic](https://github.com/pydantic/pydantic) | `2.12.5` | `2.13.3`
|


Updates `orjson` from 3.11.7 to 3.11.8
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ijl/orjson/releases">orjson's
releases</a>.</em></p>
<blockquote>
<h2>3.11.8</h2>
<h3>Changed</h3>
<ul>
<li>Build and compatibility improvements.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ijl/orjson/blob/master/CHANGELOG.md">orjson's
changelog</a>.</em></p>
<blockquote>
<h2>3.11.8 - 2026-03-31</h2>
<h3>Changed</h3>
<ul>
<li>Build and compatibility improvements.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/ijl/orjson/commit/5cbb3d0398a2f42de51210270286fecd798c5d78"><code>5cbb3d0</code></a>
3.11.8</li>
<li><a
href="https://github.com/ijl/orjson/commit/4195d7f263e33076295b75efdcbaf6a55af8674e"><code>4195d7f</code></a>
writer::half</li>
<li><a
href="https://github.com/ijl/orjson/commit/d00641b69410728a735f0855eb1c2843b0a5819b"><code>d00641b</code></a>
writer::uuid</li>
<li><a
href="https://github.com/ijl/orjson/commit/c84d9b4ba4853781af943fa5c493e261e2f82b84"><code>c84d9b4</code></a>
build and compatibility misc</li>
<li><a
href="https://github.com/ijl/orjson/commit/4547234b681fac5e0e0734cf44c21e75f9654e43"><code>4547234</code></a>
ffi::numpy</li>
<li><a
href="https://github.com/ijl/orjson/commit/0d4a5ad1f17a72528ba027554466fdec6580cdeb"><code>0d4a5ad</code></a>
datetime PyRef idiom</li>
<li><a
href="https://github.com/ijl/orjson/commit/e93a13d372ec956d027e71d023eb534b8445ac85"><code>e93a13d</code></a>
Cross-compile avoids maturin v1.12 build-details.json error</li>
<li>See full diff in <a
href="https://github.com/ijl/orjson/compare/3.11.7...3.11.8">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.15.6 to 0.15.12
<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.15.12</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>Install ruff 0.15.12</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.15.12/ruff-installer.sh
| sh
</code></pre>
<!-- 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.15.12</h2>
<p>Released on 2026-04-24.</p>
<h3>Preview features</h3>
<ul>
<li>Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23599">#23599</a>)</li>
<li>Implement <code>#ruff:ignore</code> logical-line suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23404">#23404</a>)</li>
<li>Revert preview changes to displayed diagnostic severity in LSP (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24789">#24789</a>)</li>
<li>[<code>airflow</code>] Implement
<code>task-branch-as-short-circuit</code> (<code>AIR004</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23579">#23579</a>)</li>
<li>[<code>flake8-bugbear</code>] Fix
<code>break</code>/<code>continue</code> handling in
<code>loop-iterator-mutation</code> (<code>B909</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24440">#24440</a>)</li>
<li>[<code>pylint</code>] Fix <code>PLC2701</code> for type parameter
scopes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24576">#24576</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>pandas-vet</code>] Suggest <code>.array</code> as well in
<code>PD011</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24805">#24805</a>)</li>
</ul>
<h3>CLI</h3>
<ul>
<li>Respect default Unix permissions for cache files (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24794">#24794</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pylint</code>] Fix <code>PLR0124</code> description not to
claim self-comparison always returns the same value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24749">#24749</a>)</li>
<li>[<code>pyupgrade</code>] Expand docs on reusable
<code>TypeVar</code>s and scoping (<code>UP046</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24153">#24153</a>)</li>
<li>Improve rules table accessibility (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24711">#24711</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a
href="https://github.com/woodruffw"><code>@​woodruffw</code></a></li>
<li><a
href="https://github.com/avasis-ai"><code>@​avasis-ai</code></a></li>
<li><a href="https://github.com/Dev-iL"><code>@​Dev-iL</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a
href="https://github.com/ShipItAndPray"><code>@​ShipItAndPray</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a
href="https://github.com/augustelalande"><code>@​augustelalande</code></a></li>
<li><a
href="https://github.com/amyreese"><code>@​amyreese</code></a></li>
<li><a
href="https://github.com/majiayu000"><code>@​majiayu000</code></a></li>
</ul>
<h2>0.15.11</h2>
<p>Released on 2026-04-16.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>ruff</code>] Ignore <code>RUF029</code> when function is
decorated with <code>asynccontextmanager</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24642">#24642</a>)</li>
<li>[<code>airflow</code>] Implement
<code>airflow-xcom-pull-in-template-string</code> (<code>AIR201</code>)
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/23583">#23583</a>)</li>
<li>[<code>flake8-bandit</code>] Fix <code>S103</code> false positives
and negatives in mask analysis (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24424">#24424</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/66f93cf7ed4d36325f35a452e4afa28268fbcd28"><code>66f93cf</code></a>
Bump 0.15.12 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24815">#24815</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/476a4d02e8e3b6c157ac39979d8b698a1b6baa91"><code>476a4d0</code></a>
[ty] Complete support for more detailed diagnostics on possibly unbound
error...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/ed669eab30095d6c51fe6cdef6050fb01276bcb3"><code>ed669ea</code></a>
Implement <code>#ruff:file-ignore</code> file-level suppressions (<a
href="https://redirect.github.com/astral-sh/ruff/issues/23599">#23599</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e73d952e43feb51356ee740c5a973fce81396ff6"><code>e73d952</code></a>
[ty] Include inferred type in <code>invalid-key</code> concise
diagnostic for union/inte...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/80feb29b31cd98c093316df2e0407b0c70c01b55"><code>80feb29</code></a>
[ty] report only dead annotation-only locals as unused (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24811">#24811</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0fbf2bc27336a3d17d39af52cf89b78dcda8c7c8"><code>0fbf2bc</code></a>
Drop deprecated license classifier (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24808">#24808</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/43b174cc7f2fcb0080bb1d4843cd4bf6b72bbe27"><code>43b174c</code></a>
[ty] Infer lambda parameter types with <code>Callable</code> type
context (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24317">#24317</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/4f449ae4a2377569330a5ab94799d389357b5a3f"><code>4f449ae</code></a>
[ty] Add error context for intersection types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24772">#24772</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/5b4e753acb46e96ad408e4904c15308e33efe307"><code>5b4e753</code></a>
[ty] Add support for goto in literal enum member inlay hint (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24792">#24792</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e7cc76275a758ce1c636ea1c2d091fd576aac794"><code>e7cc762</code></a>
[ty] Add error context for TypedDict assignments (<a
href="https://redirect.github.com/astral-sh/ruff/issues/24790">#24790</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.6...0.15.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `mypy` from 1.19.1 to 1.20.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's
changelog</a>.</em></p>
<blockquote>
<h3>Mypy 1.20.2</h3>
<ul>
<li>Use WAL with SQLite cache and fix close (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21154">21154</a>)</li>
<li>Adjust SQLite journal mode (Ivan Levkivskyi, PR <a
href="https://redirect.github.com/python/mypy/pull/21217">21217</a>)</li>
<li>Correctly aggregate narrowing information on parent expressions
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21206">21206</a>)</li>
<li>Fix regression related to generic callables (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21208">21208</a>)</li>
<li>Fix regression by avoiding widening types in some contexts
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21242">21242</a>)</li>
<li>Fix slicing in non-strict optional mode (Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21282">21282</a>)</li>
<li>mypyc: Fix match statement semantics for &quot;or&quot; pattern
(Shantanu, PR <a
href="https://redirect.github.com/python/mypy/pull/21156">21156</a>)</li>
<li>mypyc: Fix issue with module dunder attributes (Piotr Sawicki, PR <a
href="https://redirect.github.com/python/mypy/pull/21275">21275</a>)</li>
<li>Initial support for Python 3.15.0a8 (Marc Mueller, PR <a
href="https://redirect.github.com/python/mypy/pull/21255">21255</a>)</li>
</ul>
<h3>Acknowledgements</h3>
<p>Thanks to all mypy contributors who contributed to this release:</p>
<ul>
<li>A5rocks</li>
<li>Aaron Wieczorek</li>
<li>Adam Turner</li>
<li>Ali Hamdan</li>
<li>asce</li>
<li>BobTheBuidler</li>
<li>Brent Westbrook</li>
<li>Brian Schubert</li>
<li>bzoracler</li>
<li>Chris Burroughs</li>
<li>Christoph Tyralla</li>
<li>Colin Watson</li>
<li>Donghoon Nam</li>
<li>E. M. Bray</li>
<li>Emma Smith</li>
<li>Ethan Sarp</li>
<li>George Ogden</li>
<li>getzze</li>
<li>grayjk</li>
<li>Gregor Riepl</li>
<li>Ivan Levkivskyi</li>
<li>James Hilliard</li>
<li>James Le Cuirot</li>
<li>Jeremy Nimmer</li>
<li>Joren Hammudoglu</li>
<li>Kai (Kazuya Ito)</li>
<li>kaushal trivedi</li>
<li>Kevin Kannammalil</li>
<li>Lukas Geiger</li>
<li>Łukasz Langa</li>
<li>Marc Mueller</li>
<li>Michael R. Crusoe</li>
<li>michaelm-openai</li>
<li>Neil Schemenauer</li>
<li>Piotr Sawicki</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/python/mypy/commit/145a062651b5f9996b75ef32b7040bd2e885ed82"><code>145a062</code></a>
Bump version to 1.20.2</li>
<li><a
href="https://github.com/python/mypy/commit/81cd49215c288eacb987de066f02daff2553b7c7"><code>81cd492</code></a>
Fix slicing with nonstrict optional (<a
href="https://redirect.github.com/python/mypy/issues/21282">#21282</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/908d3441eecbaa2a6193165317177db834d7ca1a"><code>908d344</code></a>
[mypyc] Set dunder attrs when adding module to sys.modules (<a
href="https://redirect.github.com/python/mypy/issues/21275">#21275</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/ba28610fac9d2b33be210ca8dcfe4bc47b7af424"><code>ba28610</code></a>
Initial support for Python 3.15.0a8 (<a
href="https://redirect.github.com/python/mypy/issues/21255">#21255</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/7b0e09f48dbd3717ed008a273cd17e8e960c2037"><code>7b0e09f</code></a>
Fix match statement semantics for &quot;or&quot; pattern (<a
href="https://redirect.github.com/python/mypy/issues/21156">#21156</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/92b74f226de62f7505f5ef5cb158e8ec9c58b8b7"><code>92b74f2</code></a>
Avoid widening types in conditional_types (<a
href="https://redirect.github.com/python/mypy/issues/21242">#21242</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/0dcbfaa40b0e360a16baea9cf851955375d91b54"><code>0dcbfaa</code></a>
Fix is_overlapping_types for generic callables (<a
href="https://redirect.github.com/python/mypy/issues/21208">#21208</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/210f518dede35292033ef0d387847406a0ccef8f"><code>210f518</code></a>
Correctly aggregate narrowing information on parent expressions (<a
href="https://redirect.github.com/python/mypy/issues/21206">#21206</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/c34530e53a10e385d8b0f1af4baa88a596b5ceaa"><code>c34530e</code></a>
Only set journal mode in coordinator (<a
href="https://redirect.github.com/python/mypy/issues/21217">#21217</a>)</li>
<li><a
href="https://github.com/python/mypy/commit/79a3ec6d01b56a27c00e9b3320c2b1d4d73a77f9"><code>79a3ec6</code></a>
Use WAL with SQLite cache, fix close (<a
href="https://redirect.github.com/python/mypy/issues/21154">#21154</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python/mypy/compare/v1.19.1...v1.20.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `ty` from 0.0.23 to 0.0.32
<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.32</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-20.</p>
<h3>Bug fixes</h3>
<ul>
<li>Fix panic when <code>__get__</code> uses <code>Concatenate</code>
<code>self</code>-type and wraps a <code>__call__</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24692">#24692</a>)</li>
<li>Avoid panicking on overloaded <code>Callable</code> type context (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24661">#24661</a>)</li>
<li>Expand class bases in per-base lint checks (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24695">#24695</a>,
<a
href="https://redirect.github.com/astral-sh/ruff/pull/24699">#24699</a>)</li>
<li>Fix stack overflow for binary operator inference involving recursive
types (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24551">#24551</a>)</li>
</ul>
<h3>LSP server</h3>
<ul>
<li>Dim out unreachable code in IDEs (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24580">#24580</a>)</li>
<li>Do not suggest argument completion when typing the value of a
keyword argument (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24669">#24669</a>)</li>
<li>Retrieve the docstring from the overload implementation if an
<code>@overload</code>-decorated function has no docstring (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23920">#23920</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Allow <code>if</code> statements in TypedDict bodies (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24702">#24702</a>)</li>
<li>Disallow <code>@disjoint_base</code> on TypedDicts and Protocols (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24671">#24671</a>)</li>
<li>Do not consider a subclass of a
<code>@dataclass_transform</code>-decorated class to have dataclass-like
semantics if it has <code>type</code> in its MRO (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24679">#24679</a>)</li>
<li>Reject using properties with <code>Never</code> setters or deleters
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24510">#24510</a>)</li>
<li>Sync vendored typeshed stubs (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24646">#24646</a>).
<a
href="https://github.com/python/typeshed/compare/f8f0794d0fe249c06dc9f31a004d85be6cca6ced...c03c2b926422c82ab680d27f3ad2491845000802">Typeshed
diff</a></li>
</ul>
<h3>Diagnostics</h3>
<ul>
<li>Show error context for assignability diagnostics (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24309">#24309</a>)</li>
<li>Use partially qualified names when reporting diagnostics regarding
bad calls to methods (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24560">#24560</a>)</li>
<li>Reduce source code context window to zero (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24689">#24689</a>)</li>
<li>Merge same-file annotations if there is only a single line
separating them (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24694">#24694</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Memoize binary operator return types (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24700">#24700</a>)</li>
<li>Gate protocol compatibility on member count (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24684">#24684</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/JelleZijlstra"><code>@​JelleZijlstra</code></a></li>
<li><a href="https://github.com/kc0506"><code>@​kc0506</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a href="https://github.com/carljm"><code>@​carljm</code></a></li>
<li><a
href="https://github.com/dcreager"><code>@​dcreager</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a href="https://github.com/sharkdp"><code>@​sharkdp</code></a></li>
<li><a
href="https://github.com/ibraheemdev"><code>@​ibraheemdev</code></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.32</h2>
<p>Released on 2026-04-20.</p>
<h3>Bug fixes</h3>
<ul>
<li>Fix panic when <code>__get__</code> uses <code>Concatenate</code>
<code>self</code>-type and wraps a <code>__call__</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24692">#24692</a>)</li>
<li>Avoid panicking on overloaded <code>Callable</code> type context (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24661">#24661</a>)</li>
<li>Expand class bases in per-base lint checks (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24695">#24695</a>,
<a
href="https://redirect.github.com/astral-sh/ruff/pull/24699">#24699</a>)</li>
<li>Fix stack overflow for binary operator inference involving recursive
types (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24551">#24551</a>)</li>
</ul>
<h3>LSP server</h3>
<ul>
<li>Dim out unreachable code in IDEs (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24580">#24580</a>)</li>
<li>Do not suggest argument completion when typing the value of a
keyword argument (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24669">#24669</a>)</li>
<li>Retrieve the docstring from the overload implementation if an
<code>@overload</code>-decorated function has no docstring (<a
href="https://redirect.github.com/astral-sh/ruff/pull/23920">#23920</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Allow <code>if</code> statements in TypedDict bodies (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24702">#24702</a>)</li>
<li>Disallow <code>@disjoint_base</code> on TypedDicts and Protocols (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24671">#24671</a>)</li>
<li>Do not consider a subclass of a
<code>@dataclass_transform</code>-decorated class to have dataclass-like
semantics if it has <code>type</code> in its MRO (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24679">#24679</a>)</li>
<li>Reject using properties with <code>Never</code> setters or deleters
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/24510">#24510</a>)</li>
<li>Sync vendored typeshed stubs (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24646">#24646</a>).
<a
href="https://github.com/python/typeshed/compare/f8f0794d0fe249c06dc9f31a004d85be6cca6ced...c03c2b926422c82ab680d27f3ad2491845000802">Typeshed
diff</a></li>
</ul>
<h3>Diagnostics</h3>
<ul>
<li>Show error context for assignability diagnostics (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24309">#24309</a>)</li>
<li>Use partially qualified names when reporting diagnostics regarding
bad calls to methods (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24560">#24560</a>)</li>
<li>Reduce source code context window to zero (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24689">#24689</a>)</li>
<li>Merge same-file annotations if there is only a single line
separating them (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24694">#24694</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Memoize binary operator return types (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24700">#24700</a>)</li>
<li>Gate protocol compatibility on member count (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24684">#24684</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/JelleZijlstra"><code>@​JelleZijlstra</code></a></li>
<li><a href="https://github.com/kc0506"><code>@​kc0506</code></a></li>
<li><a
href="https://github.com/denyszhak"><code>@​denyszhak</code></a></li>
<li><a href="https://github.com/carljm"><code>@​carljm</code></a></li>
<li><a
href="https://github.com/dcreager"><code>@​dcreager</code></a></li>
<li><a
href="https://github.com/AlexWaygood"><code>@​AlexWaygood</code></a></li>
<li><a href="https://github.com/dylwil3"><code>@​dylwil3</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a href="https://github.com/sharkdp"><code>@​sharkdp</code></a></li>
<li><a
href="https://github.com/ibraheemdev"><code>@​ibraheemdev</code></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/4d1e1fc57ca8bfdcbcee513ba92135d2932eb279"><code>4d1e1fc</code></a>
Bump version to 0.0.32 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3302">#3302</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/a537bdefc97080d4400113eb6635ae133c5b3bcf"><code>a537bde</code></a>
Update PyO3/maturin-action action to v1.51.0 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3300">#3300</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/81e41250e0b53893f1f86eae3bc59867f15720ac"><code>81e4125</code></a>
Update actions/upload-artifact action to v7.0.1 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3296">#3296</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/a9dd1cbb536838e21b36b2c459b5bf6098c295cf"><code>a9dd1cb</code></a>
Update docker/build-push-action action to v7.1.0 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3299">#3299</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/ef0a7dd496dbf60000d79ac92ff171191cae424b"><code>ef0a7dd</code></a>
Update actions/github-script action to v9 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3301">#3301</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/74f058324890813da652964cdf2d29c1207fdeab"><code>74f0583</code></a>
Update astral-sh/setup-uv action to v8.1.0 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3298">#3298</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/82799cc0d01beed42e06c110bdaabb9ab243fce2"><code>82799cc</code></a>
Update prek dependencies (<a
href="https://redirect.github.com/astral-sh/ty/issues/3297">#3297</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/daaa40454966558ffd2a0ea3dcf86bc1749c8715"><code>daaa404</code></a>
Bump version to 0.0.31 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3280">#3280</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/12e86b58b5034a29266268195afc16feeab6e3ea"><code>12e86b5</code></a>
Bump version to 0.0.30 (<a
href="https://redirect.github.com/astral-sh/ty/issues/3270">#3270</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/67077ad5fbde5adc1df0e3169525040ad1139dab"><code>67077ad</code></a>
Reorder sections in FAQ (<a
href="https://redirect.github.com/astral-sh/ty/issues/3267">#3267</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ty/compare/0.0.23...0.0.32">compare
view</a></li>
</ul>
</details>
<br />

Updates `pydantic` from 2.12.5 to 2.13.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pydantic/pydantic/releases">pydantic's
releases</a>.</em></p>
<blockquote>
<h2>v2.13.3 2026-04-20</h2>
<h2>v2.13.3 (2026-04-20)</h2>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Handle <code>AttributeError</code> subclasses with
<code>from_attributes</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13096">#13096</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pydantic/pydantic/compare/v2.13.2...v2.13.3">https://github.com/pydantic/pydantic/compare/v2.13.2...v2.13.3</a></p>
<h2>v2.13.2 2026-04-17</h2>
<h2>v2.13.2 (2026-04-17)</h2>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.field_name</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13084">#13084</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pydantic/pydantic/compare/v2.13.1...v2.13.2">https://github.com/pydantic/pydantic/compare/v2.13.1...v2.13.2</a></p>
<h2>v2.13.1 2026-04-15</h2>
<h2>v2.13.1 (2026-04-15)</h2>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.data</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/davidhewitt"><code>@​davidhewitt</code></a> in
<a
href="https://redirect.github.com/pydantic/pydantic/pull/13079">#13079</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pydantic/pydantic/compare/v2.13.0...v2.13.1">https://github.com/pydantic/pydantic/compare/v2.13.0...v2.13.1</a></p>
<h2>v2.13.0 2026-04-13</h2>
<h2>v2.13.0 (2026-04-13)</h2>
<p>The highlights of the v2.13 release are available in the <a
href="https://pydantic.dev/articles/pydantic-v2-13-release">blog
post</a>.
Several minor changes (considered non-breaking changes according to our
<a
href="https://pydantic.dev/docs/validation/2.13/get-started/version-policy/#pydantic-v2">versioning
policy</a>) are also included in this release. Make sure to look into
them before upgrading.</p>
<p>This release contains the updated <code>pydantic.v1</code> namespace,
matching version 1.10.26 which includes support for Python 3.14.</p>
<h3>What's Changed</h3>
<p>See the beta releases for all changes sinces 2.12.</p>
<h4>Packaging</h4>
<ul>
<li>Add zizmor for GitHub Actions workflow linting by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13039">#13039</a></li>
<li>Update jiter to v0.14.0 to fix a segmentation fault on musl Linux by
<a href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13064">#13064</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pydantic/pydantic/blob/main/HISTORY.md">pydantic's
changelog</a>.</em></p>
<blockquote>
<h2>v2.13.3 (2026-04-20)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.3">GitHub
release</a></p>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Handle <code>AttributeError</code> subclasses with
<code>from_attributes</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13096">#13096</a></li>
</ul>
<h2>v2.13.2 (2026-04-17)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.2">GitHub
release</a></p>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.field_name</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13084">#13084</a></li>
</ul>
<h2>v2.13.1 (2026-04-15)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.1">GitHub
release</a></p>
<h3>What's Changed</h3>
<h4>Fixes</h4>
<ul>
<li>Fix <code>ValidationInfo.data</code> missing with
<code>model_validate_json()</code> by <a
href="https://github.com/davidhewitt"><code>@​davidhewitt</code></a> in
<a
href="https://redirect.github.com/pydantic/pydantic/pull/13079">#13079</a></li>
</ul>
<h2>v2.13.0 (2026-04-13)</h2>
<p><a
href="https://github.com/pydantic/pydantic/releases/tag/v2.13.0">GitHub
release</a></p>
<p>The highlights of the v2.13 release are available in the <a
href="https://pydantic.dev/articles/pydantic-v2-13-release">blog
post</a>.
Several minor changes (considered non-breaking changes according to our
<a
href="https://pydantic.dev/docs/validation/2.13/get-started/version-policy/#pydantic-v2">versioning
policy</a>)
are also included in this release. Make sure to look into them before
upgrading.</p>
<p>This release contains the updated <code>pydantic.v1</code> namespace,
matching version 1.10.26 which includes support for Python 3.14.</p>
<h3>What's Changed</h3>
<p>See the beta releases for all changes sinces 2.12.</p>
<h4>New Features</h4>
<ul>
<li>Allow default factories of private attributes to take validated
model data by <a
href="https://github.com/Viicos"><code>@​Viicos</code></a> in <a
href="https://redirect.github.com/pydantic/pydantic/pull/13013">#13013</a></li>
</ul>
<h4>Changes</h4>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pydantic/pydantic/commit/9e9a11132c980c4ec84ff6d2df06d4c1a55ca8f3"><code>9e9a111</code></a>
Fix backported test</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/1ec8c6aa0cae1c78409c6f31af738a24e16c7124"><code>1ec8c6a</code></a>
Prepare release v2.13.3</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/fb4f2044436e9fa4ff9745479ce263016a99c4d6"><code>fb4f204</code></a>
Handle <code>AttributeError</code> subclasses with
<code>from_attributes</code></li>
<li><a
href="https://github.com/pydantic/pydantic/commit/ca3ddd1149bd3b14273922a29da174804496a055"><code>ca3ddd1</code></a>
Prepare release v2.13.2</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/000e823a3ab7545b0c6064fde34effdcd02044ab"><code>000e823</code></a>
Fix <code>ValidationInfo.field_name</code> missing with
<code>model_validate_json()</code></li>
<li><a
href="https://github.com/pydantic/pydantic/commit/d45d8be3ffb6dfc6d0c64da6980140c91ff841fb"><code>d45d8be</code></a>
Prepare release 2.13.1</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/54aca609ce58e73dfe1c681d93432fbe3a4621da"><code>54aca60</code></a>
Fix <code>ValidationInfo.data</code> missing with
<code>model_validate_json()</code></li>
<li><a
href="https://github.com/pydantic/pydantic/commit/46bf4fa648af3a1fbf4603a37f210e9d9c618357"><code>46bf4fa</code></a>
Fix Pydantic release workflow (<a
href="https://redirect.github.com/pydantic/pydantic/issues/13067">#13067</a>)</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/1b359edab09c623464d23c6fd2503ae5ff276d43"><code>1b359ed</code></a>
Prepare release v2.13.0 (<a
href="https://redirect.github.com/pydantic/pydantic/issues/13065">#13065</a>)</li>
<li><a
href="https://github.com/pydantic/pydantic/commit/b1bf19445d8ac144a7a0e82674d2d87eebab6c18"><code>b1bf194</code></a>
Fix model equality when using runtime <code>extra</code> configuration
(<a
href="https://redirect.github.com/pydantic/pydantic/issues/13062">#13062</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pydantic/pydantic/compare/v2.12.5...v2.13.3">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>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: jkennedyvz <65985482+jkennedyvz@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 01:20:26 -07:00
Sydney RunkleandGitHub cb328b57f1 release(prebuilt): 1.0.12, langgraph 1.1.10 (#7623)
## Summary

- Bumps `langgraph-prebuilt` `1.0.11` → `1.0.12`
- Bumps `langgraph` `1.1.9` → `1.1.10` (requires
`langgraph-prebuilt>=1.0.12`)
- Updates all downstream `uv.lock` files

## Changes since last release

**prebuilt (`1.0.11` → `1.0.12`)**
- fix(prebuilt): hydrate ToolNode state from channels via pregel helpers
(#7594)

**langgraph (`1.1.8` → `1.1.10`)**
- fix: don't propagate ReplayState to subgraphs on plain resume (#7561)
2026-04-27 13:11:43 -04:00
William FHandGitHub d177a0db43 Revert "chore: node-level timeouts" (#7627)
Reverts langchain-ai/langgraph#7599

I am going to implement this as an `idle_timeout` instead. I think
that's a better default behavior.
2026-04-27 09:03:19 -07:00
Sydney RunkleandGitHub 372d54dc4f release(checkpoint): 4.0.3 (#7625)
## Summary

Bumps `langgraph-checkpoint` `4.0.2` → `4.0.3` and updates all
downstream `uv.lock` files.

## Changes since 4.0.2

- fix(checkpoint): revive lc=2 JSON blobs for safe types without
allowlist (#7582)
- chore: dedup warnings (#7257)
- chore(deps): bump langsmith from 0.6.4 to 0.7.31 (#7525)
2026-04-27 10:27:56 -04:00
f4aee546ad fix(prebuilt): hydrate ToolNode state from channels via pregel helpers (#7594)
## Summary

When `ToolNode` receives a bare `[tool_call]` list via the Send API (the
dispatch shape `create_agent` will use once langchain-ai/langchain#36960
lands), hydrate `ToolRuntime.state` from the current channel values
instead of requiring the dispatcher to inline the full agent state dict
into every `Send.arg`.

Motivation: the paired langchain PR drops the `ToolCallWithContext`
wrapper from `create_agent`'s tool dispatch, which eliminates an O(N²)
storage term on `__pregel_tasks` checkpoint writes. Without this
companion change there would be no path for the tool node to see the
graph state.

## What changed

- `libs/prebuilt/langgraph/prebuilt/tool_node.py` — `_extract_state`
grows a third branch for list-form input. When the input is a list whose
last entry is a `ToolCall` dict, read the current channel values via
`CONFIG_KEY_READ` and return them as the state dict.

The full new logic is four lines inline in `_extract_state`:

```python
read = config.get(CONF, {}).get(CONFIG_KEY_READ)
if read is None:
    return {}
# Pregel installs CONFIG_KEY_READ as
# `functools.partial(local_read, scratchpad, channels, managed, task)`.
channels = read.args[1]
return cast("dict[str, Any]", read(list(channels), False))
```

- No changes to the pregel read machinery (`local_read`, `ChannelRead`).
- Only channel values are read; managed values have their own injection
path (`ToolRuntime.context`, `InjectedContext`) and were never in the
pre-fix inlined state dict, so we don't add them here.
- Falls back to `{}` when invoked outside a Pregel context (e.g. direct
`ToolNode(...).invoke([tool_call])` from a test harness), which
preserves existing `ToolNode` direct-invocation test behavior.

- `libs/prebuilt/tests/test_on_tool_call.py` — two new tests covering
the list-form hydration path (sync + async). They build a
`functools.partial` that matches Pregel's real `CONFIG_KEY_READ` shape
and assert `ToolRuntime.state` reflects the current channel values.

## Why it's safe

- **Same snapshot semantics as before.** `Send` is emitted at
end-of-super-step-N; consumed at start-of-super-step-N+1. Channels at
that point reflect every write from super-step N (including the new
AIMessage the tool calls originated from). Parallel tool tasks in the
tools super-step all read the same values since sibling writes don't
land until end-of-super-step.
- **Legacy `ToolCallWithContext` path preserved.** External dispatchers
that still inline state continue to work unchanged — `_extract_state`
checks that branch first.

## Test plan

- [x] `make test` in `libs/prebuilt` — **204 pass**
- [x] Two new hydration tests (sync + async) green
- [x] `make format` / `make lint` / `mypy` clean

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 09:52:23 -04:00
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>
2026-04-27 09:08:47 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
53a9806e65 chore(deps): bump nbconvert from 7.17.0 to 7.17.1 in /libs/langgraph (#7573)
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.17.0 to
7.17.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jupyter/nbconvert/releases">nbconvert's
releases</a>.</em></p>
<blockquote>
<h2>v7.17.1</h2>
<h2>7.17.1</h2>
<p>This is a security release, fixing two CVEs:</p>
<ul>
<li><a
href="https://github.com/jupyter/nbconvert/security/advisories/GHSA-4c99-qj7h-p3vg">CVE-2026-39377</a></li>
<li><a
href="https://github.com/jupyter/nbconvert/security/advisories/GHSA-7jqv-fw35-gmx9">CVE-2026-39378</a></li>
</ul>
<p>(full advisories will be published seven days after release, on
2026-04-14).</p>
<p>(<a
href="https://github.com/jupyter/nbconvert/compare/v7.17.0...b3b6ec01f872e9af8fd1769eb9cf1889c720ecf3">Full
Changelog</a>)</p>
<h3>Enhancements made</h3>
<ul>
<li>Allow configureable WebPDF JavaScript processing timeout <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2250">#2250</a>
(<a href="https://github.com/timkpaine"><code>@​timkpaine</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
</ul>
<h3>Bugs fixed</h3>
<ul>
<li>Fix <code>PermissionError</code> when checking template paths on
shared filesystems <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2252">#2252</a>
(<a href="https://github.com/ctcjab"><code>@​ctcjab</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>Tweak webpdf template logic to fix duplicate extension problem <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2249">#2249</a>
(<a href="https://github.com/timkpaine"><code>@​timkpaine</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
</ul>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>specify python version for pre <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2276">#2276</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
</ul>
<h3>Contributors to this release</h3>
<p>The following people contributed discussions, new ideas, code and
documentation contributions, and review.
See <a
href="https://github-activity.readthedocs.io/en/latest/use/#how-does-this-tool-define-contributions-in-the-reports">our
definition of contributors</a>.</p>
<p>(<a
href="https://github.com/jupyter/nbconvert/graphs/contributors?from=2026-01-29&amp;to=2026-04-08&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/akhmerov"><code>@​akhmerov</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Aakhmerov+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/bollwyvl"><code>@​bollwyvl</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Abollwyvl+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/Carreau"><code>@​Carreau</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3ACarreau+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/ctcjab"><code>@​ctcjab</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Actcjab+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a
href="https://github.com/davidbrochart"><code>@​davidbrochart</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Adavidbrochart+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/Ken-B"><code>@​Ken-B</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AKen-B+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/krassowski"><code>@​krassowski</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Akrassowski+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/mgeier"><code>@​mgeier</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Amgeier+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/minrk"><code>@​minrk</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Aminrk+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/mpacer"><code>@​mpacer</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Ampacer+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/MSeal"><code>@​MSeal</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AMSeal+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a
href="https://github.com/SylvainCorlay"><code>@​SylvainCorlay</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3ASylvainCorlay+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/takluyver"><code>@​takluyver</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Atakluyver+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/timkpaine"><code>@​timkpaine</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Atimkpaine+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md">nbconvert's
changelog</a>.</em></p>
<blockquote>
<h2>7.17.1</h2>
<p>This is a security release, fixing two CVEs:</p>
<ul>
<li><a
href="https://github.com/jupyter/nbconvert/security/advisories/GHSA-4c99-qj7h-p3vg">CVE-2026-39377</a></li>
<li><a
href="https://github.com/jupyter/nbconvert/security/advisories/GHSA-7jqv-fw35-gmx9">CVE-2026-39378</a></li>
</ul>
<p>(full advisories will be published seven days after release, on
2026-04-14).</p>
<p>(<a
href="https://github.com/jupyter/nbconvert/compare/v7.17.0...b3b6ec01f872e9af8fd1769eb9cf1889c720ecf3">Full
Changelog</a>)</p>
<h3>Enhancements made</h3>
<ul>
<li>Allow configureable WebPDF JavaScript processing timeout <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2250">#2250</a>
(<a href="https://github.com/timkpaine"><code>@​timkpaine</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
</ul>
<h3>Bugs fixed</h3>
<ul>
<li>Fix <code>PermissionError</code> when checking template paths on
shared filesystems <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2252">#2252</a>
(<a href="https://github.com/ctcjab"><code>@​ctcjab</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
<li>Tweak webpdf template logic to fix duplicate extension problem <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2249">#2249</a>
(<a href="https://github.com/timkpaine"><code>@​timkpaine</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>)</li>
</ul>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>specify python version for pre <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2276">#2276</a>
(<a href="https://github.com/minrk"><code>@​minrk</code></a>, <a
href="https://github.com/krassowski"><code>@​krassowski</code></a>)</li>
</ul>
<h3>Contributors to this release</h3>
<p>The following people contributed discussions, new ideas, code and
documentation contributions, and review.
See <a
href="https://github-activity.readthedocs.io/en/latest/use/#how-does-this-tool-define-contributions-in-the-reports">our
definition of contributors</a>.</p>
<p>(<a
href="https://github.com/jupyter/nbconvert/graphs/contributors?from=2026-01-29&amp;to=2026-04-08&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/akhmerov"><code>@​akhmerov</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Aakhmerov+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/bollwyvl"><code>@​bollwyvl</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Abollwyvl+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/Carreau"><code>@​Carreau</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3ACarreau+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/ctcjab"><code>@​ctcjab</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Actcjab+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a
href="https://github.com/davidbrochart"><code>@​davidbrochart</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Adavidbrochart+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/Ken-B"><code>@​Ken-B</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AKen-B+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/krassowski"><code>@​krassowski</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Akrassowski+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/mgeier"><code>@​mgeier</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Amgeier+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/minrk"><code>@​minrk</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Aminrk+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/mpacer"><code>@​mpacer</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Ampacer+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/MSeal"><code>@​MSeal</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AMSeal+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a
href="https://github.com/SylvainCorlay"><code>@​SylvainCorlay</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3ASylvainCorlay+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/takluyver"><code>@​takluyver</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Atakluyver+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)
| <a href="https://github.com/timkpaine"><code>@​timkpaine</code></a>
(<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Atimkpaine+updated%3A2026-01-29..2026-04-08&amp;type=Issues">activity</a>)</p>
<!-- raw HTML omitted -->
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/jupyter/nbconvert/commit/78ed30837a607deab7cf0a12dca072bf3f63417a"><code>78ed308</code></a>
Publish 7.17.1</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/f090a64606fde4c7f87cbf478f51b4aa46a425ec"><code>f090a64</code></a>
ruff format</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/b3b6ec01f872e9af8fd1769eb9cf1889c720ecf3"><code>b3b6ec0</code></a>
chore: update pre-commit hooks (<a
href="https://redirect.github.com/jupyter/nbconvert/issues/2277">#2277</a>)</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/be4841f7da51c499b1937e41e6e71926dbe0daa3"><code>be4841f</code></a>
ignore silly security lint in tests</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/26d57b295870f5572d9bd0535acee4a120339c10"><code>26d57b2</code></a>
fix type annotation on Lexer</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/0e6b8ccabf2aca6c18fac8c574f22b7155f441fb"><code>0e6b8cc</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/ba5e5cdd737704388251fa55fa9e58f5752fa39d"><code>ba5e5cd</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/1db0c88d865146ce02b6405a8d96753d3d0cd0c2"><code>1db0c88</code></a>
Specify python version for pre (<a
href="https://redirect.github.com/jupyter/nbconvert/issues/2276">#2276</a>)</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/7473fc3037a6317bff54380e3a7162d73bf089b3"><code>7473fc3</code></a>
chore: update pre-commit hooks (<a
href="https://redirect.github.com/jupyter/nbconvert/issues/2242">#2242</a>)</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/4322f7f290694929f414cefadc942111afad3762"><code>4322f7f</code></a>
Bump the actions group across 1 directory with 2 updates (<a
href="https://redirect.github.com/jupyter/nbconvert/issues/2273">#2273</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/jupyter/nbconvert/compare/v7.17.0...v7.17.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nbconvert&package-manager=uv&previous-version=7.17.0&new-version=7.17.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 20:30:47 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
219fbbe8d0 chore(deps): bump python-dotenv from 1.2.1 to 1.2.2 in /libs/langgraph (#7574)
Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from
1.2.1 to 1.2.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/theskumar/python-dotenv/releases">python-dotenv's
releases</a>.</em></p>
<blockquote>
<h2>v1.2.2</h2>
<h3>Added</h3>
<ul>
<li>Support for Python 3.14, including the free-threaded (3.14t) build.
(#)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>The <code>dotenv run</code> command now forwards flags directly to
the specified command by <a
href="https://github.com/bbc2"><code>@​bbc2</code></a> in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/607">theskumar/python-dotenv#607</a></li>
<li>Improved documentation clarity regarding override behavior and the
reference page.</li>
<li>Updated PyPy support to version 3.11.</li>
<li>Documentation for FIFO file support.</li>
<li>Support for Python 3.9.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Improved <code>set_key</code> and <code>unset_key</code> behavior
when interacting with symlinks by <a
href="https://github.com/bbc2"><code>@​bbc2</code></a> in <a
href="https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311">#790c5</a></li>
<li>Corrected the license specifier and added missing Python 3.14
classifiers in package metadata by <a
href="https://github.com/JYOuyang"><code>@​JYOuyang</code></a> in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/590">theskumar/python-dotenv#590</a></li>
</ul>
<h3>Breaking Changes</h3>
<ul>
<li>
<p><code>dotenv.set_key</code> and <code>dotenv.unset_key</code> used to
follow symlinks in some
situations. This is no longer the case. For that behavior to be restored
in
all cases, <code>follow_symlinks=True</code> should be used.</p>
</li>
<li>
<p>In the CLI, <code>set</code> and <code>unset</code> used to follow
symlinks in some situations. This
is no longer the case.</p>
</li>
<li>
<p><code>dotenv.set_key</code>, <code>dotenv.unset_key</code> and the
CLI commands <code>set</code> and <code>unset</code>
used to reset the file mode of the modified .env file to
<code>0o600</code> in some
situations. This is no longer the case: The original mode of the file is
now
preserved. Is the file needed to be created or wasn't a regular file,
mode
<code>0o600</code> is used.</p>
</li>
</ul>
<h3>Misc</h3>
<ul>
<li>skip 000 permission tests for root user by <a
href="https://github.com/burnout-projects"><code>@​burnout-projects</code></a>
in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/561">theskumar/python-dotenv#561</a></li>
<li>Bump actions/checkout from 5 to 6 in the github-actions group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/593">theskumar/python-dotenv#593</a></li>
<li>Add Windows testing to CI by <a
href="https://github.com/bbc2"><code>@​bbc2</code></a> in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/604">theskumar/python-dotenv#604</a></li>
<li>Improve workflow efficiency with best practices by <a
href="https://github.com/theskumar"><code>@​theskumar</code></a> in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/609">theskumar/python-dotenv#609</a></li>
<li>Remove the use of <code>sh</code> in tests by <a
href="https://github.com/bbc2"><code>@​bbc2</code></a> in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/612">theskumar/python-dotenv#612</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/JYOuyang"><code>@​JYOuyang</code></a>
made their first contribution in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/590">theskumar/python-dotenv#590</a></li>
<li><a
href="https://github.com/burnout-projects"><code>@​burnout-projects</code></a>
made their first contribution in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/561">theskumar/python-dotenv#561</a></li>
<li><a
href="https://github.com/cpackham-atlnz"><code>@​cpackham-atlnz</code></a>
made their first contribution in <a
href="https://redirect.github.com/theskumar/python-dotenv/pull/597">theskumar/python-dotenv#597</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/theskumar/python-dotenv/compare/v1.2.1...v1.2.2">https://github.com/theskumar/python-dotenv/compare/v1.2.1...v1.2.2</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md">python-dotenv's
changelog</a>.</em></p>
<blockquote>
<h2>[1.2.2] - 2026-03-01</h2>
<h3>Added</h3>
<ul>
<li>Support for Python 3.14, including the free-threaded (3.14t) build.
(<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/588">#588</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>The <code>dotenv run</code> command now forwards flags directly to
the specified command by [<a
href="https://github.com/bbc2"><code>@​bbc2</code></a>] in <a
href="https://redirect.github.com/theskumar/python-dotenv/issues/607">#607</a></li>
<li>Improved documentation clarity regarding override behavior and the
reference page.</li>
<li>Updated PyPy support to version 3.11.</li>
<li>Documentation for FIFO file support.</li>
<li>Dropped Support for Python 3.9.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Improved <code>set_key</code> and <code>unset_key</code> behavior
when interacting with symlinks by [<a
href="https://github.com/bbc2"><code>@​bbc2</code></a>] in
[790c5c0]</li>
<li>Corrected the license specifier and added missing Python 3.14
classifiers in package metadata by [<a
href="https://github.com/JYOuyang"><code>@​JYOuyang</code></a>] in <a
href="https://redirect.github.com/theskumar/python-dotenv/issues/590">#590</a></li>
</ul>
<h3>Breaking Changes</h3>
<ul>
<li>
<p><code>dotenv.set_key</code> and <code>dotenv.unset_key</code> used to
follow symlinks in some
situations. This is no longer the case. For that behavior to be restored
in
all cases, <code>follow_symlinks=True</code> should be used.</p>
</li>
<li>
<p>In the CLI, <code>set</code> and <code>unset</code> used to follow
symlinks in some situations. This
is no longer the case.</p>
</li>
<li>
<p><code>dotenv.set_key</code>, <code>dotenv.unset_key</code> and the
CLI commands <code>set</code> and <code>unset</code>
used to reset the file mode of the modified .env file to
<code>0o600</code> in some
situations. This is no longer the case: The original mode of the file is
now
preserved. Is the file needed to be created or wasn't a regular file,
mode
<code>0o600</code> is used.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/36004e0e34be7665ff2b11a8a4005144f76f176d"><code>36004e0</code></a>
Bump version: 1.2.1 → 1.2.2</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/eb202520e5933c9daf42501e1e42fdb0144002c8"><code>eb20252</code></a>
docs: update changelog for v1.2.2</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311"><code>790c5c0</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/43340da220fb4ca4f95357bbe21a3c7f8f1278b1"><code>43340da</code></a>
Remove the use of <code>sh</code> in tests (<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/612">#612</a>)</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/09d7cee32459e7abdcb5c9d8122a552589c06a9c"><code>09d7cee</code></a>
docs: clarify override behavior and document FIFO support (<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/610">#610</a>)</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/c8de2887c00198c22842c5ae5e92d1747467363c"><code>c8de288</code></a>
ci: improve workflow efficiency with best practices (<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/609">#609</a>)</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/7bd9e3dbfedc0983ad7d56d5570013035242bdf4"><code>7bd9e3d</code></a>
Add Windows testing to CI (<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/604">#604</a>)</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/1baaf04f336072e0ee324d5df9563ec767f14f81"><code>1baaf04</code></a>
Drop Python 3.9 support and update to PyPy 3.11 (<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/608">#608</a>)</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/4a22cf8993804aeede0c20b75bb1a29d3a99e9dc"><code>4a22cf8</code></a>
ci: enable testing on Python 3.14t (free-threaded) (<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/588">#588</a>)</li>
<li><a
href="https://github.com/theskumar/python-dotenv/commit/e2e8e776b42e382ae38b44d3982dd649e7507dd4"><code>e2e8e77</code></a>
Fix license specifier (<a
href="https://redirect.github.com/theskumar/python-dotenv/issues/597">#597</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/theskumar/python-dotenv/compare/v1.2.1...v1.2.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=python-dotenv&package-manager=uv&previous-version=1.2.1&new-version=1.2.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 20:30:29 -07:00
aeff9549c2 chore: node-level timeouts (#7599)
This PR implements task/node-level timeouts. 

Since python has a terrible multi-processing model, we make two
concessions:
- we only support for async functions/nodes. Sync nodes with a timeout
raise an error at compile time
- we implement this with asyncio wait_for in the async path.
 
Each timed attempt is wrapped in _retry.py, and the timer is reset on
each node-level retry. When the deadline is exceeded LangGraph raises
NodeTimeoutError, clears buffered writes, and prevents any late writes
or child-task scheduling from leaking past the timeout via
_TimedAttemptScope.

The design also adds a timed-attempt observer hook
(CONFIG_KEY_TIMED_ATTEMPT_OBSERVER) that emits start/finish events with
identifiers and deadlines. This means that if you have an orchestrating
process starting a worker process, it can listen to start/end events and
hard-kill the process to enforce a timeout if there is a deadlock.

---------

Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
2026-04-24 18:27:22 -07:00
ccurmeandGitHub 1a248cba45 release(prebuilt): 1.0.11 (#7610) 2026-04-24 14:16:17 -04:00
45246f6c74 feat(prebuilt): allow ToolNode tools to return list[Command | ToolMessage] (#7596)
## Summary

Extends `ToolNode` so that a single tool invocation can return
`list[Command | ToolMessage]` instead of only a single `Command` or
`ToolMessage`. This brings `ToolNode`'s per-tool-call contract in line
with the rest of LangGraph, where nodes can already return multiple
Commands.

Depends on langchain-ai/langchain#36963 which allows
`list[ToolOutputMixin]` to pass through `BaseTool._format_output`
unchanged.

## Changes

### `libs/prebuilt/langgraph/prebuilt/tool_node.py`

**New list-return gate in `_execute_tool_sync` / `_execute_tool_async`**
— After the existing `Command` and `ToolMessage` checks, a new branch
accepts `list[Command | ToolMessage]` and routes it through
`_validate_tool_command_list`. Lists with non-`Command`/`ToolMessage`
elements raise `TypeError`. Both sync and async paths are updated
symmetrically.

**`_validate_tool_command_list`** — Enforces the terminating-ToolMessage
rule: exactly one `ToolMessage` in the list must carry `tool_call_id ==
<outer_id>` (top-level or nested inside a `Command.update["messages"]`).
Zero or multiple terminators raise `_MissingToolMessageError`.
Individual Commands in the list are validated via the existing
`_validate_tool_command`; when a Command lacks the terminator (which is
allowed since the list-level check handles it), the
`_MissingToolMessageError` is caught and the already-normalized command
from the exception is used.

**`_MissingToolMessageError`** — A `ValueError` subclass raised by
`_validate_tool_command` (and `_validate_tool_command_list`) when no
matching `ToolMessage` is found. Carries the already-normalized command
so callers can recover without re-doing deepcopy/message-conversion
work. Using a typed exception avoids brittle string-matching on error
messages.

**`_combine_tool_outputs`** — Flattens list entries at the top of the
method so downstream combiner logic (parent-`goto` accumulation,
ToolMessage wrapping) is unchanged.

**Response processing moved inside try/except** — In both sync and async
execute methods, the response validation (Command/ToolMessage/list
checks) now runs inside the existing error-handling try block, so
validation errors from the list path go through `_handle_tool_errors`
like other tool errors.

**Return type signatures** widened on `_execute_tool_sync`,
`_execute_tool_async`, `_run_one`, `_arun_one` to include `list[Command
| ToolMessage]`.

### `libs/prebuilt/tests/test_tool_node.py`

New tests covering: valid list returns (top-level terminator, nested
terminator, parent-goto + terminator), regression tests for single
Command/ToolMessage returns, invalid cases (no terminator, multiple
terminators), async parity, integration with mixed list/non-list tool
calls, and `_handle_tool_errors` interaction.

---------

Co-authored-by: Sydney Runkle <sydneymarierunkle@gmail.com>
2026-04-23 13:37:45 -07:00
8657df80f3 chore: mixup cli formatting (#7585)
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
2026-04-22 11:29:23 -07:00
Sydney RunkleandGitHub a529b9bede chore(langgraph): bump version 1.1.8 -> 1.1.9 (#7563)
## Summary

- Bumps `langgraph` patch version from `1.1.8` to `1.1.9` in
`libs/langgraph/pyproject.toml`

## Test plan

- [ ] Verify version string is correct in `pyproject.toml`
- [ ] Confirm release workflow triggers on merge
2026-04-21 09:38:58 -04:00
0a26b471d3 fix(langgraph): don't propagate ReplayState to subgraphs on plain resume (#7561)
**Description:**
When clients resume an interrupted subgraph with `Command(resume=...)`
plus an explicit `checkpoint_id` in the config (the pattern LangGraph
Studio and the API server emit on every resume), the subgraph restarts
from its first node instead of continuing at the interrupted node.

Fix: gate `ReplayState` propagation on `is_time_traveling` rather than
`is_replaying`, so a resume that happens to carry a head checkpoint_id
behaves the same as one with just a thread_id.

**Verification:** added regression test
`test_subgraph_interrupt_resume_with_explicit_head_checkpoint_id` (fails
on main, passes with fix, across memory/sqlite/sqlite_aes).
Full `test_time_travel.py`, `test_time_travel_async.py`,
`test_interruption.py`, and all pregel subgraph/interrupt/resume/replay
tests still pass.

Co-authored-by: Jessie Ibarra <jessie.ibarra@langgraph.dev>
2026-04-20 21:29:18 -04:00
Eugene YurtsevandGitHub b674dd4622 feat(prebuilt): expose available tools on ToolRuntime (#7512) 2026-04-17 16:54:16 -04:00
Eugene YurtsevandGitHub 8df0a377d0 chore(langgraph): undo unnecessary changes in stream handler (#7536)
This change is no longer necessary as langchain-core will continue
copying checkpoint_ns for backwards compatibility
2026-04-17 16:22:34 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
216cf33a54 chore(deps): bump the pip group across 3 directories with 1 update (#7537)
Bumps the pip group with 1 update in the
/libs/cli/examples/graph_prerelease_reqs/deps/zuper_deps directory:
[langchain-openai](https://github.com/langchain-ai/langchain).
Bumps the pip group with 1 update in the
/libs/cli/examples/graph_prerelease_reqs_fail directory:
[langchain-openai](https://github.com/langchain-ai/langchain).
Bumps the pip group with 1 update in the
/libs/cli/examples/graph_prerelease_reqs directory:
[langchain-openai](https://github.com/langchain-ai/langchain).

Updates `langchain-openai` from 1.0.1 to 1.1.14
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchain/releases">langchain-openai's
releases</a>.</em></p>
<blockquote>
<h2>langchain-openai==1.1.14</h2>
<p>Changes since langchain-openai==1.1.13</p>
<p>release(openai): 1.1.14 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36820">#36820</a>)
fix(openai): use SSRF-safe transport for image token counting (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36819">#36819</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36795">#36795</a>)
chore: bump pillow from 12.1.1 to 12.2.0 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36777">#36777</a>)</p>
<h2>langchain-openai==1.1.13</h2>
<p>Changes since langchain-openai==1.1.12</p>
<p>release(openai): 1.1.13 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36729">#36729</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36539">#36539</a>)
chore(openai): fix broken vcr cassette playback and add ci guard (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36502">#36502</a>)
fix(openai,groq,openrouter): use is-not-None checks in usage metadata
token extraction (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36500">#36500</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36455">#36455</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36368">#36368</a>)
fix(openai): update computer call test (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36352">#36352</a>)
fix(openai): let user-provided User-Agent override the Azure default (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35523">#35523</a>)
chore: bump requests from 2.32.5 to 2.33.0 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36248">#36248</a>)</p>
<h2>langchain-openai==1.1.12</h2>
<p>Changes since langchain-openai==1.1.11</p>
<p>fix(openai): bump min core version (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36180">#36180</a>)
release(openai): 1.1.12 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36178">#36178</a>)
fix(core,model-profiles): add missing <code>ModelProfile</code> fields,
warn on schema drift (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36129">#36129</a>)
fix(openai): support phase parameter (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36161">#36161</a>)
fix(openai): preserve namespace field in streaming function_call chunks
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36108">#36108</a>)
ci: suppress pytest streaming output in CI (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36092">#36092</a>)
ci: avoid unnecessary dep installs in lint targets (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36046">#36046</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36039">#36039</a>)
chore: bump orjson from 3.11.5 to 3.11.6 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35860">#35860</a>)
fix(openai): add type: message to Responses API input items (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35693">#35693</a>)
perf(.github): set a timeout on get min versions HTTP calls (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35851">#35851</a>)
feat(model-profiles): new fields + <code>Makefile</code> target (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35788">#35788</a>)
fix(openai): close PIL Image handles in token counting to prevent fd
leak (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35742">#35742</a>)
fix(openai): typo (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35763">#35763</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35754">#35754</a>)</p>
<h2>langchain-openai==1.1.11</h2>
<p>Changes since langchain-openai==1.1.10</p>
<p>fix(openai): bump min core version (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35705">#35705</a>)
release(openai): 1.1.11 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35703">#35703</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/b7447c6969fc928ec3f29c200e2e56c0a46c4c77"><code>b7447c6</code></a>
fix(infra): skip serdes tests in min-version release step (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36818">#36818</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/41c0cc58b0dac82000d24715f7a4b44dc8b01fd3"><code>41c0cc5</code></a>
release(openai): 1.1.14 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36820">#36820</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/0516156ef98f5001129f6d47bc8682d6536d58fb"><code>0516156</code></a>
fix(openai): use SSRF-safe transport for image token counting (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36819">#36819</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/338aa8131a8124e7aa1e042616ccd2366ff9f699"><code>338aa81</code></a>
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/3">#3</a>...</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/51e954877efd2d2c3c5bf09364dcfec8794eadb0"><code>51e9548</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/text-splitters (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36797">#36797</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/e85c418cfa559d4a794ddc6db92c6febab44651c"><code>e85c418</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/model-profiles (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36798">#36798</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/789126e6c78ad74664bea26228dda6e72e135dce"><code>789126e</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/standard-tests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36799">#36799</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/937b3eb3827551d17ee4736f9acc4aa57e88c716"><code>937b3eb</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/langchain_v1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36800">#36800</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/a06c205738cf5953e28c37287ddb1559d67c01f6"><code>a06c205</code></a>
ci(infra): validate issue checkboxes by section (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36811">#36811</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/aa33b06deb0d65489ce254b48a8aaf8a86304c18"><code>aa33b06</code></a>
fix(langchain-classic): suppress mypy errors in compat code (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36806">#36806</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-openai==1.0.1...langchain-openai==1.1.14">compare
view</a></li>
</ul>
</details>
<br />

Updates `langchain-openai` from 1.0.0a2 to 1.1.14
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchain/releases">langchain-openai's
releases</a>.</em></p>
<blockquote>
<h2>langchain-openai==1.1.14</h2>
<p>Changes since langchain-openai==1.1.13</p>
<p>release(openai): 1.1.14 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36820">#36820</a>)
fix(openai): use SSRF-safe transport for image token counting (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36819">#36819</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36795">#36795</a>)
chore: bump pillow from 12.1.1 to 12.2.0 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36777">#36777</a>)</p>
<h2>langchain-openai==1.1.13</h2>
<p>Changes since langchain-openai==1.1.12</p>
<p>release(openai): 1.1.13 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36729">#36729</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36539">#36539</a>)
chore(openai): fix broken vcr cassette playback and add ci guard (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36502">#36502</a>)
fix(openai,groq,openrouter): use is-not-None checks in usage metadata
token extraction (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36500">#36500</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36455">#36455</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36368">#36368</a>)
fix(openai): update computer call test (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36352">#36352</a>)
fix(openai): let user-provided User-Agent override the Azure default (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35523">#35523</a>)
chore: bump requests from 2.32.5 to 2.33.0 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36248">#36248</a>)</p>
<h2>langchain-openai==1.1.12</h2>
<p>Changes since langchain-openai==1.1.11</p>
<p>fix(openai): bump min core version (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36180">#36180</a>)
release(openai): 1.1.12 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36178">#36178</a>)
fix(core,model-profiles): add missing <code>ModelProfile</code> fields,
warn on schema drift (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36129">#36129</a>)
fix(openai): support phase parameter (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36161">#36161</a>)
fix(openai): preserve namespace field in streaming function_call chunks
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36108">#36108</a>)
ci: suppress pytest streaming output in CI (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36092">#36092</a>)
ci: avoid unnecessary dep installs in lint targets (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36046">#36046</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36039">#36039</a>)
chore: bump orjson from 3.11.5 to 3.11.6 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35860">#35860</a>)
fix(openai): add type: message to Responses API input items (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35693">#35693</a>)
perf(.github): set a timeout on get min versions HTTP calls (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35851">#35851</a>)
feat(model-profiles): new fields + <code>Makefile</code> target (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35788">#35788</a>)
fix(openai): close PIL Image handles in token counting to prevent fd
leak (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35742">#35742</a>)
fix(openai): typo (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35763">#35763</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35754">#35754</a>)</p>
<h2>langchain-openai==1.1.11</h2>
<p>Changes since langchain-openai==1.1.10</p>
<p>fix(openai): bump min core version (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35705">#35705</a>)
release(openai): 1.1.11 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35703">#35703</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/b7447c6969fc928ec3f29c200e2e56c0a46c4c77"><code>b7447c6</code></a>
fix(infra): skip serdes tests in min-version release step (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36818">#36818</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/41c0cc58b0dac82000d24715f7a4b44dc8b01fd3"><code>41c0cc5</code></a>
release(openai): 1.1.14 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36820">#36820</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/0516156ef98f5001129f6d47bc8682d6536d58fb"><code>0516156</code></a>
fix(openai): use SSRF-safe transport for image token counting (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36819">#36819</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/338aa8131a8124e7aa1e042616ccd2366ff9f699"><code>338aa81</code></a>
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/3">#3</a>...</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/51e954877efd2d2c3c5bf09364dcfec8794eadb0"><code>51e9548</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/text-splitters (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36797">#36797</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/e85c418cfa559d4a794ddc6db92c6febab44651c"><code>e85c418</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/model-profiles (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36798">#36798</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/789126e6c78ad74664bea26228dda6e72e135dce"><code>789126e</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/standard-tests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36799">#36799</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/937b3eb3827551d17ee4736f9acc4aa57e88c716"><code>937b3eb</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/langchain_v1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36800">#36800</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/a06c205738cf5953e28c37287ddb1559d67c01f6"><code>a06c205</code></a>
ci(infra): validate issue checkboxes by section (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36811">#36811</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/aa33b06deb0d65489ce254b48a8aaf8a86304c18"><code>aa33b06</code></a>
fix(langchain-classic): suppress mypy errors in compat code (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36806">#36806</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-openai==1.0.1...langchain-openai==1.1.14">compare
view</a></li>
</ul>
</details>
<br />

Updates `langchain-openai` from 1.0.0a2 to 1.1.14
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchain/releases">langchain-openai's
releases</a>.</em></p>
<blockquote>
<h2>langchain-openai==1.1.14</h2>
<p>Changes since langchain-openai==1.1.13</p>
<p>release(openai): 1.1.14 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36820">#36820</a>)
fix(openai): use SSRF-safe transport for image token counting (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36819">#36819</a>)
chore(deps): bump pytest to <code>9.0.3</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36801">#36801</a>)
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36795">#36795</a>)
chore: bump pillow from 12.1.1 to 12.2.0 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36777">#36777</a>)</p>
<h2>langchain-openai==1.1.13</h2>
<p>Changes since langchain-openai==1.1.12</p>
<p>release(openai): 1.1.13 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36729">#36729</a>)
fix(openai): handle content blocks without type key in responses api
conversion (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36725">#36725</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36539">#36539</a>)
chore(openai): fix broken vcr cassette playback and add ci guard (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36502">#36502</a>)
fix(openai,groq,openrouter): use is-not-None checks in usage metadata
token extraction (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36500">#36500</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36455">#36455</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36368">#36368</a>)
fix(openai): update computer call test (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36352">#36352</a>)
fix(openai): let user-provided User-Agent override the Azure default (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35523">#35523</a>)
chore: bump requests from 2.32.5 to 2.33.0 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36248">#36248</a>)</p>
<h2>langchain-openai==1.1.12</h2>
<p>Changes since langchain-openai==1.1.11</p>
<p>fix(openai): bump min core version (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36180">#36180</a>)
release(openai): 1.1.12 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36178">#36178</a>)
fix(core,model-profiles): add missing <code>ModelProfile</code> fields,
warn on schema drift (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36129">#36129</a>)
fix(openai): support phase parameter (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36161">#36161</a>)
fix(openai): preserve namespace field in streaming function_call chunks
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36108">#36108</a>)
ci: suppress pytest streaming output in CI (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36092">#36092</a>)
ci: avoid unnecessary dep installs in lint targets (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36046">#36046</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36039">#36039</a>)
chore: bump orjson from 3.11.5 to 3.11.6 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35860">#35860</a>)
fix(openai): add type: message to Responses API input items (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35693">#35693</a>)
perf(.github): set a timeout on get min versions HTTP calls (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35851">#35851</a>)
feat(model-profiles): new fields + <code>Makefile</code> target (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35788">#35788</a>)
fix(openai): close PIL Image handles in token counting to prevent fd
leak (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35742">#35742</a>)
fix(openai): typo (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35763">#35763</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35754">#35754</a>)</p>
<h2>langchain-openai==1.1.11</h2>
<p>Changes since langchain-openai==1.1.10</p>
<p>fix(openai): bump min core version (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35705">#35705</a>)
release(openai): 1.1.11 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/35703">#35703</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/b7447c6969fc928ec3f29c200e2e56c0a46c4c77"><code>b7447c6</code></a>
fix(infra): skip serdes tests in min-version release step (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36818">#36818</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/41c0cc58b0dac82000d24715f7a4b44dc8b01fd3"><code>41c0cc5</code></a>
release(openai): 1.1.14 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36820">#36820</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/0516156ef98f5001129f6d47bc8682d6536d58fb"><code>0516156</code></a>
fix(openai): use SSRF-safe transport for image token counting (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36819">#36819</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/338aa8131a8124e7aa1e042616ccd2366ff9f699"><code>338aa81</code></a>
fix(core): restore cloud metadata IPs and link-local range in SSRF
policy (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/3">#3</a>...</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/51e954877efd2d2c3c5bf09364dcfec8794eadb0"><code>51e9548</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/text-splitters (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36797">#36797</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/e85c418cfa559d4a794ddc6db92c6febab44651c"><code>e85c418</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/model-profiles (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36798">#36798</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/789126e6c78ad74664bea26228dda6e72e135dce"><code>789126e</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/standard-tests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36799">#36799</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/937b3eb3827551d17ee4736f9acc4aa57e88c716"><code>937b3eb</code></a>
chore: bump langsmith from 0.6.3 to 0.7.31 in /libs/langchain_v1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36800">#36800</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/a06c205738cf5953e28c37287ddb1559d67c01f6"><code>a06c205</code></a>
ci(infra): validate issue checkboxes by section (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36811">#36811</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/aa33b06deb0d65489ce254b48a8aaf8a86304c18"><code>aa33b06</code></a>
fix(langchain-classic): suppress mypy errors in compat code (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36806">#36806</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-openai==1.0.1...langchain-openai==1.1.14">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
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-17 12:42:55 -07:00
Eugene YurtsevandGitHub 4956134a37 release(langgraph): 1.1.8 (#7545)
releasing 1.1.8
2026-04-17 19:41:13 +00:00
aa94790f36 fix(langgraph): remove strict add_handler type check that breaks OTel instrumentation (#7544)
## Summary

Removes the `add_handler()` overrides on `_GraphCallbackManager` and
`_AsyncGraphCallbackManager` that reject handlers not inheriting from
`GraphCallbackHandler`. This fixes a regression in 1.1.7 where
`opentelemetry-instrumentation-langchain` (and likely other libraries
that patch `BaseCallbackManager.__init__`) crash with `TypeError:
handlers must inherit GraphCallbackHandler` at invocation time.

## Why this is safe

The strict type check is redundant — `_configure_graph_callbacks` and
`_filter_graph_handlers` already filter handlers to
`GraphCallbackHandler` instances at construction time. Non-graph
handlers that enter via external patches (like OTel's monkey-patch) are
harmless because `handle_event("on_interrupt", ...)` /
`handle_event("on_resume", ...)` will simply no-op on handlers that
don't implement those methods.

## What changed

- Deleted `add_handler()` override from `_GraphCallbackManager` (was
lines 248-255)
- Deleted `add_handler()` override from `_AsyncGraphCallbackManager`
(was lines 324-331)
- No other changes — 18 lines removed, 0 added

## Test plan

- [x] All 8 existing `test_graph_callbacks.py` tests pass (`make test
TEST=tests/test_graph_callbacks.py`)
- [x] `make lint` passes
- [x] `make format` passes (no changes needed)
- [x] Verified fix locally: `LangchainInstrumentor().instrument()` +
`create_react_agent()` + `graph.ainvoke()` no longer raises `TypeError`
- [x] Verified the graph lifecycle callbacks (`on_interrupt`,
`on_resume`) still work correctly

Closes #7543

---------

Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
2026-04-17 15:33:10 -04:00
ccurmeandGitHub e002711ede release(prebuilt): 1.0.10 (#7541) 2026-04-17 13:52:20 -04:00
f44b49b33d chore: dedup warnings (#7257)
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
2026-04-17 10:13:03 -07:00
a0a95df2ac release(cli): 0.4.23 (#7542)
Release Note: Increase the max bound for langgraph-api

Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
2026-04-17 16:38:14 +00:00
Eugene YurtsevandGitHub d194c18c06 release(langgraph): 1.1.7 (#7540)
release langgraph 1.1.7
2026-04-17 15:19:18 +00:00
Sydney Runkle eae916719f CLI bump 2026-04-16 13:48:02 -04:00
f093702e4e fix(prebuilt): handle injected NotRequired keys (#7392)
Resolves https://github.com/langchain-ai/langchain/issues/35585

This would previously raise KeyError:
```python
from typing import Annotated

from langchain_core.tools import tool
from langchain.agents import create_agent
from typing_extensions import NotRequired
from langgraph.prebuilt import InjectedState
from langchain.agents import AgentState


class CustomAgentState(AgentState):
    city: NotRequired[str]


@tool
def get_weather(city: Annotated[str | None, InjectedState("city")] = None) -> str:
    """Get weather for a given city."""
    if city is None:
        city = "Boston"
    return f"It's always sunny in {city}!"


agent = create_agent(
    model="claude-sonnet-4-6",
    tools=[get_weather],
    system_prompt="You are a helpful assistant",
    state_schema=CustomAgentState,
)

input_message = {
    "role": "user",
    "content": "What's the weather?",
}

result = agent.invoke({"messages": [input_message]})
for m in result["messages"]:
    m.pretty_print()
```

---------

Co-authored-by: Sydney Runkle <sydneymarierunkle@gmail.com>
2026-04-16 13:06:33 +00:00
Sydney RunkleandGitHub 51cbdbd5cd fix: time travel when going back to interrupt node (#7498)
# Fix: Create fork checkpoint on subgraph time travel

## Problem

When time-traveling to a subgraph checkpoint that has an interrupt, and
then resuming, the resume would load the **wrong state** — it would pick
up the original execution's latest checkpoint instead of the
time-traveled one.

This happened because replaying from a subgraph checkpoint never created
a new parent checkpoint. If the replay hit an interrupt before
`after_tick()` ran, no checkpoint was written at all, so the parent's
"latest" checkpoint was still the old one from the original execution.

## Fix

When the loop detects a time-travel replay (not an `update_state` fork),
it now **eagerly writes a fork checkpoint** at the start of the tick.
This ensures:

1. The parent thread's latest checkpoint points to the replayed state
2. Subsequent `Command(resume=...)` calls find the correct checkpoint
3. Stale `INTERRUPT` pending writes from the old checkpoint are cleared
(they reference old task IDs)

Additionally, the subgraph replay logic now uses the **parent checkpoint
ID** (from `prev_checkpoint_config`) when resolving subgraph checkpoints
during time-travel, matching the existing behavior for `update_state`
forks.

## Checkpoint flow diagrams

### Before fix: time travel leaves no fork

```
Original execution:

  C0 (start) --> C1 (step_a) --> C2 (ask_1 interrupt) --> C3 (resume) --> C4 (ask_2 interrupt) --> C5 (done)

Time travel to C2 (subgraph config):

  Replay runs... hits interrupt... no new checkpoint written.
  Parent "latest" is still C5.

  Command(resume="new_answer"):
    Loads C5 (wrong!) instead of the replayed C2 state.
```

### After fix: time travel creates a fork

```
Original execution:

  C0 --> C1 --> C2 --> C3 --> C4 --> C5 (done)

Time travel to C2 (subgraph config):

  C0 --> C1 --> C2 --> C3 --> C4 --> C5
                  \
                   F1 (fork, source="fork")  <-- new latest

  Command(resume="new_answer"):
    Loads F1 (correct!) --> resumes from the right state.

  After full resume:

  C0 --> C1 --> C2 --> C3 --> C4 --> C5
                  \
                   F1 --> F2 (ask_1 result) --> F3 (ask_2 interrupt) --> F4 (done)
```

### Manual fork via `update_state` (unchanged)

```
  C0 --> C1 --> C2 --> C3
                  \
                   U1 (source="update")  <-- created by update_state()

  This path already worked. The fix skips update/fork sources
  so existing behavior is preserved.
```

## Changes

- **`libs/langgraph/langgraph/pregel/_loop.py`**:
- Extract `is_time_traveling` flag from the existing replay detection
logic for reuse
- Write a fork checkpoint (`source="fork"`) eagerly at the start of a
time-travel tick, before execution begins
- Clear stale `INTERRUPT` pending writes when creating the fork (they
reference old task IDs that won't match the new checkpoint)
- Unify subgraph replay ID resolution: check `source in ("update",
"fork")` instead of a separate `is_time_traveling` condition, since the
new fork checkpoint now has `source="fork"`
- **`libs/langgraph/tests/test_time_travel.py`** and
**`test_time_travel_async.py`**: Added 4 new test cases (sync + async):
- `test_replay_from_before_interrupt_then_resume` — replays from a
checkpoint before an interrupt, resumes with a new answer, and verifies
the full checkpoint history (source, next, values) at each stage
- `test_subgraph_time_travel_resume_from_first_interrupt` — time-travels
to a subgraph's first interrupt, resumes both interrupts with new
answers, and verifies the fork creates a new branch while preserving the
original
- `test_subgraph_time_travel_resume_from_second_interrupt` —
time-travels to a subgraph's second interrupt, resumes with a new
answer, and verifies the first interrupt's original answer is preserved
- `test_subgraph_time_travel_checkpoint_pattern` — verifies the fork
checkpoint branches from the correct replay point and that the full
checkpoint tree is correct after resume
- **`libs/langgraph/tests/test_pregel.py`** /
**`test_pregel_async.py`**: Updated existing
`test_weather_subgraph_state` to account for the new fork checkpoint
appearing in history (history length increases by 1)
2026-04-16 08:29:48 -04:00
hari-dhanushkodiandGitHub 4d64227c13 chore: start tracking cli deploy source (#7520)
Fixes #

<!-- Replace everything above this line with a 1-2 sentence description
of your change. Keep the "Fixes #xx" keyword and update the issue
number. -->

Read the full contributing guidelines:
https://docs.langchain.com/oss/python/contributing/overview

> **All contributions must be in English.** See the [language
policy](https://docs.langchain.com/oss/python/contributing/overview#language-policy).

If you paste a large clearly AI generated description here your PR may
be IGNORED or CLOSED!

Thank you for contributing to LangGraph! Follow these steps to have your
pull request considered as ready for review.

1. PR title: Should follow the format: TYPE(SCOPE): DESCRIPTION

    - feat(langgraph): add multi-tenant support
- Allowed TYPE and SCOPE values:
https://github.com/langchain-ai/langgraph/blob/main/.github/workflows/pr_lint.yml#L19-L43

2. PR description:

  - Write 1-2 sentences summarizing the change.
- The `Fixes #xx` line at the top is **required** for external
contributions — update the issue number and keep the keyword. This links
your PR to the approved issue and auto-closes it on merge.
  - If there are any breaking changes, please clearly describe them.
- If this PR depends on another PR being merged first, please include
"Depends on #PR_NUMBER" in the description.

3. Run `make format`, `make lint` and `make test` from the root of the
package(s) you've modified.

  - We will not consider a PR unless these three are passing in CI.

4. How did you verify your code works?

Additional guidelines:

- All external PRs must link to an issue or discussion where a solution
has been approved by a maintainer, and you must be assigned to that
issue. PRs without prior approval will be closed.
- PRs should not touch more than one package unless absolutely
necessary.
- Do not update the `uv.lock` files or add dependencies to
`pyproject.toml` files (even optional ones) unless you have explicit
permission to do so by a maintainer.

## Social handles (optional)
<!-- If you'd like a shoutout on release, add your socials below -->
Twitter: @
LinkedIn: https://linkedin.com/in/
2026-04-16 08:24:47 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6bcac5d72e chore(deps): bump langsmith from 0.6.4 to 0.7.31 in /libs/prebuilt (#7530)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.6.4 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.6.4...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.6.4&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:37:47 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6177c4311b chore(deps): bump langsmith from 0.6.4 to 0.7.31 in /libs/checkpoint (#7525)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.6.4 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.6.4...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.6.4&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:37:35 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
303769904b chore(deps): bump langsmith from 0.7.20 to 0.7.31 in /libs/sdk-py (#7528)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.7.20 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.20...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.7.20&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:37:27 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
cee7dcd523 chore(deps): bump langsmith from 0.7.26 to 0.7.31 in /libs/cli (#7529)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.7.26 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.26...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.7.26&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:37:13 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3413723e5a chore(deps): bump langsmith from 0.6.4 to 0.7.31 in /libs/checkpoint-postgres (#7527)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.6.4 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.6.4...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.6.4&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:37:03 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
07252d2cda chore(deps): bump langsmith from 0.6.4 to 0.7.31 in /libs/langgraph (#7526)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.6.4 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.6.4...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.6.4&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:36:52 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
47fd42abb2 chore(deps): bump langsmith from 0.6.4 to 0.7.31 in /libs/checkpoint-sqlite (#7524)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.6.4 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.6.4...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.6.4&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:36:42 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
554b2db1f8 chore(deps): bump langsmith from 0.7.3 to 0.7.31 in /libs/checkpoint-conformance (#7523)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.7.3 to 0.7.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.3...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=uv&previous-version=0.7.3&new-version=0.7.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:36:27 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
93a144b404 chore(deps): bump the uv group across 2 directories with 1 update (#7531)
Bumps the uv group with 1 update in the /libs/cli/uv-examples/monorepo
directory: [langsmith](https://github.com/langchain-ai/langsmith-sdk).
Bumps the uv group with 1 update in the /libs/cli/uv-examples/simple
directory: [langsmith](https://github.com/langchain-ai/langsmith-sdk).

Updates `langsmith` from 0.7.26 to 0.7.31
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.26...v0.7.31">compare
view</a></li>
</ul>
</details>
<br />

Updates `langsmith` from 0.7.26 to 0.7.31
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.31</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps-dev): bump langchain-core from 1.2.23 to 1.2.28 in
/python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2692">langchain-ai/langsmith-sdk#2692</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.82.0 to
0.84.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2684">langchain-ai/langsmith-sdk#2684</a></li>
<li>chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /python by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2693">langchain-ai/langsmith-sdk#2693</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.84.0 to
0.85.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2700">langchain-ai/langsmith-sdk#2700</a></li>
<li>feat(py): Tag OpenAI Agent Python SDK runs with ls_agent_type by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2699">langchain-ai/langsmith-sdk#2699</a></li>
<li>feat(js): Adds ls_agent_type metadata to AI SDK runs by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2701">langchain-ai/langsmith-sdk#2701</a></li>
<li>chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to
4.67.3.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2710">langchain-ai/langsmith-sdk#2710</a></li>
<li>chore(deps): bump pnpm/action-setup from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2705">langchain-ai/langsmith-sdk#2705</a></li>
<li>chore(deps): bump the py-minor-and-patch group across 1 directory
with 10 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2711">langchain-ai/langsmith-sdk#2711</a></li>
<li>chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2702">langchain-ai/langsmith-sdk#2702</a></li>
<li>chore(deps): bump actions/github-script from 8 to 9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2706">langchain-ai/langsmith-sdk#2706</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 7 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2712">langchain-ai/langsmith-sdk#2712</a></li>
<li>chore(deps-dev): bump types-psutil from 7.2.2.20260130 to
7.2.2.20260408 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2709">langchain-ai/langsmith-sdk#2709</a></li>
<li>chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2708">langchain-ai/langsmith-sdk#2708</a></li>
<li>feat: Filter kwargs from new token events by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2714">langchain-ai/langsmith-sdk#2714</a></li>
<li>release(py): 0.7.31 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2716">langchain-ai/langsmith-sdk#2716</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.30...v0.7.31</a></p>
<h2>v0.7.30</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(python): add service feature to sandbox by <a
href="https://github.com/DanielKneipp"><code>@​DanielKneipp</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2665">langchain-ai/langsmith-sdk#2665</a></li>
<li>fix(js): Fix prototype pollution bug in anonymizers by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2690">langchain-ai/langsmith-sdk#2690</a></li>
<li>release(js): 0.5.18 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2691">langchain-ai/langsmith-sdk#2691</a></li>
<li>chore(js/sandbox): suppress warning log by <a
href="https://github.com/hntrl"><code>@​hntrl</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2694">langchain-ai/langsmith-sdk#2694</a></li>
<li>feat(js): Add metadata to Claude Agent SDK JS tracing by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2695">langchain-ai/langsmith-sdk#2695</a></li>
<li>fix(py): Fix run tree memory leak by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2696">langchain-ai/langsmith-sdk#2696</a></li>
<li>release(py): 0.7.30 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2698">langchain-ai/langsmith-sdk#2698</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.29...v0.7.30</a></p>
<h2>v0.7.29</h2>
<h2>What's Changed</h2>
<ul>
<li>release(js): 0.5.17 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2681">langchain-ai/langsmith-sdk#2681</a></li>
<li>feat(py): Fix race condition around Claude Agent SDK instrumentation
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2685">langchain-ai/langsmith-sdk#2685</a></li>
<li>release(py): 0.7.29 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2686">langchain-ai/langsmith-sdk#2686</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.28...v0.7.29</a></p>
<h2>v0.7.28</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py): Support subagent tracing in Claude Agents SDK, fix usage
and duplicate messages by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2670">langchain-ai/langsmith-sdk#2670</a></li>
<li>chore(deps-dev): bump the py-minor-and-patch group across 1
directory with 11 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2677">langchain-ai/langsmith-sdk#2677</a></li>
<li>chore(deps-dev): bump the js-minor-and-patch group across 1
directory with 8 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2667">langchain-ai/langsmith-sdk#2667</a></li>
<li>chore(deps): bump pnpm/action-setup from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2658">langchain-ai/langsmith-sdk#2658</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/c434999d05c00334efeba88b8bbd2de9f3afbef6"><code>c434999</code></a>
release(py): 0.7.31 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2716">#2716</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/47d7c4a783333e716395d802e7632f1f1b4744d3"><code>47d7c4a</code></a>
feat: Filter kwargs from new token events (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2714">#2714</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/3c57445b543c9a2f86db52024ea2c998bfc2ffab"><code>3c57445</code></a>
chore(deps-dev): bump rich from 14.3.3 to 15.0.0 in /python (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2708">#2708</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/2be6cd01a2b6e35e811488d3561e7b0b57b06f63"><code>2be6cd0</code></a>
chore(deps-dev): bump types-psutil from 7.2.2.20260130 to 7.2.2.20260408
in /...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/b8b6ca32d43c919c07a4e13c99a83bcaab8accb0"><code>b8b6ca3</code></a>
chore(deps-dev): bump the js-minor-and-patch group across 1 directory
with 7 ...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9897cb33da7698291637f268edd833ca3e1adde6"><code>9897cb3</code></a>
chore(deps): bump actions/github-script from 8 to 9 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2706">#2706</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/572c0184285747e027a796e03ea6c9ba171e09a6"><code>572c018</code></a>
chore(deps-dev): bump <code>@​anthropic-ai/sdk</code> from 0.85.0 to
0.86.0 in /js (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2702">#2702</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/57447524c88b6bba2775161aa449da32fb8e5c42"><code>5744752</code></a>
chore(deps): bump the py-minor-and-patch group across 1 directory with
10 upd...</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/960cae7f490e9ccbe428e6b56c8047bdb7b942a5"><code>960cae7</code></a>
chore(deps): bump pnpm/action-setup from 5 to 6 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2705">#2705</a>)</li>
<li><a
href="https://github.com/langchain-ai/langsmith-sdk/commit/9370e7670abf7f8f9a36fbb72250bcfd2f91e7c6"><code>9370e76</code></a>
chore(deps-dev): bump types-tqdm from 4.67.3.20260303 to 4.67.3.20260408
in /...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.26...v0.7.31">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
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:36:05 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2f4611db8f chore(deps): bump langsmith from 0.5.18 to 0.5.20 in /libs/cli/js-examples (#7522)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.5.18 to 0.5.20.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/langchain-ai/langsmith-sdk/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=npm_and_yarn&previous-version=0.5.18&new-version=0.5.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:21:10 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
20c5d36efe chore(deps): bump langsmith from 0.5.18 to 0.5.20 in /libs/cli/js-monorepo-example (#7521)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.5.18 to 0.5.20.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/langchain-ai/langsmith-sdk/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=npm_and_yarn&previous-version=0.5.18&new-version=0.5.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 19:21:00 -07:00
25470ea435 release(checkpoint): 4.0.2 (#7518)
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
2026-04-15 20:52:57 +00:00
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>
2026-04-15 13:05:55 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6719d34023 chore(deps): bump pytest from 9.0.2 to 9.0.3 in /libs/checkpoint-sqlite (#7502)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 17:39:56 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>John Kennedy
96843788d0 chore(deps): bump langchain-core from 1.2.27 to 1.2.28 in /libs/cli (#7450)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.2.27 to 1.2.28.
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li>See full diff in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.27...langchain-core==1.2.28">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.2.27&new-version=1.2.28)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

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>
2026-04-14 10:39:03 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ba5e3c4a9b chore(deps): bump pytest from 9.0.2 to 9.0.3 in /libs/checkpoint (#7506)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=uv&previous-version=9.0.2&new-version=9.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 10:38:29 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9e9783b156 chore(deps): bump pytest from 9.0.2 to 9.0.3 in /libs/prebuilt (#7505)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=uv&previous-version=9.0.2&new-version=9.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 10:38:19 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c27d103e04 chore(deps): bump pytest from 9.0.2 to 9.0.3 in /libs/checkpoint-conformance (#7508)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=uv&previous-version=9.0.2&new-version=9.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 10:35:00 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d189f6551e chore(deps): bump pytest from 9.0.2 to 9.0.3 in /libs/langgraph (#7507)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=uv&previous-version=9.0.2&new-version=9.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 10:34:46 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1dd9adf833 chore(deps): bump pytest from 9.0.2 to 9.0.3 in /libs/checkpoint-postgres (#7503)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=uv&previous-version=9.0.2&new-version=9.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 10:34:34 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>John Kennedy
92c66ca997 chore(deps): bump langchain-core from 1.2.22 to 1.2.28 in /libs/checkpoint-sqlite (#7451)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.2.22 to 1.2.28.
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
<h2>langchain-core==1.2.27</h2>
<p>Changes since langchain-core==1.2.26</p>
<p>release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)
chore: add comment explaining <code>pygments&gt;=2.20.0</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36570">#36570</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>.</p>
<h2>langchain-core==1.2.26</h2>
<p>Changes since langchain-core==1.2.25</p>
<p>release(core): 1.2.26 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36511">#36511</a>)
fix(core): add init validator and serialization mappings for Bedrock
models (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34510">#34510</a>)
feat(core): add <code>ChatBaseten</code> to serializable mapping (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36510">#36510</a>)
chore(core): drop <code>gpt-3.5-turbo</code> from docstrings (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36497">#36497</a>)
fix(core): correct parameter names in filter_messages docstring example
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36462">#36462</a>)</p>
<h2>langchain-core==1.2.25</h2>
<p>Changes since langchain-core==1.2.24</p>
<p>release(core): 1.2.25 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36473">#36473</a>)
fix(core): harden check for txt files in deprecated prompt loading
functions (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue resolved in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>.</p>
<h2>langchain-core==1.2.24</h2>
<p>Changes since langchain-core==1.2.23</p>
<p>release(core): 1.2.24 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36434">#36434</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
fix(core): add &quot;computer&quot; to _WellKnownOpenAITools (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36261">#36261</a>)</p>
<h2>langchain-core==1.2.23</h2>
<p>Changes since langchain-core==1.2.22</p>
<p>release(core): 1.2.23 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36323">#36323</a>)
revert: Revert &quot;fix(core): trace invocation params in
metadata&quot; (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36322">#36322</a>)
chore: bump requests from 2.32.5 to 2.33.0 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36243">#36243</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/64864041168606535dfbd39055c0dca3dd61b5ba"><code>6486404</code></a>
release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7629c747260cbaed7ca55466d5b9e1b520a7de77"><code>7629c74</code></a>
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/ce21bf469d7493f4716bc30feb15a5b3f16ebe1e"><code>ce21bf4</code></a>
ci: convert working-directory to validated dropdown (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36575">#36575</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/b8698eacbd2960c7e3195018f42992bf2c9d69c7"><code>b8698ea</code></a>
release(ollama): 1.1.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36574">#36574</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3beba77e2e23d498fda07f9b8d6ba00aabfaf69f"><code>3beba77</code></a>
feat(ollama): support <code>response_format</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34612">#34612</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.22...langchain-core==1.2.28">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.2.22&new-version=1.2.28)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

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>
2026-04-14 10:32:10 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a7356edf8a chore(deps): bump langsmith from 0.5.4 to 0.5.18 in /libs/cli/js-examples (#7474)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.5.4 to 0.5.18.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/langchain-ai/langsmith-sdk/commits">compare
view</a></li>
</ul>
</details>
<details>
<summary>Install script changes</summary>
<p>This version modifies <code>prepublish</code> script that runs during
installation. Review the package contents before updating.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=npm_and_yarn&previous-version=0.5.4&new-version=0.5.18)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 10:25:09 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
354dceaac7 chore(deps-dev): bump pytest from 9.0.2 to 9.0.3 in /libs/sdk-py (#7504)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=uv&previous-version=9.0.2&new-version=9.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 10:24:27 -07:00
Eugene YurtsevandGitHub 2ff294af77 release(langgraph): 1.1.7a2 (#7511)
Release 1.1.7a2
2026-04-14 16:55:35 +00:00
4c67f84016 chore: allow passing some metadata only for tracing purposes (#7383)
Allows us to put some more information for tracing purposes (e.g.,
ls_integration) without dumping it into the streaming APIs (good for
performance)
Move some other metadata into tracing only since it's not needed in
streaming APIs

---------

Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
2026-04-14 09:58:24 -04:00
open-swe[bot]GitHubopen-swe[bot] <open-swe@users.noreply.github.com>Sydney Runkle
2c98c59fca fix: populate assistant_id from config configurable instead of metadata (#7468)
## Description
The `_build_server_info` function was reading `assistant_id` and
`graph_id` from `config["metadata"]`, but the server puts these values
in `config["configurable"]`. This updates the source to read from
`configurable` consistently.

## Test Plan
- [ ] Verify `server_info.assistant_id` and `server_info.graph_id` are
correctly populated from `config["configurable"]`

_Opened collaboratively by Sydney Runkle and open-swe._

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com>
2026-04-13 09:08:09 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d27d4b2d98 chore(deps): bump langsmith from 0.5.4 to 0.5.18 in /libs/cli/js-monorepo-example (#7475)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.5.4 to 0.5.18.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/langchain-ai/langsmith-sdk/commits">compare
view</a></li>
</ul>
</details>
<details>
<summary>Install script changes</summary>
<p>This version modifies <code>prepublish</code> script that runs during
installation. Review the package contents before updating.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=npm_and_yarn&previous-version=0.5.4&new-version=0.5.18)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 14:29:04 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
742d165acb chore(deps): bump uv from 0.11.3 to 0.11.6 in /libs/cli (#7472)
Bumps [uv](https://github.com/astral-sh/uv) from 0.11.3 to 0.11.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/uv/releases">uv's
releases</a>.</em></p>
<blockquote>
<h2>0.11.6</h2>
<h2>Release Notes</h2>
<p>Released on 2026-04-09.</p>
<p>This release resolves a low severity security advisory in which
wheels with malformed RECORD entries could delete arbitrary files on
uninstall. See <a
href="https://github.com/astral-sh/uv/security/advisories/GHSA-pjjw-68hj-v9mw">GHSA-pjjw-68hj-v9mw</a>
for details.</p>
<h3>Bug fixes</h3>
<ul>
<li>Do not remove files outside the venv on uninstall (<a
href="https://redirect.github.com/astral-sh/uv/pull/18942">#18942</a>)</li>
<li>Validate and heal wheel <code>RECORD</code> during installation (<a
href="https://redirect.github.com/astral-sh/uv/pull/18943">#18943</a>)</li>
<li>Avoid <code>uv cache clean</code> errors due to Win32 path
normalization (<a
href="https://redirect.github.com/astral-sh/uv/pull/18856">#18856</a>)</li>
</ul>
<h2>Install uv 0.11.6</h2>
<h3>Install prebuilt binaries via shell script</h3>
<pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf
https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-installer.sh
| sh
</code></pre>
<h3>Install prebuilt binaries via powershell script</h3>
<pre lang="sh"><code>powershell -ExecutionPolicy Bypass -c &quot;irm
https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-installer.ps1
| iex&quot;
</code></pre>
<h2>Download uv 0.11.6</h2>
<table>
<thead>
<tr>
<th>File</th>
<th>Platform</th>
<th>Checksum</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-apple-darwin.tar.gz">uv-aarch64-apple-darwin.tar.gz</a></td>
<td>Apple Silicon macOS</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-apple-darwin.tar.gz">uv-x86_64-apple-darwin.tar.gz</a></td>
<td>Intel macOS</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-pc-windows-msvc.zip">uv-aarch64-pc-windows-msvc.zip</a></td>
<td>ARM64 Windows</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-i686-pc-windows-msvc.zip">uv-i686-pc-windows-msvc.zip</a></td>
<td>x86 Windows</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-i686-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-pc-windows-msvc.zip">uv-x86_64-pc-windows-msvc.zip</a></td>
<td>x64 Windows</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-unknown-linux-gnu.tar.gz">uv-aarch64-unknown-linux-gnu.tar.gz</a></td>
<td>ARM64 Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-i686-unknown-linux-gnu.tar.gz">uv-i686-unknown-linux-gnu.tar.gz</a></td>
<td>x86 Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-i686-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-powerpc64le-unknown-linux-gnu.tar.gz">uv-powerpc64le-unknown-linux-gnu.tar.gz</a></td>
<td>PPC64LE Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-powerpc64le-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-riscv64gc-unknown-linux-gnu.tar.gz">uv-riscv64gc-unknown-linux-gnu.tar.gz</a></td>
<td>RISCV Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-riscv64gc-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-s390x-unknown-linux-gnu.tar.gz">uv-s390x-unknown-linux-gnu.tar.gz</a></td>
<td>S390x Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-s390x-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-unknown-linux-gnu.tar.gz">uv-x86_64-unknown-linux-gnu.tar.gz</a></td>
<td>x64 Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-armv7-unknown-linux-gnueabihf.tar.gz">uv-armv7-unknown-linux-gnueabihf.tar.gz</a></td>
<td>ARMv7 Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-armv7-unknown-linux-gnueabihf.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-unknown-linux-musl.tar.gz">uv-aarch64-unknown-linux-musl.tar.gz</a></td>
<td>ARM64 MUSL Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-aarch64-unknown-linux-musl.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-i686-unknown-linux-musl.tar.gz">uv-i686-unknown-linux-musl.tar.gz</a></td>
<td>x86 MUSL Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-i686-unknown-linux-musl.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-riscv64gc-unknown-linux-musl.tar.gz">uv-riscv64gc-unknown-linux-musl.tar.gz</a></td>
<td>RISCV MUSL Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-riscv64gc-unknown-linux-musl.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-unknown-linux-musl.tar.gz">uv-x86_64-unknown-linux-musl.tar.gz</a></td>
<td>x64 MUSL Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-x86_64-unknown-linux-musl.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-arm-unknown-linux-musleabihf.tar.gz">uv-arm-unknown-linux-musleabihf.tar.gz</a></td>
<td>ARMv6 MUSL Linux (Hardfloat)</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-arm-unknown-linux-musleabihf.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-armv7-unknown-linux-musleabihf.tar.gz">uv-armv7-unknown-linux-musleabihf.tar.gz</a></td>
<td>ARMv7 MUSL Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.6/uv-armv7-unknown-linux-musleabihf.tar.gz.sha256">checksum</a></td>
</tr>
</tbody>
</table>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/uv/blob/main/CHANGELOG.md">uv's
changelog</a>.</em></p>
<blockquote>
<h2>0.11.6</h2>
<p>Released on 2026-04-09.</p>
<p>This release resolves a low severity security advisory in which
wheels with malformed RECORD entries could delete arbitrary files on
uninstall. See <a
href="https://github.com/astral-sh/uv/security/advisories/GHSA-pjjw-68hj-v9mw">GHSA-pjjw-68hj-v9mw</a>
for details.</p>
<h3>Bug fixes</h3>
<ul>
<li>Do not remove files outside the venv on uninstall (<a
href="https://redirect.github.com/astral-sh/uv/pull/18942">#18942</a>)</li>
<li>Validate and heal wheel <code>RECORD</code> during installation (<a
href="https://redirect.github.com/astral-sh/uv/pull/18943">#18943</a>)</li>
<li>Avoid <code>uv cache clean</code> errors due to Win32 path
normalization (<a
href="https://redirect.github.com/astral-sh/uv/pull/18856">#18856</a>)</li>
</ul>
<h2>0.11.5</h2>
<p>Released on 2026-04-08.</p>
<h3>Python</h3>
<ul>
<li>Add CPython 3.13.13, 3.14.4, and 3.15.0a8 (<a
href="https://redirect.github.com/astral-sh/uv/pull/18908">#18908</a>)</li>
</ul>
<h3>Enhancements</h3>
<ul>
<li>Fix <code>build_system.requires</code> error message (<a
href="https://redirect.github.com/astral-sh/uv/pull/18911">#18911</a>)</li>
<li>Remove trailing path separators in path normalization (<a
href="https://redirect.github.com/astral-sh/uv/pull/18915">#18915</a>)</li>
<li>Improve error messages for unsupported or invalid TLS certificates
(<a
href="https://redirect.github.com/astral-sh/uv/pull/18924">#18924</a>)</li>
</ul>
<h3>Preview features</h3>
<ul>
<li>Add <code>exclude-newer</code> to <code>[[tool.uv.index]]</code> (<a
href="https://redirect.github.com/astral-sh/uv/pull/18839">#18839</a>)</li>
<li><code>uv audit</code>: add context/warnings for ignored
vulnerabilities (<a
href="https://redirect.github.com/astral-sh/uv/pull/18905">#18905</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Normalize persisted fork markers before lock equality checks (<a
href="https://redirect.github.com/astral-sh/uv/pull/18612">#18612</a>)</li>
<li>Clear junction properly when uninstalling Python versions on Windows
(<a
href="https://redirect.github.com/astral-sh/uv/pull/18815">#18815</a>)</li>
<li>Report error cleanly instead of panicking on TLS certificate error
(<a
href="https://redirect.github.com/astral-sh/uv/pull/18904">#18904</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>Remove the legacy <code>PIP_COMPATIBILITY.md</code> redirect file
(<a
href="https://redirect.github.com/astral-sh/uv/pull/18928">#18928</a>)</li>
<li>Fix <code>uv init example-bare --bare</code> examples (<a
href="https://redirect.github.com/astral-sh/uv/pull/18822">#18822</a>,
<a
href="https://redirect.github.com/astral-sh/uv/pull/18925">#18925</a>)</li>
</ul>
<h2>0.11.4</h2>
<p>Released on 2026-04-07.</p>
<h3>Enhancements</h3>
<ul>
<li>Add support for <code>--upgrade-group</code> (<a
href="https://redirect.github.com/astral-sh/uv/pull/18266">#18266</a>)</li>
<li>Merge repeated archive URL hashes by version ID (<a
href="https://redirect.github.com/astral-sh/uv/pull/18841">#18841</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/astral-sh/uv/commit/65950801cc3c609b65be34938bb407ab6e30a9fe"><code>6595080</code></a>
Bump version to 0.11.6 (<a
href="https://redirect.github.com/astral-sh/uv/issues/18948">#18948</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/7983c7a5bef236fd8a04580fcedae7bd5bde4cdb"><code>7983c7a</code></a>
Validate and heal RECORD during installation (<a
href="https://redirect.github.com/astral-sh/uv/issues/18943">#18943</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/b38439bfc731d5281e933656ce2e5b910da037b0"><code>b38439b</code></a>
Avoid <code>uv cache clean</code> errors due to Win32 path normalization
(<a
href="https://redirect.github.com/astral-sh/uv/issues/18856">#18856</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/a0e461ac44851f9a0f6e8974733e77d46f7a9ea9"><code>a0e461a</code></a>
Do not remove files outside the venv on uninstall (<a
href="https://redirect.github.com/astral-sh/uv/issues/18942">#18942</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/95eaa68c8df627eb915bc355831fd7d169d91fe3"><code>95eaa68</code></a>
Bump version to 0.11.5 (<a
href="https://redirect.github.com/astral-sh/uv/issues/18930">#18930</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/f6d67d57c1a9f17f7ab233654b55e061eb4bfd10"><code>f6d67d5</code></a>
Improve certificate loading error messages (<a
href="https://redirect.github.com/astral-sh/uv/issues/18924">#18924</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/39b83c30e0cdaed833e88564878376f9361987d2"><code>39b83c3</code></a>
Add <code>exclude-newer</code> to <code>[[tool.uv.index]]</code> (<a
href="https://redirect.github.com/astral-sh/uv/issues/18839">#18839</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/7924ba5b1419345dc5b9a9a16e6bcba2b59a41a6"><code>7924ba5</code></a>
uv audit: add context/warnings for ignored vulnerabilities (<a
href="https://redirect.github.com/astral-sh/uv/issues/18905">#18905</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/a352ce01eab5f19bbd5929f2a5f346187552ee7c"><code>a352ce0</code></a>
Remove the legacy PIP_COMPATIBILITY.md redirect file (<a
href="https://redirect.github.com/astral-sh/uv/issues/18928">#18928</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/33b633891181f768568bfc3196039d368417fe98"><code>33b6338</code></a>
Normalize persisted fork markers before lock equality checks (<a
href="https://redirect.github.com/astral-sh/uv/issues/18612">#18612</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/uv/compare/0.11.3...0.11.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=uv&package-manager=uv&previous-version=0.11.3&new-version=0.11.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 14:28:46 -07:00
Eugene YurtsevandGitHub b442bf802a release(langgraph): 1.1.7a1 (#7476)
Adding graph life cycle callbacks
2026-04-10 21:12:03 +00:00
Eugene YurtsevandGitHub bede0b7acf test(langgraph): use monotonic clock in flaky streaming test (#7477)
Switches test_sync_streaming_with_functional_api to time.monotonic() for
both emitted task timestamps and observed arrival times so the assertion
is based on a monotonic clock instead of wall time. This makes the
streaming timing check less flaky on systems where time.time() can jump
or lack sufficient precision.

Created with [Deep Agents
CLI](https://docs.langchain.com/oss/python/deepagents/cli/overview)
using gpt-5.4 (provider: openai).
2026-04-10 21:05:37 +00:00
3a5b5c9821 feat(langgraph): add graph lifecycle callback handlers (#7429)
## Summary

This change adds first-class graph lifecycle callbacks to LangGraph so
interrupt and resume transitions can be observed without overloading the
existing LangChain custom event system. It introduces a dedicated graph
callback manager and wires lifecycle emission through Pregel execution
in both sync and async paths.

## Changes

- **`libs/langgraph/langgraph/callbacks.py`**: Adds
`GraphCallbackHandler` and `GraphCallbackManager` (built on LangChain
base callback classes), plus config plumbing via `graph_callbacks` and
`get_graph_callback_manager_for_config`.
- **`libs/langgraph/langgraph/pregel/_loop.py`**: Introduces
`GraphLifecycleEvent` and records lifecycle transitions (`resume`,
`interrupt`) into an internal FIFO queue with
`shift_graph_lifecycle_event()`.
- **`libs/langgraph/langgraph/pregel/main.py`**: Resolves graph callback
manager from config and drains lifecycle events while loop execution
progresses, dispatching `on_resume` and `on_interrupt` consistently in
sync and async runtimes.
- **`libs/langgraph/tests/test_graph_callbacks.py`**: Adds sync and
async coverage verifying lifecycle callbacks fire correctly and remain
distinct from LangChain `on_custom_event` handlers.

---------

Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
2026-04-10 20:43:48 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>John Kennedy
1142ebf921 chore(deps): bump langchain-core from 1.2.23 to 1.2.28 in /libs/checkpoint (#7453)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.2.23 to 1.2.28.
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
<h2>langchain-core==1.2.27</h2>
<p>Changes since langchain-core==1.2.26</p>
<p>release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)
chore: add comment explaining <code>pygments&gt;=2.20.0</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36570">#36570</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>.</p>
<h2>langchain-core==1.2.26</h2>
<p>Changes since langchain-core==1.2.25</p>
<p>release(core): 1.2.26 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36511">#36511</a>)
fix(core): add init validator and serialization mappings for Bedrock
models (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34510">#34510</a>)
feat(core): add <code>ChatBaseten</code> to serializable mapping (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36510">#36510</a>)
chore(core): drop <code>gpt-3.5-turbo</code> from docstrings (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36497">#36497</a>)
fix(core): correct parameter names in filter_messages docstring example
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36462">#36462</a>)</p>
<h2>langchain-core==1.2.25</h2>
<p>Changes since langchain-core==1.2.24</p>
<p>release(core): 1.2.25 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36473">#36473</a>)
fix(core): harden check for txt files in deprecated prompt loading
functions (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue resolved in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>.</p>
<h2>langchain-core==1.2.24</h2>
<p>Changes since langchain-core==1.2.23</p>
<p>release(core): 1.2.24 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36434">#36434</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
fix(core): add &quot;computer&quot; to _WellKnownOpenAITools (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36261">#36261</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/64864041168606535dfbd39055c0dca3dd61b5ba"><code>6486404</code></a>
release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7629c747260cbaed7ca55466d5b9e1b520a7de77"><code>7629c74</code></a>
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/ce21bf469d7493f4716bc30feb15a5b3f16ebe1e"><code>ce21bf4</code></a>
ci: convert working-directory to validated dropdown (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36575">#36575</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/b8698eacbd2960c7e3195018f42992bf2c9d69c7"><code>b8698ea</code></a>
release(ollama): 1.1.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36574">#36574</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3beba77e2e23d498fda07f9b8d6ba00aabfaf69f"><code>3beba77</code></a>
feat(ollama): support <code>response_format</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34612">#34612</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.23...langchain-core==1.2.28">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.2.23&new-version=1.2.28)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

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>
2026-04-10 00:14:25 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>John Kennedy
37b34bdb1e chore(deps): bump langchain-core from 1.2.22 to 1.2.28 in /libs/sdk-py (#7449)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.2.22 to 1.2.28.
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
<h2>langchain-core==1.2.27</h2>
<p>Changes since langchain-core==1.2.26</p>
<p>release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)
chore: add comment explaining <code>pygments&gt;=2.20.0</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36570">#36570</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>.</p>
<h2>langchain-core==1.2.26</h2>
<p>Changes since langchain-core==1.2.25</p>
<p>release(core): 1.2.26 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36511">#36511</a>)
fix(core): add init validator and serialization mappings for Bedrock
models (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34510">#34510</a>)
feat(core): add <code>ChatBaseten</code> to serializable mapping (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36510">#36510</a>)
chore(core): drop <code>gpt-3.5-turbo</code> from docstrings (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36497">#36497</a>)
fix(core): correct parameter names in filter_messages docstring example
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36462">#36462</a>)</p>
<h2>langchain-core==1.2.25</h2>
<p>Changes since langchain-core==1.2.24</p>
<p>release(core): 1.2.25 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36473">#36473</a>)
fix(core): harden check for txt files in deprecated prompt loading
functions (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue resolved in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>.</p>
<h2>langchain-core==1.2.24</h2>
<p>Changes since langchain-core==1.2.23</p>
<p>release(core): 1.2.24 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36434">#36434</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
fix(core): add &quot;computer&quot; to _WellKnownOpenAITools (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36261">#36261</a>)</p>
<h2>langchain-core==1.2.23</h2>
<p>Changes since langchain-core==1.2.22</p>
<p>release(core): 1.2.23 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36323">#36323</a>)
revert: Revert &quot;fix(core): trace invocation params in
metadata&quot; (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36322">#36322</a>)
chore: bump requests from 2.32.5 to 2.33.0 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36243">#36243</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/64864041168606535dfbd39055c0dca3dd61b5ba"><code>6486404</code></a>
release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7629c747260cbaed7ca55466d5b9e1b520a7de77"><code>7629c74</code></a>
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/ce21bf469d7493f4716bc30feb15a5b3f16ebe1e"><code>ce21bf4</code></a>
ci: convert working-directory to validated dropdown (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36575">#36575</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/b8698eacbd2960c7e3195018f42992bf2c9d69c7"><code>b8698ea</code></a>
release(ollama): 1.1.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36574">#36574</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3beba77e2e23d498fda07f9b8d6ba00aabfaf69f"><code>3beba77</code></a>
feat(ollama): support <code>response_format</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34612">#34612</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.22...langchain-core==1.2.28">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.2.22&new-version=1.2.28)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

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>
2026-04-09 23:55:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
11e8d827eb chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /libs/langgraph (#7457)
Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.6
to 46.0.7.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst">cryptography's
changelog</a>.</em></p>
<blockquote>
<p>46.0.7 - 2026-04-07</p>
<pre><code>
* **SECURITY ISSUE**: Fixed an issue where non-contiguous buffers could
be
  passed to APIs that accept Python buffers, which could lead to buffer
  overflow. **CVE-2026-39892**
* Updated Windows, macOS, and Linux wheels to be compiled with OpenSSL
3.5.6.
<p>.. _v46-0-6:<br />
</code></pre></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/cryptography/commit/622d672e429a7cff836a23c5903683dbec1901f5"><code>622d672</code></a>
46.0.7 release (<a
href="https://redirect.github.com/pyca/cryptography/issues/14602">#14602</a>)</li>
<li>See full diff in <a
href="https://github.com/pyca/cryptography/compare/46.0.6...46.0.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cryptography&package-manager=uv&previous-version=46.0.6&new-version=46.0.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 23:54:47 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5fcebdd30a chore(deps): bump cryptography from 46.0.6 to 46.0.7 in /libs/cli (#7456)
Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.6
to 46.0.7.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst">cryptography's
changelog</a>.</em></p>
<blockquote>
<p>46.0.7 - 2026-04-07</p>
<pre><code>
* **SECURITY ISSUE**: Fixed an issue where non-contiguous buffers could
be
  passed to APIs that accept Python buffers, which could lead to buffer
  overflow. **CVE-2026-39892**
* Updated Windows, macOS, and Linux wheels to be compiled with OpenSSL
3.5.6.
<p>.. _v46-0-6:<br />
</code></pre></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/cryptography/commit/622d672e429a7cff836a23c5903683dbec1901f5"><code>622d672</code></a>
46.0.7 release (<a
href="https://redirect.github.com/pyca/cryptography/issues/14602">#14602</a>)</li>
<li>See full diff in <a
href="https://github.com/pyca/cryptography/compare/46.0.6...46.0.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cryptography&package-manager=uv&previous-version=46.0.6&new-version=46.0.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 23:54:28 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
173ef2ff44 chore(deps): bump langchain-core from 1.2.22 to 1.2.28 in /libs/checkpoint-postgres (#7454)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.2.22 to 1.2.28.
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
<h2>langchain-core==1.2.27</h2>
<p>Changes since langchain-core==1.2.26</p>
<p>release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)
chore: add comment explaining <code>pygments&gt;=2.20.0</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36570">#36570</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>.</p>
<h2>langchain-core==1.2.26</h2>
<p>Changes since langchain-core==1.2.25</p>
<p>release(core): 1.2.26 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36511">#36511</a>)
fix(core): add init validator and serialization mappings for Bedrock
models (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34510">#34510</a>)
feat(core): add <code>ChatBaseten</code> to serializable mapping (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36510">#36510</a>)
chore(core): drop <code>gpt-3.5-turbo</code> from docstrings (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36497">#36497</a>)
fix(core): correct parameter names in filter_messages docstring example
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36462">#36462</a>)</p>
<h2>langchain-core==1.2.25</h2>
<p>Changes since langchain-core==1.2.24</p>
<p>release(core): 1.2.25 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36473">#36473</a>)
fix(core): harden check for txt files in deprecated prompt loading
functions (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue resolved in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>.</p>
<h2>langchain-core==1.2.24</h2>
<p>Changes since langchain-core==1.2.23</p>
<p>release(core): 1.2.24 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36434">#36434</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
fix(core): add &quot;computer&quot; to _WellKnownOpenAITools (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36261">#36261</a>)</p>
<h2>langchain-core==1.2.23</h2>
<p>Changes since langchain-core==1.2.22</p>
<p>release(core): 1.2.23 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36323">#36323</a>)
revert: Revert &quot;fix(core): trace invocation params in
metadata&quot; (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36322">#36322</a>)
chore: bump requests from 2.32.5 to 2.33.0 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36243">#36243</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/64864041168606535dfbd39055c0dca3dd61b5ba"><code>6486404</code></a>
release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7629c747260cbaed7ca55466d5b9e1b520a7de77"><code>7629c74</code></a>
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/ce21bf469d7493f4716bc30feb15a5b3f16ebe1e"><code>ce21bf4</code></a>
ci: convert working-directory to validated dropdown (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36575">#36575</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/b8698eacbd2960c7e3195018f42992bf2c9d69c7"><code>b8698ea</code></a>
release(ollama): 1.1.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36574">#36574</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3beba77e2e23d498fda07f9b8d6ba00aabfaf69f"><code>3beba77</code></a>
feat(ollama): support <code>response_format</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34612">#34612</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.22...langchain-core==1.2.28">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.2.22&new-version=1.2.28)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 23:54:09 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>John Kennedy
98afc106a0 chore(deps): bump the uv group across 2 directories with 1 update (#7458)
Bumps the uv group with 1 update in the /libs/cli/uv-examples/monorepo
directory: [langchain-core](https://github.com/langchain-ai/langchain).
Bumps the uv group with 1 update in the /libs/cli/uv-examples/simple
directory: [langchain-core](https://github.com/langchain-ai/langchain).

Updates `langchain-core` from 1.2.27 to 1.2.28
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li>See full diff in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.27...langchain-core==1.2.28">compare
view</a></li>
</ul>
</details>
<br />

Updates `langchain-core` from 1.2.27 to 1.2.28
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li>See full diff in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.27...langchain-core==1.2.28">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
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

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>
2026-04-09 23:51:22 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
80ef3ced0b chore(deps): bump langchain-core from 1.2.22 to 1.2.28 in /libs/checkpoint-conformance (#7448)
Bumps [langchain-core](https://github.com/langchain-ai/langchain) from
1.2.22 to 1.2.28.
<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.2.28</h2>
<p>Changes since langchain-core==1.2.27</p>
<p>release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</p>
<h2>langchain-core==1.2.27</h2>
<p>Changes since langchain-core==1.2.26</p>
<p>release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)
chore: add comment explaining <code>pygments&gt;=2.20.0</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36570">#36570</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>.</p>
<h2>langchain-core==1.2.26</h2>
<p>Changes since langchain-core==1.2.25</p>
<p>release(core): 1.2.26 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36511">#36511</a>)
fix(core): add init validator and serialization mappings for Bedrock
models (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34510">#34510</a>)
feat(core): add <code>ChatBaseten</code> to serializable mapping (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36510">#36510</a>)
chore(core): drop <code>gpt-3.5-turbo</code> from docstrings (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36497">#36497</a>)
fix(core): correct parameter names in filter_messages docstring example
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36462">#36462</a>)</p>
<h2>langchain-core==1.2.25</h2>
<p>Changes since langchain-core==1.2.24</p>
<p>release(core): 1.2.25 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36473">#36473</a>)
fix(core): harden check for txt files in deprecated prompt loading
functions (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>)
fix(core): fixed typos in the documentation (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36459">#36459</a>)</p>
<p>Credit to Jeff Ponte (<a
href="https://github.com/JDP-Security"><code>@​JDP-Security</code></a>)
for reporting the symlink resolution issue resolved in <a
href="https://redirect.github.com/langchain-ai/langchain/issues/36471">#36471</a>.</p>
<h2>langchain-core==1.2.24</h2>
<p>Changes since langchain-core==1.2.23</p>
<p>release(core): 1.2.24 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36434">#36434</a>)
feat(core): impute placeholder filenames for OpenAI file inputs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36433">#36433</a>)
chore: pygments&gt;=2.20.0 across all packages (CVE-2026-4539) (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36385">#36385</a>)
fix(core): add &quot;computer&quot; to _WellKnownOpenAITools (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36261">#36261</a>)</p>
<h2>langchain-core==1.2.23</h2>
<p>Changes since langchain-core==1.2.22</p>
<p>release(core): 1.2.23 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36323">#36323</a>)
revert: Revert &quot;fix(core): trace invocation params in
metadata&quot; (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36322">#36322</a>)
chore: bump requests from 2.32.5 to 2.33.0 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36243">#36243</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/dd7c3eb3a4acfc834b038ec9dbde94478c66776e"><code>dd7c3eb</code></a>
release(core): release 1.2.28 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36614">#36614</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/af2ed47c6f008cdd551f3c0d87db3774c8dfe258"><code>af2ed47</code></a>
fix(core): add more sanitization to templates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36612">#36612</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7e5858d8078124f98f10102da21414689467c132"><code>7e5858d</code></a>
release(standard-tests): 1.1.6 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36610">#36610</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/fe99cb29123b704a90f5c8587a757def3b1471e0"><code>fe99cb2</code></a>
fix(standard-tests): update standard tests for sandbox backends (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36036">#36036</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/65bbd47cb2721c51ef8638f9e7da35247c4bfdde"><code>65bbd47</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36596">#36596</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/64864041168606535dfbd39055c0dca3dd61b5ba"><code>6486404</code></a>
release(core): 1.2.27 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36586">#36586</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/7629c747260cbaed7ca55466d5b9e1b520a7de77"><code>7629c74</code></a>
fix(core): handle symlinks in deprecated prompt save path (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36585">#36585</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/ce21bf469d7493f4716bc30feb15a5b3f16ebe1e"><code>ce21bf4</code></a>
ci: convert working-directory to validated dropdown (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36575">#36575</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/b8698eacbd2960c7e3195018f42992bf2c9d69c7"><code>b8698ea</code></a>
release(ollama): 1.1.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/36574">#36574</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3beba77e2e23d498fda07f9b8d6ba00aabfaf69f"><code>3beba77</code></a>
feat(ollama): support <code>response_format</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/34612">#34612</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.2.22...langchain-core==1.2.28">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langchain-core&package-manager=uv&previous-version=1.2.22&new-version=1.2.28)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/langchain-ai/langgraph/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 16:46:02 -07:00
1629794658 chore: update conformance lint (#7459)
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
2026-04-08 18:22:18 -07:00
194 changed files with 28995 additions and 14816 deletions
+2 -47
View File
@@ -9,9 +9,6 @@ on:
permissions:
contents: read
env:
GO_VERSION: "1.23"
jobs:
build:
runs-on: ubuntu-latest
@@ -54,23 +51,12 @@ jobs:
python-version: ${{ matrix.python-version }}
enable-cache: "false"
working-directory: libs/cli
- name: Set up Go
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install cli globally
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
run: pip install -e .
- name: Build Go CLI binary
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
run: make build-go
- name: Build service ${{ matrix.example.name }}
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
working-directory: ${{ matrix.example.workdir }}
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t ${{ matrix.example.tag }}
- name: Test service ${{ matrix.example.name }}
@@ -78,8 +64,6 @@ jobs:
working-directory: ${{ matrix.example.workdir }}
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
# Prepare environment file from local or parent example directory
if [ -f .env.example ]; then cp .env.example .env; elif [ -f ../.env.example ]; then cp ../.env.example .env && cp ../.env.example ../.env; fi
@@ -92,27 +76,18 @@ jobs:
- name: Build JS service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/js-examples
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t langgraph-test-e
- name: Build JS monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/js-monorepo-example
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json --build-command "yarn run turbo build" --install-command "yarn install"
- name: Build Python monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/python-monorepo-example
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
- name: Test Python monorepo service
@@ -120,8 +95,6 @@ jobs:
working-directory: libs/cli/python-monorepo-example
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
cp apps/agent/.env.example apps/agent/.env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env
@@ -130,9 +103,6 @@ jobs:
- name: Build prerelease reqs service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/examples/graph_prerelease_reqs
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t langgraph-test-h
- name: Test prerelease reqs service
@@ -140,8 +110,6 @@ jobs:
working-directory: libs/cli/examples/graph_prerelease_reqs
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
cp ../.env.example .env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
@@ -153,8 +121,8 @@ jobs:
exit 1
fi
LANGCHAIN_OPENAI_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-openai'); print(v);")
if [ "$LANGCHAIN_OPENAI_VERSION" != "1.0.1" ]; then
echo "LANGCHAIN_OPENAI_VERSION != 1.0.1; $LANGCHAIN_OPENAI_VERSION"
if [ "$LANGCHAIN_OPENAI_VERSION" != "1.1.14" ]; then
echo "LANGCHAIN_OPENAI_VERSION != 1.1.14; $LANGCHAIN_OPENAI_VERSION"
exit 1
fi
LANGCHAIN_ANTHROPIC_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-anthropic'); print(v);")
@@ -166,18 +134,12 @@ jobs:
- name: Build and test prerelease reqs fail service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/examples/graph_prerelease_reqs_fail
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t langgraph-test-i || [ $? -eq 1 ]
- name: Build uv simple service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/uv-examples/simple
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t langgraph-test-uv-simple
- name: Test uv simple service
@@ -185,8 +147,6 @@ jobs:
working-directory: libs/cli/uv-examples/simple
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
cp .env.example .env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
@@ -195,9 +155,6 @@ jobs:
- name: Build uv monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/uv-examples/monorepo/apps/agent
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
langgraph build -t langgraph-test-uv-monorepo
- name: Test uv monorepo service
@@ -205,8 +162,6 @@ jobs:
working-directory: libs/cli/uv-examples/monorepo/apps/agent
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: |
cp .env.example .env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
+1 -30
View File
@@ -11,9 +11,6 @@ on:
permissions:
contents: read
env:
GO_VERSION: "1.23"
jobs:
build:
runs-on: ubuntu-latest
@@ -29,25 +26,14 @@ jobs:
name: "test #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v6
- name: Get changed files
id: changed-files
if: ${{ inputs.working-directory == 'libs/cli' && github.event_name != 'workflow_dispatch' }}
uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0
with:
filter: "libs/cli/**"
- name: Set up Python ${{ matrix.python-version }}
uses: ./.github/actions/uv_setup
with:
python-version: ${{ matrix.python-version }}
cache-suffix: test-${{ inputs.working-directory }}
working-directory: ${{ inputs.working-directory }}
- name: Set up Go
if: ${{ inputs.working-directory == 'libs/cli' && (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Login to Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -58,26 +44,11 @@ jobs:
working-directory: ${{ inputs.working-directory }}
run: uv sync --frozen --group test --no-dev
- name: Build Go CLI binary
if: ${{ inputs.working-directory == 'libs/cli' && (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') }}
shell: bash
working-directory: ${{ inputs.working-directory }}
run: make build-go
- name: Run tests
shell: bash
working-directory: ${{ inputs.working-directory }}
run: make test
- name: Run Go CLI smoke tests
if: ${{ inputs.working-directory == 'libs/cli' && (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') }}
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
LANGGRAPH_USE_GO_CLI: "1"
LANGGRAPH_GO_CLI_PATH: ${{ github.workspace }}/libs/cli/langgraph_cli/bin/langgraph
run: TEST='tests/unit_tests/test_go_cli_smoke.py' make test
- name: Ensure the tests did not create any additional files
shell: bash
working-directory: ${{ inputs.working-directory }}
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
cache-suffix: "test-langgraph"
working-directory: libs/langgraph
- name: Login to Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
+1 -46
View File
@@ -10,7 +10,6 @@ on:
env:
PYTHON_VERSION: "3.10"
GO_VERSION: "1.23"
permissions:
contents: read
@@ -33,17 +32,6 @@ jobs:
cache-suffix: "release"
working-directory: ${{ inputs.working-directory }}
- name: Set up Go
if: inputs.working-directory == 'libs/cli'
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cross-compile Go binaries
if: inputs.working-directory == 'libs/cli'
working-directory: ${{ inputs.working-directory }}
run: make build-go-all GO_BIN_DIR=build/go-bin
# We want to keep this build stage *separate* from the release stage,
# so that there's no sharing of permissions between them.
# The release stage has trusted publishing and GitHub repo contents write access,
@@ -55,40 +43,7 @@ jobs:
# > It is strongly advised to separate jobs for building [...]
# > from the publish job.
# https://github.com/pypa/gh-action-pypi-publish#non-goals
- name: Build CLI platform wheels
if: inputs.working-directory == 'libs/cli'
working-directory: ${{ inputs.working-directory }}
run: |
TARGETS=(
"linux-amd64 manylinux_2_17_x86_64.manylinux2014_x86_64"
"linux-arm64 manylinux_2_17_aarch64.manylinux2014_aarch64"
"linux-arm manylinux_2_17_armv7l.manylinux2014_armv7l"
"linux-386 manylinux_2_17_i686.manylinux2014_i686"
"linux-ppc64le manylinux_2_17_ppc64le.manylinux2014_ppc64le"
"linux-s390x manylinux_2_17_s390x.manylinux2014_s390x"
"linux-amd64 musllinux_1_2_x86_64"
"linux-arm64 musllinux_1_2_aarch64"
"linux-arm musllinux_1_2_armv7l"
"linux-386 musllinux_1_2_i686"
"darwin-amd64 macosx_11_0_x86_64"
"darwin-arm64 macosx_11_0_arm64"
"windows-amd64 win_amd64"
"windows-arm64 win_arm64"
"windows-386 win32"
)
for entry in "${TARGETS[@]}"; do
key=$(echo "$entry" | awk '{print $1}')
plat=$(echo "$entry" | awk '{print $2}')
ext=""
if [[ "$key" == windows-* ]]; then ext=".exe"; fi
binary="build/go-bin/langgraph-${key}${ext}"
LANGGRAPH_GO_BINARY="$binary" LANGGRAPH_WHEEL_PLAT="$plat" uv build --wheel
done
rm -rf langgraph_cli/bin
uv build
- name: Build project for distribution
if: inputs.working-directory != 'libs/cli'
run: uv build
working-directory: ${{ inputs.working-directory }}
@@ -127,7 +82,7 @@ jobs:
path: ${{ inputs.working-directory }}/dist/
- name: Publish to test PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
with:
packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true
+1 -1
View File
@@ -57,7 +57,7 @@ jobs:
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Annotation
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const file = JSON.parse(`${{ steps.files.outputs.added_modified_renamed }}`)[0]
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
uses: actions/configure-pages@v6
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@v5
with:
path: 'docs/_site'
+1 -75
View File
@@ -13,7 +13,6 @@ permissions:
env:
PYTHON_VERSION: "3.11"
GO_VERSION: "1.23"
jobs:
build:
@@ -35,71 +34,6 @@ jobs:
cache-suffix: "release"
working-directory: ${{ inputs.working-directory }}
# -- Go cross-compilation (libs/cli only) --
# When releasing the CLI, we cross-compile the Go binary for each
# platform and build platform-specific wheels that bundle it.
- name: Set up Go
if: inputs.working-directory == 'libs/cli'
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cross-compile Go binaries
if: inputs.working-directory == 'libs/cli'
working-directory: ${{ inputs.working-directory }}
run: make build-go-all GO_BIN_DIR=build/go-bin
- name: Run Go tests
if: inputs.working-directory == 'libs/cli'
working-directory: ${{ inputs.working-directory }}
run: go test -count=1 -race ./...
- name: Build CLI platform wheels
if: inputs.working-directory == 'libs/cli'
working-directory: ${{ inputs.working-directory }}
run: |
# Each entry: GO_BINARY_KEY WHEEL_PLATFORM_TAG
#
# Since Go builds are statically linked (CGO_ENABLED=0), the same
# linux binary works on both glibc and musl. We produce separate
# manylinux and musllinux wheels so pip installs the right one.
TARGETS=(
# Linux glibc
"linux-amd64 manylinux_2_17_x86_64.manylinux2014_x86_64"
"linux-arm64 manylinux_2_17_aarch64.manylinux2014_aarch64"
"linux-arm manylinux_2_17_armv7l.manylinux2014_armv7l"
"linux-386 manylinux_2_17_i686.manylinux2014_i686"
"linux-ppc64le manylinux_2_17_ppc64le.manylinux2014_ppc64le"
"linux-s390x manylinux_2_17_s390x.manylinux2014_s390x"
# Linux musl (same Go binaries, different wheel tag)
"linux-amd64 musllinux_1_2_x86_64"
"linux-arm64 musllinux_1_2_aarch64"
"linux-arm musllinux_1_2_armv7l"
"linux-386 musllinux_1_2_i686"
# macOS
"darwin-amd64 macosx_11_0_x86_64"
"darwin-arm64 macosx_11_0_arm64"
# Windows
"windows-amd64 win_amd64"
"windows-arm64 win_arm64"
"windows-386 win32"
)
for entry in "${TARGETS[@]}"; do
key=$(echo "$entry" | awk '{print $1}')
plat=$(echo "$entry" | awk '{print $2}')
ext=""
if [[ "$key" == windows-* ]]; then ext=".exe"; fi
binary="build/go-bin/langgraph-${key}${ext}"
echo "Building wheel for ${key} -> ${plat}..."
LANGGRAPH_GO_BINARY="$binary" LANGGRAPH_WHEEL_PLAT="$plat" uv build --wheel
done
# Also build the sdist and pure-Python fallback wheel
rm -rf langgraph_cli/bin
uv build
echo "All wheels built:"
ls -lh dist/
# -- Standard build (all other packages) --
# We want to keep this build stage *separate* from the release stage,
# so that there's no sharing of permissions between them.
# The release stage has trusted publishing and GitHub repo contents write access,
@@ -112,7 +46,6 @@ jobs:
# > from the publish job.
# https://github.com/pypa/gh-action-pypi-publish#non-goals
- name: Build project for distribution
if: inputs.working-directory != 'libs/cli'
run: uv build
working-directory: ${{ inputs.working-directory }}
@@ -308,13 +241,6 @@ jobs:
run: make test
working-directory: ${{ inputs.working-directory }}
- name: Run Go CLI smoke tests
if: inputs.working-directory == 'libs/cli'
working-directory: ${{ inputs.working-directory }}
env:
LANGGRAPH_USE_GO_CLI: "1"
run: uv run pytest tests/unit_tests/test_go_cli_smoke.py
publish:
needs:
- build
@@ -350,7 +276,7 @@ jobs:
path: ${{ inputs.working-directory }}/dist/
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
with:
packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Find and reopen matching PRs
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
+4 -4
View File
@@ -50,7 +50,7 @@ jobs:
steps:
- name: Check for issue link and assignee
id: check-link
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
@@ -283,7 +283,7 @@ jobs:
if: >-
env.ENFORCE_ISSUE_LINK == 'true' &&
(steps.check-link.outputs.has-link != 'true' || steps.check-link.outputs.is-assigned != 'true')
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
@@ -312,7 +312,7 @@ jobs:
if: >-
env.ENFORCE_ISSUE_LINK == 'true' &&
steps.check-link.outputs.has-link == 'true' && steps.check-link.outputs.is-assigned == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
@@ -366,7 +366,7 @@ jobs:
if: >-
env.ENFORCE_ISSUE_LINK == 'true' &&
(steps.check-link.outputs.has-link != 'true' || steps.check-link.outputs.is-assigned != 'true')
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
+5 -5
View File
@@ -63,7 +63,7 @@ jobs:
- name: Check if contributor is external
if: steps.app-token.outcome == 'success'
id: check-membership
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
@@ -102,7 +102,7 @@ jobs:
- name: Apply contributor tier label
if: steps.check-membership.outputs.is-external == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -155,7 +155,7 @@ jobs:
- name: Add external label
if: steps.check-membership.outputs.is-external == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -168,7 +168,7 @@ jobs:
- name: Add internal label
if: steps.check-membership.outputs.is-external == 'false'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -196,7 +196,7 @@ jobs:
private-key: ${{ secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }}
- name: Backfill labels
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
+4 -4
View File
@@ -48,7 +48,7 @@ jobs:
- name: Check if contributor is external
if: steps.app-token.outcome == 'success'
id: check-membership
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
@@ -90,7 +90,7 @@ jobs:
# event fires and triggers require_issue_link.yml.
- name: Apply contributor tier label
if: steps.check-membership.outputs.is-external == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
# Use App token so the "labeled" event propagates to downstream
# workflows (e.g. require_issue_link.yml).
@@ -145,7 +145,7 @@ jobs:
- name: Add external label
if: steps.check-membership.outputs.is-external == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
# Use App token so the "labeled" event propagates to downstream
# workflows (e.g. require_issue_link.yml). Events created by the
@@ -161,7 +161,7 @@ jobs:
- name: Add internal label
if: steps.check-membership.outputs.is-external == 'false'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
run: make lock-upgrade
- name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "chore(deps): upgrade dependencies with `uv lock --upgrade`"
+1
View File
@@ -100,3 +100,4 @@ dmypy.json
.turbo
.editorconfig
.scratch
.worktrees/
+5 -5
View File
@@ -16,7 +16,7 @@
<a href="https://opensource.org/licenses/MIT" target="_blank"><img src="https://img.shields.io/pypi/l/langgraph" alt="PyPI - License"></a>
<a href="https://pypistats.org/packages/langgraph" target="_blank"><img src="https://img.shields.io/pepy/dt/langgraph" alt="PyPI - Downloads"></a>
<a href="https://pypi.org/project/langgraph/" target="_blank"><img src="https://img.shields.io/pypi/v/langgraph.svg?label=%20" alt="Version"></a>
<a href="https://x.com/langchain" target="_blank"><img src="https://img.shields.io/twitter/url/https/twitter.com/langchain.svg?style=social&label=Follow%20%40LangChain" alt="Twitter / X"></a>
<a href="https://x.com/langchain_oss" target="_blank"><img src="https://img.shields.io/twitter/url/https/twitter.com/langchain_oss.svg?style=social&label=Follow%20%40LangChain" alt="Twitter / X"></a>
</div>
<br>
@@ -27,10 +27,10 @@ Trusted by companies shaping the future of agents including Klarna, Replit,
pip install -U langgraph
```
If you're looking to quickly build agents with LangChain's `create_agent` (built on LangGraph), check out the [LangChain Agents documentation](https://docs.langchain.com/oss/python/langchain/agents).
> [!TIP]
> If you're looking to quickly build agents, check out **[Deep Agents](https://docs.langchain.com/oss/python/deepagents/overview)** — a higher-level package built on LangGraph for agents that can plan, use subagents, and leverage file systems for complex tasks.
> [!NOTE]
> Looking for the JS/TS library? Check out [LangGraph.js](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://docs.langchain.com/oss/javascript/langgraph/overview).
For an equivalent JS/TS library, check out [LangGraph.js](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://docs.langchain.com/oss/javascript/langgraph/overview).
## Why use LangGraph?
@@ -51,7 +51,7 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
To improve your LLM application development, pair LangGraph with:
- [Deep Agents](https://github.com/langchain-ai/deepagents) *(new!)* Build agents that can plan, use subagents, and leverage file systems for complex tasks.
- [Deep Agents](https://docs.langchain.com/oss/python/deepagents/overview) Build agents that can plan, use subagents, and leverage file systems for complex tasks.
- [LangChain](https://docs.langchain.com/oss/python/langchain/overview) Provides integrations and composable components to streamline LLM application development.
- [LangSmith](https://www.langchain.com/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](https://docs.langchain.com/langsmith/deployments) 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](https://docs.langchain.com/langsmith/studio).
+1
View File
@@ -63,6 +63,7 @@ The suite tests **base** capabilities (required) and **extended** capabilities (
| `delete_for_runs` | no | `adelete_for_runs` |
| `copy_thread` | no | `acopy_thread` |
| `prune` | no | `aprune` |
| `delta_channel_history` | no | `aget_delta_channel_history` |
Extended capabilities are detected by checking whether the method is overridden from `BaseCheckpointSaver`. If not overridden, those tests are skipped.
@@ -23,6 +23,7 @@ class Capability(str, Enum):
DELETE_FOR_RUNS = "delete_for_runs"
COPY_THREAD = "copy_thread"
PRUNE = "prune"
DELTA_CHANNEL_HISTORY = "delta_channel_history"
# Capabilities that every checkpointer must support.
@@ -42,6 +43,7 @@ EXTENDED_CAPABILITIES = frozenset(
Capability.DELETE_FOR_RUNS,
Capability.COPY_THREAD,
Capability.PRUNE,
Capability.DELTA_CHANNEL_HISTORY,
}
)
@@ -57,6 +59,7 @@ _CAPABILITY_METHOD_MAP: dict[Capability, str] = {
Capability.DELETE_FOR_RUNS: "adelete_for_runs",
Capability.COPY_THREAD: "acopy_thread",
Capability.PRUNE: "aprune",
Capability.DELTA_CHANNEL_HISTORY: "aget_delta_channel_history",
}
@@ -9,6 +9,9 @@ from langgraph.checkpoint.conformance.spec.test_delete_for_runs import (
from langgraph.checkpoint.conformance.spec.test_delete_thread import (
run_delete_thread_tests,
)
from langgraph.checkpoint.conformance.spec.test_delta_channel_history import (
run_delta_channel_history_tests,
)
from langgraph.checkpoint.conformance.spec.test_get_tuple import run_get_tuple_tests
from langgraph.checkpoint.conformance.spec.test_list import run_list_tests
from langgraph.checkpoint.conformance.spec.test_prune import run_prune_tests
@@ -24,4 +27,5 @@ __all__ = [
"run_delete_for_runs_tests",
"run_copy_thread_tests",
"run_prune_tests",
"run_delta_channel_history_tests",
]
@@ -0,0 +1,99 @@
"""Shared fixtures for delta-channel conformance tests.
Builds a parent chain with `_DeltaSnapshot` blobs at known positions via
direct `aput` / `aput_writes` calls. No langgraph or Pregel dependency.
"""
from __future__ import annotations
from collections.abc import Sequence
from typing import Any
from uuid import uuid4
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.base import BaseCheckpointSaver, Checkpoint
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.conformance.test_utils import generate_metadata
async def build_delta_chain(
saver: BaseCheckpointSaver,
*,
thread_id: str | None = None,
checkpoint_ns: str = "",
channel: str = "messages",
snapshots_at_steps: Sequence[int] = (0,),
total_steps: int = 6,
write_value_fn: Any | None = None,
) -> list[RunnableConfig]:
"""Build a parent chain with `_DeltaSnapshot` at known positions.
Args:
saver: Checkpointer instance.
thread_id: Defaults to a random UUID.
checkpoint_ns: Namespace (default root).
channel: Channel name used for snapshots and writes.
snapshots_at_steps: Steps at which a `_DeltaSnapshot` blob is stored
in `channel_values[channel]`. Step 0 is the oldest checkpoint.
total_steps: Number of checkpoints in the chain.
write_value_fn: Callable(step) -> write value. Defaults to step index.
Returns:
List of stored configs (oldest first), one per step.
"""
if write_value_fn is None:
def write_value_fn(step: int) -> Any:
return step
from langgraph.checkpoint.serde.types import _DeltaSnapshot
thread_id = thread_id or str(uuid4())
snapshot_set = set(snapshots_at_steps)
stored: list[RunnableConfig] = []
parent_cfg: RunnableConfig | None = None
for step in range(total_steps):
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": checkpoint_ns,
}
}
if parent_cfg:
config["configurable"]["checkpoint_id"] = parent_cfg["configurable"][
"checkpoint_id"
]
channel_values: dict[str, Any] = {}
channel_versions: dict[str, int] = {}
if step in snapshot_set:
channel_values[channel] = _DeltaSnapshot(
write_value_fn(step),
)
channel_versions[channel] = step + 1
cp = Checkpoint(
v=1,
id=str(uuid6(clock_seq=-1)),
ts="",
channel_values=channel_values,
channel_versions=channel_versions,
versions_seen={},
updated_channels=None,
)
new_versions = dict(channel_versions)
parent_cfg = await saver.aput(
config, cp, generate_metadata(step=step), new_versions
)
stored.append(parent_cfg)
# Write a pending write for non-snapshot steps so the walk has
# something to collect.
if step not in snapshot_set:
await saver.aput_writes(
parent_cfg, [(channel, write_value_fn(step))], str(uuid4())
)
return stored
@@ -0,0 +1,247 @@
"""DELTA_CHANNEL_HISTORY capability tests — aget_delta_channel_history contract."""
from __future__ import annotations
import traceback
from collections.abc import Callable
from uuid import uuid4
from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.checkpoint.conformance.spec._delta_fixtures import build_delta_chain
async def test_history_returns_writes_oldest_first(
saver: BaseCheckpointSaver,
) -> None:
"""Writes are returned oldest-to-newest."""
tid = str(uuid4())
# 5 steps: snapshot at 0, writes at 1,2,3,4.
# Head is step 4. Walk starts at step 3 (parent of head).
# Collects writes from steps 1,2,3 (between snapshot at 0 and head's parent).
configs = await build_delta_chain(
saver, thread_id=tid, channel="ch", snapshots_at_steps=[0], total_steps=5
)
head = configs[-1]
result = await saver.aget_delta_channel_history(config=head, channels=["ch"])
writes = result["ch"]["writes"]
values = [w[2] for w in writes]
assert values == [1, 2, 3], f"Expected [1,2,3], got {values}"
async def test_history_seed_is_nearest_snapshot(
saver: BaseCheckpointSaver,
) -> None:
"""Seed is the value from the nearest ancestor with channel_values populated."""
tid = str(uuid4())
# 6 steps: snapshots at 0 and 3, writes at 1,2,4,5.
# Head is step 5. Walk from step 4 backward stops at step 3 (snapshot).
# Collects writes from step 4 only (between step 3 and head's parent step 4).
configs = await build_delta_chain(
saver,
thread_id=tid,
channel="ch",
snapshots_at_steps=[0, 3],
total_steps=6,
)
head = configs[-1]
result = await saver.aget_delta_channel_history(config=head, channels=["ch"])
assert "seed" in result["ch"], "Expected seed from snapshot at step 3"
seed = result["ch"]["seed"]
from langgraph.checkpoint.serde.types import _DeltaSnapshot
actual_value = seed.value if isinstance(seed, _DeltaSnapshot) else seed
assert actual_value == 3, f"Expected seed value 3 (step 3), got {actual_value}"
writes = result["ch"]["writes"]
values = [w[2] for w in writes]
assert values == [4], f"Expected [4], got {values}"
async def test_history_excludes_target_pending_writes(
saver: BaseCheckpointSaver,
) -> None:
"""Target's own pending_writes are NOT included in the history."""
tid = str(uuid4())
configs = await build_delta_chain(
saver, thread_id=tid, channel="ch", snapshots_at_steps=[0], total_steps=3
)
head = configs[-1]
# Add writes directly to the head checkpoint
await saver.aput_writes(head, [("ch", "extra")], str(uuid4()))
result = await saver.aget_delta_channel_history(config=head, channels=["ch"])
writes = result["ch"]["writes"]
values = [w[2] for w in writes]
assert "extra" not in values, f"Target's writes should be excluded, got {values}"
async def test_history_multi_channel(
saver: BaseCheckpointSaver,
) -> None:
"""Multiple channels have independent walk termination."""
tid = str(uuid4())
configs: list = []
parent_cfg = None
from langgraph.checkpoint.base import Checkpoint
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.serde.types import _DeltaSnapshot
from langgraph.checkpoint.conformance.test_utils import generate_metadata
for step in range(5):
config = {"configurable": {"thread_id": tid, "checkpoint_ns": ""}}
if parent_cfg:
config["configurable"]["checkpoint_id"] = parent_cfg["configurable"][
"checkpoint_id"
]
cv: dict = {}
cvs: dict = {}
if step == 1:
cv["a"] = _DeltaSnapshot("snap_a")
cvs["a"] = step + 1
if step == 3:
cv["b"] = _DeltaSnapshot("snap_b")
cvs["b"] = step + 1
cp = Checkpoint(
v=1,
id=str(uuid6(clock_seq=-1)),
ts="",
channel_values=cv,
channel_versions=cvs,
versions_seen={},
updated_channels=None,
)
parent_cfg = await saver.aput(config, cp, generate_metadata(step=step), cvs)
configs.append(parent_cfg)
await saver.aput_writes(parent_cfg, [("a", step), ("b", step)], str(uuid4()))
head = configs[-1]
result = await saver.aget_delta_channel_history(config=head, channels=["a", "b"])
a_writes = [w[2] for w in result["a"]["writes"]]
b_writes = [w[2] for w in result["b"]["writes"]]
assert a_writes == [1, 2, 3], f"Expected a writes [1,2,3], got {a_writes}"
assert b_writes == [3], f"Expected b writes [3], got {b_writes}"
async def test_history_empty_channels_returns_empty(
saver: BaseCheckpointSaver,
) -> None:
"""Empty channels list returns empty mapping."""
tid = str(uuid4())
configs = await build_delta_chain(
saver, thread_id=tid, channel="ch", snapshots_at_steps=[0], total_steps=3
)
result = await saver.aget_delta_channel_history(config=configs[-1], channels=[])
assert result == {}
async def test_history_walk_to_root_no_seed(
saver: BaseCheckpointSaver,
) -> None:
"""Walk reaches root without finding seed — no 'seed' key in result."""
tid = str(uuid4())
configs = await build_delta_chain(
saver,
thread_id=tid,
channel="ch",
snapshots_at_steps=[],
total_steps=4,
)
head = configs[-1]
result = await saver.aget_delta_channel_history(config=head, channels=["ch"])
assert "seed" not in result["ch"], f"Expected no seed, got {result['ch']}"
async def test_history_migration_plain_value_as_seed(
saver: BaseCheckpointSaver,
) -> None:
"""Pre-delta plain value in channel_values acts as seed (migration case).
When a thread was originally using a regular channel (BinaryOperatorAggregate)
and later switches to DeltaChannel, the old checkpoint has a plain value in
channel_values[ch] (not a _DeltaSnapshot). The walk should treat it as the
seed and terminate there.
"""
from langgraph.checkpoint.base import Checkpoint
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.conformance.test_utils import generate_metadata
tid = str(uuid4())
configs: list = []
parent_cfg = None
for step in range(4):
config = {"configurable": {"thread_id": tid, "checkpoint_ns": ""}}
if parent_cfg:
config["configurable"]["checkpoint_id"] = parent_cfg["configurable"][
"checkpoint_id"
]
cv: dict = {}
cvs: dict = {}
# Step 1: plain value (migration case — old checkpoint before delta)
if step == 1:
cv["ch"] = [10, 20, 30]
cvs["ch"] = step + 1
cp = Checkpoint(
v=1,
id=str(uuid6(clock_seq=-1)),
ts="",
channel_values=cv,
channel_versions=cvs,
versions_seen={},
updated_channels=None,
)
parent_cfg = await saver.aput(config, cp, generate_metadata(step=step), cvs)
configs.append(parent_cfg)
if step != 1:
await saver.aput_writes(parent_cfg, [("ch", step)], str(uuid4()))
head = configs[-1]
result = await saver.aget_delta_channel_history(config=head, channels=["ch"])
# Seed should be the plain value from step 1
assert "seed" in result["ch"], "Expected seed from migration plain value at step 1"
seed = result["ch"]["seed"]
assert seed == [10, 20, 30], f"Expected plain value [10,20,30], got {seed}"
# Writes should be from step 2 only (between seed at step 1 and head's parent step 2)
writes = result["ch"]["writes"]
values = [w[2] for w in writes]
assert values == [2], f"Expected [2], got {values}"
ALL_DELTA_CHANNEL_HISTORY_TESTS = [
test_history_returns_writes_oldest_first,
test_history_seed_is_nearest_snapshot,
test_history_excludes_target_pending_writes,
test_history_multi_channel,
test_history_empty_channels_returns_empty,
test_history_walk_to_root_no_seed,
test_history_migration_plain_value_as_seed,
]
async def run_delta_channel_history_tests(
saver: BaseCheckpointSaver,
on_test_result: Callable[[str, str, bool, str | None], None] | None = None,
) -> tuple[int, int, list[str]]:
"""Run all delta_channel_history tests. Returns (passed, failed, failure_names)."""
passed = 0
failed = 0
failures: list[str] = []
for test_fn in ALL_DELTA_CHANNEL_HISTORY_TESTS:
try:
await test_fn(saver)
passed += 1
if on_test_result:
on_test_result("delta_channel_history", test_fn.__name__, True, None)
except Exception:
failed += 1
msg = f"{test_fn.__name__}: {traceback.format_exc()}"
failures.append(msg)
if on_test_result:
on_test_result(
"delta_channel_history",
test_fn.__name__,
False,
traceback.format_exc(),
)
return passed, failed, failures
@@ -339,7 +339,6 @@ async def test_list_metadata_custom_keys(
assert results[0].metadata["run_id"] == "run-abc"
ALL_LIST_TESTS = [
test_list_all,
test_list_by_thread,
@@ -19,6 +19,9 @@ from langgraph.checkpoint.conformance.spec.test_delete_for_runs import (
from langgraph.checkpoint.conformance.spec.test_delete_thread import (
run_delete_thread_tests,
)
from langgraph.checkpoint.conformance.spec.test_delta_channel_history import (
run_delta_channel_history_tests,
)
from langgraph.checkpoint.conformance.spec.test_get_tuple import run_get_tuple_tests
from langgraph.checkpoint.conformance.spec.test_list import run_list_tests
from langgraph.checkpoint.conformance.spec.test_prune import run_prune_tests
@@ -35,6 +38,7 @@ _RUNNERS = {
Capability.DELETE_FOR_RUNS: run_delete_for_runs_tests,
Capability.COPY_THREAD: run_copy_thread_tests,
Capability.PRUNE: run_prune_tests,
Capability.DELTA_CHANNEL_HISTORY: run_delta_channel_history_tests,
}
@@ -43,7 +43,11 @@ asyncio_mode = "auto"
# The extended methods (acopy_thread, adelete_for_runs, aprune) are checked
# at runtime via capability detection and may not exist on the installed
# base class. Dict literal inference is also overly strict for RunnableConfig.
# Delta-channel tests import from `langgraph` (not a declared dep of this
# package — at test time it is installed alongside); private `_DeltaSnapshot`
# imports are intentional (beta surface).
unresolved-attribute = "ignore"
unresolved-import = "ignore"
invalid-argument-type = "ignore"
invalid-return-type = "ignore"
@@ -58,6 +62,9 @@ lint.select = [
lint.ignore = ["E501", "B008"]
target-version = "py310"
[tool.uv.sources]
langgraph-checkpoint = {path = "../checkpoint", editable = true}
[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
+628 -527
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -6,6 +6,11 @@ Implementation of LangGraph CheckpointSaver that uses Postgres.
By default `langgraph-checkpoint-postgres` installs `psycopg` (Psycopg 3) without any extras. However, you can choose a specific installation that best suits your needs [here](https://www.psycopg.org/psycopg3/docs/basic/install.html) (for example, `psycopg[binary]`).
## Security
> [!IMPORTANT]
> Set `LANGGRAPH_STRICT_MSGPACK=true` or pass an explicit `allowed_msgpack_modules` list when creating your checkpointer. This restricts checkpoint deserialization to known-safe types, preventing code execution if the database is compromised. See the [langgraph-checkpoint README](https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint#serde) for details.
## Usage
> [!IMPORTANT]
@@ -2,9 +2,9 @@ from __future__ import annotations
import threading
from collections import defaultdict
from collections.abc import Iterator, Sequence
from collections.abc import Iterator, Mapping, Sequence
from contextlib import contextmanager
from typing import Any
from typing import Any, cast
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.base import (
@@ -13,17 +13,25 @@ from langgraph.checkpoint.base import (
Checkpoint,
CheckpointMetadata,
CheckpointTuple,
DeltaChannelHistory,
get_checkpoint_id,
get_serializable_checkpoint_metadata,
)
from langgraph.checkpoint.serde.base import SerializerProtocol
from langgraph.checkpoint.serde.types import _DeltaSnapshot
from psycopg import Capabilities, Connection, Cursor, Pipeline
from psycopg.rows import DictRow, dict_row
from psycopg.types.json import Jsonb
from psycopg_pool import ConnectionPool
from langgraph.checkpoint.postgres import _internal
from langgraph.checkpoint.postgres.base import BasePostgresSaver
from langgraph.checkpoint.postgres.base import (
_DELTA_PAGE_SIZE,
BasePostgresSaver,
_build_delta_stage1_sql,
_build_delta_stage2_sql,
_DeltaStage2Row,
)
from langgraph.checkpoint.postgres.shallow import ShallowPostgresSaver
Conn = _internal.Conn # For backward compatibility
@@ -302,7 +310,10 @@ class PostgresSaver(BasePostgresSaver):
# others are stored in blobs table
blob_values = {}
for k, v in checkpoint["channel_values"].items():
if v is None or isinstance(v, (str, int, float, bool)):
if isinstance(v, _DeltaSnapshot):
blob_values[k] = copy["channel_values"].pop(k)
copy["channel_values"][k] = True
elif v is None or isinstance(v, (str, int, float, bool)):
pass
else:
blob_values[k] = copy["channel_values"].pop(k)
@@ -430,6 +441,114 @@ class PostgresSaver(BasePostgresSaver):
with conn.cursor(binary=True, row_factory=dict_row) as cur:
yield cur
def get_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Fast-path override of `BaseCheckpointSaver.get_delta_channel_history`.
Two-stage query, both stages cover ALL requested channels:
* Stage 1 (paged): dynamic SELECT over `checkpoints` with K parallel
JSONB key lookups (one column pair per channel) — no subquery, no
aggregation. Pages newest-first by `checkpoint_id` with a cursor;
page size is `_DELTA_PAGE_SIZE`. Stops paging when every channel
has found its seed or the chain is exhausted.
* Stage 2 (per-channel UNION ALL): one branch per channel reading
`checkpoint_writes` filtered to that channel's specific
`chain_cids`, plus one branch per channel that has a seed reading
`checkpoint_blobs` for that channel + version. Avoids the
over-fetch of a single `channel = ANY(channels)` filter when
channels have different chain depths.
"""
if not channels:
return {}
channels = list(channels)
thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_id = get_checkpoint_id(config)
if checkpoint_id is None:
target = self.get_tuple(config)
if target is None:
return {ch: {"writes": []} for ch in channels}
checkpoint_id = target.config["configurable"]["checkpoint_id"]
# Stage 1: paged K-JSONB-lookup scan, walking the parent chain in
# Python after each page. Stops as soon as every channel has its seed.
stage1_sql = _build_delta_stage1_sql(channels, paged=True)
parent_of: dict[str, str | None] = {}
ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels]
hs_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels]
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels}
walk_cursor_by_ch: dict[str, str | None] = {}
seeded: set[str] = set()
cursor: str | None = None
with self._cursor() as cur:
while True:
stage1_params: list[Any] = []
for ch in channels:
stage1_params.extend([ch, ch])
stage1_params.extend(
[thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE]
)
cur.execute(stage1_sql, stage1_params)
page = cur.fetchall()
if not page:
break
oldest = self._ingest_stage1_page(
cast("list[Mapping[str, Any]]", page),
channels,
parent_of,
ver_by_i_by_cid,
hs_by_i_by_cid,
)
self._try_advance_walks(
checkpoint_id,
channels,
parent_of,
ver_by_i_by_cid,
hs_by_i_by_cid,
chain_by_ch,
seed_ver_by_ch,
walk_cursor_by_ch,
seeded,
)
# Stop if every channel is seeded, or the page was short
# (chain exhausted — no more rows to fetch).
if len(seeded) == len(channels) or len(page) < _DELTA_PAGE_SIZE:
break
cursor = oldest
# Stage 2: per-channel UNION ALL — one writes branch per channel
# with non-empty chain, plus one blob branch per seeded channel.
channels_with_chain = [ch for ch in channels if chain_by_ch[ch]]
channels_with_seed = [ch for ch in channels if seed_ver_by_ch[ch] is not None]
stage2_sql = _build_delta_stage2_sql(
channels_with_chain=channels_with_chain,
channels_with_seed=channels_with_seed,
)
if stage2_sql:
stage2_params: list[Any] = []
for ch in channels_with_chain:
stage2_params.extend([thread_id, checkpoint_ns, ch, chain_by_ch[ch]])
for ch in channels_with_seed:
stage2_params.extend([thread_id, checkpoint_ns, ch, seed_ver_by_ch[ch]])
with self._cursor() as cur:
cur.execute(stage2_sql, stage2_params)
stage2_rows = cur.fetchall()
else:
stage2_rows = []
return self._build_delta_channels_writes_history(
channels=channels,
chain_by_ch=chain_by_ch,
seed_ver_by_ch=seed_ver_by_ch,
stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows),
)
def _load_checkpoint_tuple(self, value: DictRow) -> CheckpointTuple:
"""
Convert a database row into a CheckpointTuple object.
@@ -2,9 +2,9 @@ from __future__ import annotations
import asyncio
from collections import defaultdict
from collections.abc import AsyncIterator, Iterator, Sequence
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
from contextlib import asynccontextmanager
from typing import Any
from typing import Any, cast
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.base import (
@@ -13,17 +13,25 @@ from langgraph.checkpoint.base import (
Checkpoint,
CheckpointMetadata,
CheckpointTuple,
DeltaChannelHistory,
get_checkpoint_id,
get_serializable_checkpoint_metadata,
)
from langgraph.checkpoint.serde.base import SerializerProtocol
from langgraph.checkpoint.serde.types import _DeltaSnapshot
from psycopg import AsyncConnection, AsyncCursor, AsyncPipeline, Capabilities
from psycopg.rows import DictRow, dict_row
from psycopg.types.json import Jsonb
from psycopg_pool import AsyncConnectionPool
from langgraph.checkpoint.postgres import _ainternal
from langgraph.checkpoint.postgres.base import BasePostgresSaver
from langgraph.checkpoint.postgres.base import (
_DELTA_PAGE_SIZE,
BasePostgresSaver,
_build_delta_stage1_sql,
_build_delta_stage2_sql,
_DeltaStage2Row,
)
from langgraph.checkpoint.postgres.shallow import AsyncShallowPostgresSaver
Conn = _ainternal.Conn # For backward compatibility
@@ -261,7 +269,10 @@ class AsyncPostgresSaver(BasePostgresSaver):
# others are stored in blobs table
blob_values = {}
for k, v in checkpoint["channel_values"].items():
if v is None or isinstance(v, (str, int, float, bool)):
if isinstance(v, _DeltaSnapshot):
blob_values[k] = copy["channel_values"].pop(k)
copy["channel_values"][k] = True
elif v is None or isinstance(v, (str, int, float, bool)):
pass
else:
blob_values[k] = copy["channel_values"].pop(k)
@@ -391,6 +402,97 @@ class AsyncPostgresSaver(BasePostgresSaver):
async with conn.cursor(binary=True, row_factory=dict_row) as cur:
yield cur
async def aget_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Fast-path override of `BaseCheckpointSaver.aget_delta_channel_history`.
See `PostgresSaver.get_delta_channel_history` for design notes; this is
the async equivalent with internal stage-1 paging and per-channel
UNION ALL stage-2.
"""
if not channels:
return {}
channels = list(channels)
thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_id = get_checkpoint_id(config)
if checkpoint_id is None:
target = await self.aget_tuple(config)
if target is None:
return {ch: {"writes": []} for ch in channels}
checkpoint_id = target.config["configurable"]["checkpoint_id"]
stage1_sql = _build_delta_stage1_sql(channels, paged=True)
parent_of: dict[str, str | None] = {}
ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels]
hs_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels]
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels}
walk_cursor_by_ch: dict[str, str | None] = {}
seeded: set[str] = set()
cursor: str | None = None
async with self._cursor() as cur:
while True:
stage1_params: list[Any] = []
for ch in channels:
stage1_params.extend([ch, ch])
stage1_params.extend(
[thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE]
)
await cur.execute(stage1_sql, stage1_params)
page = await cur.fetchall()
if not page:
break
oldest = self._ingest_stage1_page(
cast("list[Mapping[str, Any]]", page),
channels,
parent_of,
ver_by_i_by_cid,
hs_by_i_by_cid,
)
self._try_advance_walks(
checkpoint_id,
channels,
parent_of,
ver_by_i_by_cid,
hs_by_i_by_cid,
chain_by_ch,
seed_ver_by_ch,
walk_cursor_by_ch,
seeded,
)
if len(seeded) == len(channels) or len(page) < _DELTA_PAGE_SIZE:
break
cursor = oldest
channels_with_chain = [ch for ch in channels if chain_by_ch[ch]]
channels_with_seed = [ch for ch in channels if seed_ver_by_ch[ch] is not None]
stage2_sql = _build_delta_stage2_sql(
channels_with_chain=channels_with_chain,
channels_with_seed=channels_with_seed,
)
if stage2_sql:
stage2_params: list[Any] = []
for ch in channels_with_chain:
stage2_params.extend([thread_id, checkpoint_ns, ch, chain_by_ch[ch]])
for ch in channels_with_seed:
stage2_params.extend([thread_id, checkpoint_ns, ch, seed_ver_by_ch[ch]])
async with self._cursor() as cur:
await cur.execute(stage2_sql, stage2_params)
stage2_rows = await cur.fetchall()
else:
stage2_rows = []
return self._build_delta_channels_writes_history(
channels=channels,
chain_by_ch=chain_by_ch,
seed_ver_by_ch=seed_ver_by_ch,
stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows),
)
async def _load_checkpoint_tuple(self, value: DictRow) -> CheckpointTuple:
"""
Convert a database row into a CheckpointTuple object.
@@ -2,20 +2,26 @@ from __future__ import annotations
import random
import warnings
from collections.abc import Sequence
from collections.abc import Mapping, Sequence
from importlib.metadata import version as get_version
from typing import Any, cast
from typing import Any, TypedDict, cast
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.base import (
WRITES_IDX_MAP,
BaseCheckpointSaver,
ChannelVersions,
DeltaChannelHistory,
PendingWrite,
get_checkpoint_id,
)
from langgraph.checkpoint.serde.types import TASKS
from psycopg.types.json import Jsonb
# Page size for stage-1 paged scan in `get_delta_channel_history`. Internal
# constant — exposing this as a kwarg is left as a follow-up.
_DELTA_PAGE_SIZE = 1024
MetadataInput = dict[str, Any] | None
try:
@@ -153,6 +159,141 @@ INSERT_CHECKPOINT_WRITES_SQL = """
"""
class _DeltaStage2Row(TypedDict, total=False):
"""One row from `_build_delta_stage2_sql` (a UNION ALL of writes and blobs)."""
_kind: str # "w" or "b"
checkpoint_id: str | None # "w" rows only
channel: str | None # set on both "w" and "b" rows
type: str | None
blob: bytes | None
task_id: str | None # "w" rows only
idx: int | None # "w" rows only
version: str | None # "b" rows only
# Multi-channel two-stage DeltaChannel reconstruction.
#
# Stage 1 scans checkpoint metadata (no blob bytes) and emits one row per
# checkpoint with K parallel JSONB key lookups (one column pair per
# requested delta channel: ver_i / hs_i). No subqueries, no aggregation.
# Python walks the parent chain once across all channels.
#
# Stage 2 fetches all writes and the seed blobs for ALL channels in a
# single roundtrip via `channel = ANY(%s)` and chain/seed-version
# filtering.
#
# Empirical comparison vs an alternative "ship full channel_versions /
# channel_values JSONB and let Python pick" form (1000 checkpoints,
# 8 total channels in graph, 3 delta channels requested):
#
# Postgres execution: A=0.24ms vs B=0.38ms (both negligible)
# End-to-end latency: A=6.83ms vs B=2.28ms (B is 3.0x faster)
# Wire payload: A=836KB vs B=330KB (61% smaller)
# Buffer hits: identical (167 blocks)
#
# B (this dynamic-columns design) wins because it avoids JSONB
# serialization on the wire and JSONB-to-dict deserialization in
# psycopg. Even at K=8 (8 delta channels = 16 dynamic columns), B
# still beats A end-to-end (4.2ms vs 6.8ms).
def _build_delta_stage1_sql(channels: Sequence[str], *, paged: bool) -> str:
"""Build stage 1 SQL with 2K parallel JSONB key lookups.
For channels=["messages", "files"] (with `paged=True`) the result is::
SELECT checkpoint_id, parent_checkpoint_id,
checkpoint -> 'channel_versions' ->> %s AS ver_0,
(checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_0,
checkpoint -> 'channel_versions' ->> %s AS ver_1,
(checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_1
FROM checkpoints
WHERE thread_id = %s AND checkpoint_ns = %s
AND (%s::text IS NULL OR checkpoint_id < %s)
ORDER BY checkpoint_id DESC
LIMIT %s
Channel names are passed as `%s` parameters (safe from SQL injection).
Only the column aliases `ver_i` / `hs_i` are interpolated into the
SQL string (i is bounded by len(channels) and uses safe identifiers).
Caller must extend params with `[ch_0, ch_0, ch_1, ch_1, ...,
thread_id, ns, cursor, cursor, page_size]` when `paged=True`.
When `paged=False`, the WHERE has no cursor predicate and there's no
LIMIT/ORDER BY — kept as a non-public helper for tests/diagnostics.
"""
cols = []
for i in range(len(channels)):
cols.append(
f"checkpoint -> 'channel_versions' ->> %s AS ver_{i}, "
f"(checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_{i}"
)
sql = (
"SELECT checkpoint_id, parent_checkpoint_id, "
+ ", ".join(cols)
+ " FROM checkpoints WHERE thread_id = %s AND checkpoint_ns = %s"
)
if paged:
sql += (
" AND (%s::text IS NULL OR checkpoint_id < %s)"
" ORDER BY checkpoint_id DESC LIMIT %s"
)
return sql
def _build_delta_stage2_sql(
*,
channels_with_chain: Sequence[str],
channels_with_seed: Sequence[str],
) -> str:
"""Build stage 2 SQL as a per-channel UNION ALL.
For each channel with a non-empty chain, emit one branch reading
`checkpoint_writes` for that specific channel + chain_cids. For each
channel with a seed_version, emit one branch reading `checkpoint_blobs`
for that channel + version. This avoids the over-fetch of the prior
`channel = ANY(channels) AND checkpoint_id = ANY(union)` form when
channels have different chain depths.
The caller must pass parameters in matching order:
for ch in channels_with_chain:
params += [thread_id, checkpoint_ns, ch, chain_cids[ch]]
for ch in channels_with_seed:
params += [thread_id, checkpoint_ns, ch, seed_version[ch]]
Returns an empty SQL string if both channel lists are empty (caller
must skip executing in that case).
"""
branches: list[str] = []
for _ in channels_with_chain:
branches.append(
"SELECT 'w'::text AS _kind, "
"checkpoint_id, channel, "
"type, blob, task_id, idx, NULL::text AS version "
"FROM checkpoint_writes "
"WHERE thread_id = %s AND checkpoint_ns = %s AND channel = %s "
"AND checkpoint_id = ANY(%s)"
)
for _ in channels_with_seed:
branches.append(
"SELECT 'b'::text AS _kind, NULL::text AS checkpoint_id, channel, "
"type, blob, NULL::text AS task_id, NULL::int AS idx, version "
"FROM checkpoint_blobs "
"WHERE thread_id = %s AND checkpoint_ns = %s AND channel = %s "
"AND version = %s"
)
return " UNION ALL ".join(branches)
# Stage 1 rows are dynamic-shape dicts: {checkpoint_id, parent_checkpoint_id,
# ver_0, hs_0, ver_1, hs_1, ...}. Walking is parameterized by the channel
# list to map indices back to channel names — no static TypedDict here.
# `dict[str, Any]` is the practical signature.
class BasePostgresSaver(BaseCheckpointSaver[str]):
SELECT_SQL = SELECT_SQL
SELECT_PENDING_SENDS_SQL = SELECT_PENDING_SENDS_SQL
@@ -195,6 +336,146 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
if t.decode() != "empty"
}
@staticmethod
def _ingest_stage1_page(
stage1_rows: Sequence[Mapping[str, Any]],
channels: Sequence[str],
parent_of: dict[str, str | None],
ver_by_i_by_cid: list[dict[str, str | None]],
hs_by_i_by_cid: list[dict[str, bool]],
) -> str | None:
"""Fold one stage-1 page into the running walk-state mappings.
Returns the oldest checkpoint_id seen on this page (smallest, since
pages come back DESC). Caller uses it as the cursor for the next
page (`AND checkpoint_id < cursor`).
"""
oldest: str | None = None
for r in stage1_rows:
cid = cast(str, r["checkpoint_id"])
parent_of[cid] = cast("str | None", r["parent_checkpoint_id"])
for i in range(len(channels)):
ver_by_i_by_cid[i][cid] = cast("str | None", r.get(f"ver_{i}"))
hs_by_i_by_cid[i][cid] = bool(r.get(f"hs_{i}"))
# Rows are DESC; the last one is the smallest cid in the page.
oldest = cid
return oldest
@staticmethod
def _try_advance_walks(
target_id: str,
channels: Sequence[str],
parent_of: Mapping[str, str | None],
ver_by_i_by_cid: Sequence[Mapping[str, str | None]],
hs_by_i_by_cid: Sequence[Mapping[str, bool]],
chain_by_ch: dict[str, list[str]],
seed_ver_by_ch: dict[str, str | None],
walk_cursor_by_ch: dict[str, str | None],
seeded: set[str],
) -> None:
"""Advance each not-yet-seeded channel's walk as far as possible.
Uses the partial `parent_of` map accumulated so far. A walk stops
either because:
(a) it found a snapshot for its channel (channel becomes seeded),
(b) it reached a real root (parent_of[cid] is None — fully
materialized at this point), or
(c) the next ancestor cid isn't in `parent_of` yet (waiting for
a later page; the cursor stays put).
Mutates `chain_by_ch`, `seed_ver_by_ch`, `walk_cursor_by_ch`, and
`seeded` in place.
"""
for i, ch in enumerate(channels):
if ch in seeded:
continue
# First-time entry: cursor starts at the target's parent.
if ch not in walk_cursor_by_ch:
walk_cursor_by_ch[ch] = parent_of.get(target_id)
cur_cid = walk_cursor_by_ch[ch]
ch_chain = chain_by_ch[ch]
hs_i = hs_by_i_by_cid[i]
ver_i = ver_by_i_by_cid[i]
while cur_cid is not None:
if cur_cid not in parent_of:
# Need more pages to continue this walk.
break
ch_chain.append(cur_cid)
if hs_i.get(cur_cid, False):
seed_ver_by_ch[ch] = ver_i.get(cur_cid)
seeded.add(ch)
cur_cid = None
break
cur_cid = parent_of[cur_cid]
walk_cursor_by_ch[ch] = cur_cid
def _build_delta_channels_writes_history(
self,
*,
channels: Sequence[str],
chain_by_ch: Mapping[str, list[str]],
seed_ver_by_ch: Mapping[str, str | None],
stage2_rows: Sequence[_DeltaStage2Row],
) -> dict[str, DeltaChannelHistory]:
"""Demux stage 2 rows per channel; produce per-channel histories.
stage2_rows carry `channel` on every row. We build per-channel
`writes_by_cid` and per-channel `seed_blob` dicts, then assemble
a `DeltaChannelHistory` per requested channel. The `seed` key is omitted
when the walk reached root with no snapshot found, or when the
seed blob is sentinel "empty" — in both cases the consumer treats
absence as "start empty".
"""
# writes_by_ch_by_cid[channel][cid] = list of (type, blob, task_id, idx)
writes_by_ch_by_cid: dict[str, dict[str, list[tuple[str, bytes, str, int]]]] = {
ch: {} for ch in channels
}
# seed_blob_by_ver[(channel, version)] = (type, blob)
seed_blob_by_ver: dict[tuple[str, str], tuple[str, bytes]] = {}
for r in stage2_rows:
ch = cast(str, r["channel"])
kind = r["_kind"]
if kind == "w":
cid = cast(str, r["checkpoint_id"])
writes_by_ch_by_cid.setdefault(ch, {}).setdefault(cid, []).append(
cast(
"tuple[str, bytes, str, int]",
(r["type"], r["blob"], r["task_id"], r["idx"]),
)
)
else: # kind == "b"
ver = cast(str, r["version"])
seed_blob_by_ver[(ch, ver)] = cast(
"tuple[str, bytes]", (r["type"], r["blob"])
)
# Sort writes per (channel, cid) newest-first by (task_id, idx)
for cid_map in writes_by_ch_by_cid.values():
for ws in cid_map.values():
ws.sort(key=lambda w: (w[2], w[3]), reverse=True)
result: dict[str, DeltaChannelHistory] = {}
for ch in channels:
chain_cids = chain_by_ch.get(ch, [])
seed_version = seed_ver_by_ch.get(ch)
collected: list[PendingWrite] = []
cid_writes = writes_by_ch_by_cid.get(ch, {})
for cid in chain_cids:
for type_tag, write_blob, task_id, _idx in cid_writes.get(cid, []):
val = self.serde.loads_typed((type_tag, write_blob))
collected.append((task_id, ch, val))
collected.reverse()
entry: DeltaChannelHistory = {"writes": collected}
if seed_version is not None:
blob = seed_blob_by_ver.get((ch, seed_version))
if blob is not None and blob[0] != "empty":
entry["seed"] = self.serde.loads_typed(blob)
result[ch] = entry
return result
def _dump_blobs(
self,
thread_id: str,
+3 -3
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint-postgres"
version = "3.0.5"
version = "3.1.0"
description = "Library with a Postgres implementation of LangGraph checkpoint saver."
authors = []
requires-python = ">=3.10"
@@ -12,7 +12,7 @@ readme = "README.md"
license = "MIT"
license-files = ['LICENSE']
dependencies = [
"langgraph-checkpoint>=2.1.2,<5.0.0",
"langgraph-checkpoint>=4.1.0,<5.0.0",
"orjson>=3.11.5",
"psycopg>=3.2.0",
"psycopg-pool>=3.2.0",
@@ -20,7 +20,7 @@ dependencies = [
[project.urls]
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-postgres"
Twitter = "https://x.com/LangChain"
Twitter = "https://x.com/langchain_oss"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
+46 -2
View File
@@ -361,9 +361,9 @@ async def test_get_checkpoint_no_channel_values(
load_checkpoint_tuple = saver._load_checkpoint_tuple
def patched_load_checkpoint_tuple(value):
async def patched_load_checkpoint_tuple(value):
value["checkpoint"].pop("channel_values", None)
return load_checkpoint_tuple(value)
return await load_checkpoint_tuple(value)
monkeypatch.setattr(
saver, "_load_checkpoint_tuple", patched_load_checkpoint_tuple
@@ -371,3 +371,47 @@ async def test_get_checkpoint_no_channel_values(
checkpoint = await saver.aget_tuple(config)
assert checkpoint.checkpoint["channel_values"] == {}
@pytest.mark.parametrize("saver_name", ["base", "pool", "pipe"])
async def test_delta_channel_chain_reconstruction(saver_name: str) -> None:
"""AsyncPostgresSaver reconstructs DeltaChannel chain via point-lookup traversal."""
pytest.importorskip(
"langgraph.channels.delta", reason="langgraph core not installed"
)
from typing import Annotated
from langchain_core.messages import AIMessage, HumanMessage
from langgraph.channels.delta import DeltaChannel
from langgraph.graph import START, StateGraph
from langgraph.graph.message import _messages_delta_reducer
from typing_extensions import TypedDict
class State(TypedDict):
messages: Annotated[list, DeltaChannel(_messages_delta_reducer)]
def respond(state: State) -> dict:
n = len(state["messages"])
return {"messages": [AIMessage(content=f"reply-{n}", id=f"ai-{n}")]}
builder = StateGraph(State)
builder.add_node("respond", respond)
builder.add_edge(START, "respond")
async with _saver(saver_name) as saver:
graph = builder.compile(checkpointer=saver)
config = {"configurable": {"thread_id": "diff-channel-test-1"}}
await graph.ainvoke({"messages": [HumanMessage(content="hi", id="h1")]}, config)
await graph.ainvoke(
{"messages": [HumanMessage(content="there", id="h2")]}, config
)
state = await graph.aget_state(config)
msgs = state.values["messages"]
assert len(msgs) == 4, f"expected 4, got {len(msgs)}: {msgs}"
assert msgs[0].content == "hi"
assert msgs[1].content == "reply-1"
assert msgs[2].content == "there"
assert msgs[3].content == "reply-3"
+214 -78
View File
@@ -1,6 +1,10 @@
version = 1
revision = 3
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.15'",
"python_full_version < '3.15'",
]
[[package]]
name = "annotated-types"
@@ -240,10 +244,11 @@ wheels = [
[[package]]
name = "langchain-core"
version = "1.2.22"
version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsonpatch" },
{ name = "langchain-protocol" },
{ name = "langsmith" },
{ name = "packaging" },
{ name = "pydantic" },
@@ -252,14 +257,26 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "uuid-utils" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/a3/c4cd6827a1df46c821e7214b7f7b7a28b189e6c9b84ef15c6d629c5e3179/langchain_core-1.2.22.tar.gz", hash = "sha256:8d8f726d03d3652d403da915126626bb6250747e8ba406537d849e68b9f5d058", size = 842487, upload-time = "2026-03-24T18:48:44.9Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/ae/8b74458fc3850ec3d150eb9f45e857db129dafa801fb5cf173dfc9f8bbf3/langchain_core-1.3.3.tar.gz", hash = "sha256:fa510a5db8efdc0c6ff41c0939fb5c00a0183c11f6b84233e892e3227ff69182", size = 915041, upload-time = "2026-05-05T19:02:36.612Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/a6/2ffacf0f1a3788f250e75d0b52a24896c413be11be3a6d42bcdf46fbea48/langchain_core-1.2.22-py3-none-any.whl", hash = "sha256:7e30d586b75918e828833b9ec1efc25465723566845dd652c277baf751e9c04b", size = 506829, upload-time = "2026-03-24T18:48:43.286Z" },
{ url = "https://files.pythonhosted.org/packages/1f/01/4771b7ab2af1d1aba5b710bd8f13d9225c609425214b357590a17b01be77/langchain_core-1.3.3-py3-none-any.whl", hash = "sha256:18aae8506f37da7f74398492279a7d6efcee4f8e23c4c41c7af080eeb7ef7bd1", size = 543857, upload-time = "2026-05-05T19:02:34.52Z" },
]
[[package]]
name = "langchain-protocol"
version = "0.0.15"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4f/24/9777489d6fbbee64af0c8f96d4f840239c408cf694f3394672807dafc490/langchain_protocol-0.0.15.tar.gz", hash = "sha256:9ab2d11ee73944754f10e037e717098d3a6796f0e58afa9cadda6154e7655ade", size = 5862, upload-time = "2026-05-01T22:30:04.748Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/7a/9c97a7b9cbe4c5dc6a44cdb1545450c28f0c8ce89b9c1f0ee7fbad896263/langchain_protocol-0.0.15-py3-none-any.whl", hash = "sha256:461eb794358f83d5e42635a5797799ffec7b4702314e34edf73ac21e75d3ef79", size = 6982, upload-time = "2026-05-01T22:30:03.877Z" },
]
[[package]]
name = "langgraph-checkpoint"
version = "4.0.1"
version = "4.1.0"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -307,7 +324,7 @@ test = [
[[package]]
name = "langgraph-checkpoint-postgres"
version = "3.0.5"
version = "3.1.0"
source = { editable = "." }
dependencies = [
{ name = "langgraph-checkpoint" },
@@ -382,7 +399,7 @@ test = [
[[package]]
name = "langsmith"
version = "0.6.4"
version = "0.7.31"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -392,11 +409,12 @@ dependencies = [
{ name = "requests" },
{ name = "requests-toolbelt" },
{ name = "uuid-utils" },
{ name = "xxhash" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e7/85/9c7933052a997da1b85bc5c774f3865e9b1da1c8d71541ea133178b13229/langsmith-0.6.4.tar.gz", hash = "sha256:36f7223a01c218079fbb17da5e536ebbaf5c1468c028abe070aa3ae59bc99ec8", size = 919964, upload-time = "2026-01-15T20:02:28.873Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e6/11/696019490992db5c87774dc20515529ef42a01e1d770fb754ed6d9b12fb0/langsmith-0.7.31.tar.gz", hash = "sha256:331ee4f7c26bb5be4022b9859b7d7b122cbf8c9d01d9f530114c1914b0349ffb", size = 1178480, upload-time = "2026-04-14T17:55:41.242Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/66/0f/09a6637a7ba777eb307b7c80852d9ee26438e2bdafbad6fcc849ff9d9192/langsmith-0.6.4-py3-none-any.whl", hash = "sha256:ac4835860160be371042c7adbba3cb267bcf8d96a5ea976c33a8a4acad6c5486", size = 283503, upload-time = "2026-01-15T20:02:26.662Z" },
{ url = "https://files.pythonhosted.org/packages/1d/a1/a013cf458c301cda86a213dd153ce0a01c93f1ab5833f951e6a44c9763ce/langsmith-0.7.31-py3-none-any.whl", hash = "sha256:0291d49203f6e80dda011af1afda61eb0595a4d697adb684590a8805e1d61fb6", size = 373276, upload-time = "2026-04-14T17:55:39.677Z" },
]
[[package]]
@@ -486,7 +504,7 @@ wheels = [
[[package]]
name = "mypy"
version = "1.20.0"
version = "1.20.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
@@ -495,51 +513,51 @@ dependencies = [
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/b0089fe7fef0a994ae5ee07029ced0526082c6cfaaa4c10d40a10e33b097/mypy-1.20.0.tar.gz", hash = "sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3", size = 3815028, upload-time = "2026-03-31T16:55:14.959Z" }
sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/a2/a965c8c3fcd4fa8b84ba0d46606181b0d0a1d50f274c67877f3e9ed4882c/mypy-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8", size = 14430138, upload-time = "2026-03-31T16:52:37.843Z" },
{ url = "https://files.pythonhosted.org/packages/53/6e/043477501deeb8eabbab7f1a2f6cac62cfb631806dc1d6862a04a7f5011b/mypy-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a", size = 13311282, upload-time = "2026-03-31T16:55:11.021Z" },
{ url = "https://files.pythonhosted.org/packages/65/aa/bd89b247b83128197a214f29f0632ff3c14f54d4cd70d144d157bd7d7d6e/mypy-1.20.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865", size = 13750889, upload-time = "2026-03-31T16:52:02.909Z" },
{ url = "https://files.pythonhosted.org/packages/fa/9d/2860be7355c45247ccc0be1501c91176318964c2a137bd4743f58ce6200e/mypy-1.20.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca", size = 14619788, upload-time = "2026-03-31T16:50:48.928Z" },
{ url = "https://files.pythonhosted.org/packages/75/7f/3ef3e360c91f3de120f205c8ce405e9caf9fc52ef14b65d37073e322c114/mypy-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018", size = 14918849, upload-time = "2026-03-31T16:51:10.478Z" },
{ url = "https://files.pythonhosted.org/packages/ae/72/af970dfe167ef788df7c5e6109d2ed0229f164432ce828bc9741a4250e64/mypy-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13", size = 10822007, upload-time = "2026-03-31T16:50:25.268Z" },
{ url = "https://files.pythonhosted.org/packages/93/94/ba9065c2ebe5421619aff684b793d953e438a8bfe31a320dd6d1e0706e81/mypy-1.20.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281", size = 9756158, upload-time = "2026-03-31T16:48:36.213Z" },
{ url = "https://files.pythonhosted.org/packages/6e/1c/74cb1d9993236910286865679d1c616b136b2eae468493aa939431eda410/mypy-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b", size = 14343972, upload-time = "2026-03-31T16:49:04.887Z" },
{ url = "https://files.pythonhosted.org/packages/d5/0d/01399515eca280386e308cf57901e68d3a52af18691941b773b3380c1df8/mypy-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367", size = 13225007, upload-time = "2026-03-31T16:50:08.151Z" },
{ url = "https://files.pythonhosted.org/packages/56/ac/b4ba5094fb2d7fe9d2037cd8d18bbe02bcf68fd22ab9ff013f55e57ba095/mypy-1.20.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62", size = 13663752, upload-time = "2026-03-31T16:49:26.064Z" },
{ url = "https://files.pythonhosted.org/packages/db/a7/460678d3cf7da252d2288dad0c602294b6ec22a91932ec368cc11e44bb6e/mypy-1.20.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0", size = 14532265, upload-time = "2026-03-31T16:53:55.077Z" },
{ url = "https://files.pythonhosted.org/packages/a3/3e/051cca8166cf0438ae3ea80e0e7c030d7a8ab98dffc93f80a1aa3f23c1a2/mypy-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f", size = 14768476, upload-time = "2026-03-31T16:50:34.587Z" },
{ url = "https://files.pythonhosted.org/packages/be/66/8e02ec184f852ed5c4abb805583305db475930854e09964b55e107cdcbc4/mypy-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e", size = 10818226, upload-time = "2026-03-31T16:53:15.624Z" },
{ url = "https://files.pythonhosted.org/packages/13/4b/383ad1924b28f41e4879a74151e7a5451123330d45652da359f9183bcd45/mypy-1.20.0-cp311-cp311-win_arm64.whl", hash = "sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442", size = 9750091, upload-time = "2026-03-31T16:54:12.162Z" },
{ url = "https://files.pythonhosted.org/packages/be/dd/3afa29b58c2e57c79116ed55d700721c3c3b15955e2b6251dd165d377c0e/mypy-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214", size = 14509525, upload-time = "2026-03-31T16:55:01.824Z" },
{ url = "https://files.pythonhosted.org/packages/54/eb/227b516ab8cad9f2a13c5e7a98d28cd6aa75e9c83e82776ae6c1c4c046c7/mypy-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e", size = 13326469, upload-time = "2026-03-31T16:51:41.23Z" },
{ url = "https://files.pythonhosted.org/packages/57/d4/1ddb799860c1b5ac6117ec307b965f65deeb47044395ff01ab793248a591/mypy-1.20.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651", size = 13705953, upload-time = "2026-03-31T16:48:55.69Z" },
{ url = "https://files.pythonhosted.org/packages/c5/b7/54a720f565a87b893182a2a393370289ae7149e4715859e10e1c05e49154/mypy-1.20.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5", size = 14710363, upload-time = "2026-03-31T16:53:26.948Z" },
{ url = "https://files.pythonhosted.org/packages/b2/2a/74810274848d061f8a8ea4ac23aaad43bd3d8c1882457999c2e568341c57/mypy-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78", size = 14947005, upload-time = "2026-03-31T16:50:17.591Z" },
{ url = "https://files.pythonhosted.org/packages/77/91/21b8ba75f958bcda75690951ce6fa6b7138b03471618959529d74b8544e2/mypy-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489", size = 10880616, upload-time = "2026-03-31T16:52:19.986Z" },
{ url = "https://files.pythonhosted.org/packages/8a/15/3d8198ef97c1ca03aea010cce4f1d4f3bc5d9849e8c0140111ca2ead9fdd/mypy-1.20.0-cp312-cp312-win_arm64.whl", hash = "sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33", size = 9813091, upload-time = "2026-03-31T16:53:44.385Z" },
{ url = "https://files.pythonhosted.org/packages/d6/a7/f64ea7bd592fa431cb597418b6dec4a47f7d0c36325fec7ac67bc8402b94/mypy-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134", size = 14485344, upload-time = "2026-03-31T16:49:16.78Z" },
{ url = "https://files.pythonhosted.org/packages/bb/72/8927d84cfc90c6abea6e96663576e2e417589347eb538749a464c4c218a0/mypy-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c", size = 13327400, upload-time = "2026-03-31T16:53:08.02Z" },
{ url = "https://files.pythonhosted.org/packages/ab/4a/11ab99f9afa41aa350178d24a7d2da17043228ea10f6456523f64b5a6cf6/mypy-1.20.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe", size = 13706384, upload-time = "2026-03-31T16:52:28.577Z" },
{ url = "https://files.pythonhosted.org/packages/42/79/694ca73979cfb3535ebfe78733844cd5aff2e63304f59bf90585110d975a/mypy-1.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f", size = 14700378, upload-time = "2026-03-31T16:48:45.527Z" },
{ url = "https://files.pythonhosted.org/packages/84/24/a022ccab3a46e3d2cdf2e0e260648633640eb396c7e75d5a42818a8d3971/mypy-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726", size = 14932170, upload-time = "2026-03-31T16:49:36.038Z" },
{ url = "https://files.pythonhosted.org/packages/d8/9b/549228d88f574d04117e736f55958bd4908f980f9f5700a07aeb85df005b/mypy-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69", size = 10888526, upload-time = "2026-03-31T16:50:59.827Z" },
{ url = "https://files.pythonhosted.org/packages/91/17/15095c0e54a8bc04d22d4ff06b2139d5f142c2e87520b4e39010c4862771/mypy-1.20.0-cp313-cp313-win_arm64.whl", hash = "sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e", size = 9816456, upload-time = "2026-03-31T16:49:59.537Z" },
{ url = "https://files.pythonhosted.org/packages/4e/0e/6ca4a84cbed9e62384bc0b2974c90395ece5ed672393e553996501625fc5/mypy-1.20.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948", size = 14483331, upload-time = "2026-03-31T16:52:57.999Z" },
{ url = "https://files.pythonhosted.org/packages/7d/c5/5fe9d8a729dd9605064691816243ae6c49fde0bd28f6e5e17f6a24203c43/mypy-1.20.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5", size = 13342047, upload-time = "2026-03-31T16:54:21.555Z" },
{ url = "https://files.pythonhosted.org/packages/4c/33/e18bcfa338ca4e6b2771c85d4c5203e627d0c69d9de5c1a2cf2ba13320ba/mypy-1.20.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188", size = 13719585, upload-time = "2026-03-31T16:51:53.89Z" },
{ url = "https://files.pythonhosted.org/packages/6b/8d/93491ff7b79419edc7eabf95cb3b3f7490e2e574b2855c7c7e7394ff933f/mypy-1.20.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83", size = 14685075, upload-time = "2026-03-31T16:54:04.464Z" },
{ url = "https://files.pythonhosted.org/packages/b5/9d/d924b38a4923f8d164bf2b4ec98bf13beaf6e10a5348b4b137eadae40a6e/mypy-1.20.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2", size = 14919141, upload-time = "2026-03-31T16:54:51.785Z" },
{ url = "https://files.pythonhosted.org/packages/59/98/1da9977016678c0b99d43afe52ed00bb3c1a0c4c995d3e6acca1a6ebb9b4/mypy-1.20.0-cp314-cp314-win_amd64.whl", hash = "sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732", size = 11050925, upload-time = "2026-03-31T16:51:30.758Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e3/ba0b7a3143e49a9c4f5967dde6ea4bf8e0b10ecbbcca69af84027160ee89/mypy-1.20.0-cp314-cp314-win_arm64.whl", hash = "sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef", size = 10001089, upload-time = "2026-03-31T16:49:43.632Z" },
{ url = "https://files.pythonhosted.org/packages/12/28/e617e67b3be9d213cda7277913269c874eb26472489f95d09d89765ce2d8/mypy-1.20.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1", size = 15534710, upload-time = "2026-03-31T16:52:12.506Z" },
{ url = "https://files.pythonhosted.org/packages/6e/0c/3b5f2d3e45dc7169b811adce8451679d9430399d03b168f9b0489f43adaa/mypy-1.20.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436", size = 14393013, upload-time = "2026-03-31T16:54:41.186Z" },
{ url = "https://files.pythonhosted.org/packages/a3/49/edc8b0aa145cc09c1c74f7ce2858eead9329931dcbbb26e2ad40906daa4e/mypy-1.20.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6", size = 15047240, upload-time = "2026-03-31T16:54:31.955Z" },
{ url = "https://files.pythonhosted.org/packages/42/37/a946bb416e37a57fa752b3100fd5ede0e28df94f92366d1716555d47c454/mypy-1.20.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526", size = 15858565, upload-time = "2026-03-31T16:53:36.997Z" },
{ url = "https://files.pythonhosted.org/packages/2f/99/7690b5b5b552db1bd4ff362e4c0eb3107b98d680835e65823fbe888c8b78/mypy-1.20.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787", size = 16087874, upload-time = "2026-03-31T16:52:48.313Z" },
{ url = "https://files.pythonhosted.org/packages/aa/76/53e893a498138066acd28192b77495c9357e5a58cc4be753182846b43315/mypy-1.20.0-cp314-cp314t-win_amd64.whl", hash = "sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb", size = 12572380, upload-time = "2026-03-31T16:49:52.454Z" },
{ url = "https://files.pythonhosted.org/packages/76/9c/6dbdae21f01b7aacddc2c0bbf3c5557aa547827fdf271770fe1e521e7093/mypy-1.20.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd", size = 10381174, upload-time = "2026-03-31T16:51:20.179Z" },
{ url = "https://files.pythonhosted.org/packages/21/66/4d734961ce167f0fd8380769b3b7c06dbdd6ff54c2190f3f2ecd22528158/mypy-1.20.0-py3-none-any.whl", hash = "sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e", size = 2636365, upload-time = "2026-03-31T16:51:44.911Z" },
{ url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" },
{ url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" },
{ url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" },
{ url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" },
{ url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" },
{ url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" },
{ url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" },
{ url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" },
{ url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" },
{ url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" },
{ url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" },
{ url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" },
{ url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" },
{ url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" },
{ url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" },
{ url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" },
{ url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" },
{ url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" },
{ url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" },
{ url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" },
{ url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" },
{ url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" },
{ url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" },
{ url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" },
{ url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" },
{ url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" },
{ url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" },
{ url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" },
{ url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" },
{ url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" },
{ url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" },
{ url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" },
{ url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" },
{ url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" },
{ url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" },
{ url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" },
{ url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" },
{ url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" },
{ url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" },
{ url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" },
{ url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" },
{ url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" },
]
[[package]]
@@ -950,7 +968,7 @@ wheels = [
[[package]]
name = "pytest"
version = "9.0.2"
version = "9.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -961,9 +979,9 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
]
[[package]]
@@ -1098,27 +1116,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.15.8"
version = "0.15.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" }
sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" },
{ url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" },
{ url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" },
{ url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" },
{ url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" },
{ url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" },
{ url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" },
{ url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" },
{ url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" },
{ url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" },
{ url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" },
{ url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" },
{ url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" },
{ url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" },
{ url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" },
{ url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" },
{ url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" },
{ url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" },
{ url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" },
{ url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" },
{ url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" },
{ url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" },
{ url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" },
{ url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" },
{ url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" },
{ url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" },
{ url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" },
{ url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" },
{ url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" },
{ url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" },
{ url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" },
{ url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" },
{ url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" },
{ url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" },
]
[[package]]
@@ -1216,11 +1234,11 @@ wheels = [
[[package]]
name = "urllib3"
version = "2.6.3"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
]
[[package]]
@@ -1284,6 +1302,124 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
]
[[package]]
name = "xxhash"
version = "3.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" },
{ url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" },
{ url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" },
{ url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" },
{ url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" },
{ url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" },
{ url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" },
{ url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" },
{ url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" },
{ url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" },
{ url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" },
{ url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" },
{ url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" },
{ url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" },
{ url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" },
{ url = "https://files.pythonhosted.org/packages/17/d4/cc2f0400e9154df4b9964249da78ebd72f318e35ccc425e9f403c392f22a/xxhash-3.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b47bbd8cf2d72797f3c2772eaaac0ded3d3af26481a26d7d7d41dc2d3c46b04a", size = 32844, upload-time = "2025-10-02T14:34:14.037Z" },
{ url = "https://files.pythonhosted.org/packages/5e/ec/1cc11cd13e26ea8bc3cb4af4eaadd8d46d5014aebb67be3f71fb0b68802a/xxhash-3.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2b6821e94346f96db75abaa6e255706fb06ebd530899ed76d32cd99f20dc52fa", size = 30809, upload-time = "2025-10-02T14:34:15.484Z" },
{ url = "https://files.pythonhosted.org/packages/04/5f/19fe357ea348d98ca22f456f75a30ac0916b51c753e1f8b2e0e6fb884cce/xxhash-3.6.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d0a9751f71a1a65ce3584e9cae4467651c7e70c9d31017fa57574583a4540248", size = 194665, upload-time = "2025-10-02T14:34:16.541Z" },
{ url = "https://files.pythonhosted.org/packages/90/3b/d1f1a8f5442a5fd8beedae110c5af7604dc37349a8e16519c13c19a9a2de/xxhash-3.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b29ee68625ab37b04c0b40c3fafdf24d2f75ccd778333cfb698f65f6c463f62", size = 213550, upload-time = "2025-10-02T14:34:17.878Z" },
{ url = "https://files.pythonhosted.org/packages/c4/ef/3a9b05eb527457d5db13a135a2ae1a26c80fecd624d20f3e8dcc4cb170f3/xxhash-3.6.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6812c25fe0d6c36a46ccb002f40f27ac903bf18af9f6dd8f9669cb4d176ab18f", size = 212384, upload-time = "2025-10-02T14:34:19.182Z" },
{ url = "https://files.pythonhosted.org/packages/0f/18/ccc194ee698c6c623acbf0f8c2969811a8a4b6185af5e824cd27b9e4fd3e/xxhash-3.6.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4ccbff013972390b51a18ef1255ef5ac125c92dc9143b2d1909f59abc765540e", size = 445749, upload-time = "2025-10-02T14:34:20.659Z" },
{ url = "https://files.pythonhosted.org/packages/a5/86/cf2c0321dc3940a7aa73076f4fd677a0fb3e405cb297ead7d864fd90847e/xxhash-3.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:297b7fbf86c82c550e12e8fb71968b3f033d27b874276ba3624ea868c11165a8", size = 193880, upload-time = "2025-10-02T14:34:22.431Z" },
{ url = "https://files.pythonhosted.org/packages/82/fb/96213c8560e6f948a1ecc9a7613f8032b19ee45f747f4fca4eb31bb6d6ed/xxhash-3.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dea26ae1eb293db089798d3973a5fc928a18fdd97cc8801226fae705b02b14b0", size = 210912, upload-time = "2025-10-02T14:34:23.937Z" },
{ url = "https://files.pythonhosted.org/packages/40/aa/4395e669b0606a096d6788f40dbdf2b819d6773aa290c19e6e83cbfc312f/xxhash-3.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7a0b169aafb98f4284f73635a8e93f0735f9cbde17bd5ec332480484241aaa77", size = 198654, upload-time = "2025-10-02T14:34:25.644Z" },
{ url = "https://files.pythonhosted.org/packages/67/74/b044fcd6b3d89e9b1b665924d85d3f400636c23590226feb1eb09e1176ce/xxhash-3.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:08d45aef063a4531b785cd72de4887766d01dc8f362a515693df349fdb825e0c", size = 210867, upload-time = "2025-10-02T14:34:27.203Z" },
{ url = "https://files.pythonhosted.org/packages/bc/fd/3ce73bf753b08cb19daee1eb14aa0d7fe331f8da9c02dd95316ddfe5275e/xxhash-3.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:929142361a48ee07f09121fe9e96a84950e8d4df3bb298ca5d88061969f34d7b", size = 414012, upload-time = "2025-10-02T14:34:28.409Z" },
{ url = "https://files.pythonhosted.org/packages/ba/b3/5a4241309217c5c876f156b10778f3ab3af7ba7e3259e6d5f5c7d0129eb2/xxhash-3.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51312c768403d8540487dbbfb557454cfc55589bbde6424456951f7fcd4facb3", size = 191409, upload-time = "2025-10-02T14:34:29.696Z" },
{ url = "https://files.pythonhosted.org/packages/c0/01/99bfbc15fb9abb9a72b088c1d95219fc4782b7d01fc835bd5744d66dd0b8/xxhash-3.6.0-cp311-cp311-win32.whl", hash = "sha256:d1927a69feddc24c987b337ce81ac15c4720955b667fe9b588e02254b80446fd", size = 30574, upload-time = "2025-10-02T14:34:31.028Z" },
{ url = "https://files.pythonhosted.org/packages/65/79/9d24d7f53819fe301b231044ea362ce64e86c74f6e8c8e51320de248b3e5/xxhash-3.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:26734cdc2d4ffe449b41d186bbeac416f704a482ed835d375a5c0cb02bc63fef", size = 31481, upload-time = "2025-10-02T14:34:32.062Z" },
{ url = "https://files.pythonhosted.org/packages/30/4e/15cd0e3e8772071344eab2961ce83f6e485111fed8beb491a3f1ce100270/xxhash-3.6.0-cp311-cp311-win_arm64.whl", hash = "sha256:d72f67ef8bf36e05f5b6c65e8524f265bd61071471cd4cf1d36743ebeeeb06b7", size = 27861, upload-time = "2025-10-02T14:34:33.555Z" },
{ url = "https://files.pythonhosted.org/packages/9a/07/d9412f3d7d462347e4511181dea65e47e0d0e16e26fbee2ea86a2aefb657/xxhash-3.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01362c4331775398e7bb34e3ab403bc9ee9f7c497bc7dee6272114055277dd3c", size = 32744, upload-time = "2025-10-02T14:34:34.622Z" },
{ url = "https://files.pythonhosted.org/packages/79/35/0429ee11d035fc33abe32dca1b2b69e8c18d236547b9a9b72c1929189b9a/xxhash-3.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b2df81a23f8cb99656378e72501b2cb41b1827c0f5a86f87d6b06b69f9f204", size = 30816, upload-time = "2025-10-02T14:34:36.043Z" },
{ url = "https://files.pythonhosted.org/packages/b7/f2/57eb99aa0f7d98624c0932c5b9a170e1806406cdbcdb510546634a1359e0/xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490", size = 194035, upload-time = "2025-10-02T14:34:37.354Z" },
{ url = "https://files.pythonhosted.org/packages/4c/ed/6224ba353690d73af7a3f1c7cdb1fc1b002e38f783cb991ae338e1eb3d79/xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2", size = 212914, upload-time = "2025-10-02T14:34:38.6Z" },
{ url = "https://files.pythonhosted.org/packages/38/86/fb6b6130d8dd6b8942cc17ab4d90e223653a89aa32ad2776f8af7064ed13/xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa", size = 212163, upload-time = "2025-10-02T14:34:39.872Z" },
{ url = "https://files.pythonhosted.org/packages/ee/dc/e84875682b0593e884ad73b2d40767b5790d417bde603cceb6878901d647/xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0", size = 445411, upload-time = "2025-10-02T14:34:41.569Z" },
{ url = "https://files.pythonhosted.org/packages/11/4f/426f91b96701ec2f37bb2b8cec664eff4f658a11f3fa9d94f0a887ea6d2b/xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2", size = 193883, upload-time = "2025-10-02T14:34:43.249Z" },
{ url = "https://files.pythonhosted.org/packages/53/5a/ddbb83eee8e28b778eacfc5a85c969673e4023cdeedcfcef61f36731610b/xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9", size = 210392, upload-time = "2025-10-02T14:34:45.042Z" },
{ url = "https://files.pythonhosted.org/packages/1e/c2/ff69efd07c8c074ccdf0a4f36fcdd3d27363665bcdf4ba399abebe643465/xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e", size = 197898, upload-time = "2025-10-02T14:34:46.302Z" },
{ url = "https://files.pythonhosted.org/packages/58/ca/faa05ac19b3b622c7c9317ac3e23954187516298a091eb02c976d0d3dd45/xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374", size = 210655, upload-time = "2025-10-02T14:34:47.571Z" },
{ url = "https://files.pythonhosted.org/packages/d4/7a/06aa7482345480cc0cb597f5c875b11a82c3953f534394f620b0be2f700c/xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d", size = 414001, upload-time = "2025-10-02T14:34:49.273Z" },
{ url = "https://files.pythonhosted.org/packages/23/07/63ffb386cd47029aa2916b3d2f454e6cc5b9f5c5ada3790377d5430084e7/xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae", size = 191431, upload-time = "2025-10-02T14:34:50.798Z" },
{ url = "https://files.pythonhosted.org/packages/0f/93/14fde614cadb4ddf5e7cebf8918b7e8fac5ae7861c1875964f17e678205c/xxhash-3.6.0-cp312-cp312-win32.whl", hash = "sha256:50fc255f39428a27299c20e280d6193d8b63b8ef8028995323bf834a026b4fbb", size = 30617, upload-time = "2025-10-02T14:34:51.954Z" },
{ url = "https://files.pythonhosted.org/packages/13/5d/0d125536cbe7565a83d06e43783389ecae0c0f2ed037b48ede185de477c0/xxhash-3.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0f2ab8c715630565ab8991b536ecded9416d615538be8ecddce43ccf26cbc7c", size = 31534, upload-time = "2025-10-02T14:34:53.276Z" },
{ url = "https://files.pythonhosted.org/packages/54/85/6ec269b0952ec7e36ba019125982cf11d91256a778c7c3f98a4c5043d283/xxhash-3.6.0-cp312-cp312-win_arm64.whl", hash = "sha256:eae5c13f3bc455a3bbb68bdc513912dc7356de7e2280363ea235f71f54064829", size = 27876, upload-time = "2025-10-02T14:34:54.371Z" },
{ url = "https://files.pythonhosted.org/packages/33/76/35d05267ac82f53ae9b0e554da7c5e281ee61f3cad44c743f0fcd354f211/xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec", size = 32738, upload-time = "2025-10-02T14:34:55.839Z" },
{ url = "https://files.pythonhosted.org/packages/31/a8/3fbce1cd96534a95e35d5120637bf29b0d7f5d8fa2f6374e31b4156dd419/xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1", size = 30821, upload-time = "2025-10-02T14:34:57.219Z" },
{ url = "https://files.pythonhosted.org/packages/0c/ea/d387530ca7ecfa183cb358027f1833297c6ac6098223fd14f9782cd0015c/xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6", size = 194127, upload-time = "2025-10-02T14:34:59.21Z" },
{ url = "https://files.pythonhosted.org/packages/ba/0c/71435dcb99874b09a43b8d7c54071e600a7481e42b3e3ce1eb5226a5711a/xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263", size = 212975, upload-time = "2025-10-02T14:35:00.816Z" },
{ url = "https://files.pythonhosted.org/packages/84/7a/c2b3d071e4bb4a90b7057228a99b10d51744878f4a8a6dd643c8bd897620/xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546", size = 212241, upload-time = "2025-10-02T14:35:02.207Z" },
{ url = "https://files.pythonhosted.org/packages/81/5f/640b6eac0128e215f177df99eadcd0f1b7c42c274ab6a394a05059694c5a/xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89", size = 445471, upload-time = "2025-10-02T14:35:03.61Z" },
{ url = "https://files.pythonhosted.org/packages/5e/1e/3c3d3ef071b051cc3abbe3721ffb8365033a172613c04af2da89d5548a87/xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d", size = 193936, upload-time = "2025-10-02T14:35:05.013Z" },
{ url = "https://files.pythonhosted.org/packages/2c/bd/4a5f68381939219abfe1c22a9e3a5854a4f6f6f3c4983a87d255f21f2e5d/xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7", size = 210440, upload-time = "2025-10-02T14:35:06.239Z" },
{ url = "https://files.pythonhosted.org/packages/eb/37/b80fe3d5cfb9faff01a02121a0f4d565eb7237e9e5fc66e73017e74dcd36/xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db", size = 197990, upload-time = "2025-10-02T14:35:07.735Z" },
{ url = "https://files.pythonhosted.org/packages/d7/fd/2c0a00c97b9e18f72e1f240ad4e8f8a90fd9d408289ba9c7c495ed7dc05c/xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42", size = 210689, upload-time = "2025-10-02T14:35:09.438Z" },
{ url = "https://files.pythonhosted.org/packages/93/86/5dd8076a926b9a95db3206aba20d89a7fc14dd5aac16e5c4de4b56033140/xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11", size = 414068, upload-time = "2025-10-02T14:35:11.162Z" },
{ url = "https://files.pythonhosted.org/packages/af/3c/0bb129170ee8f3650f08e993baee550a09593462a5cddd8e44d0011102b1/xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd", size = 191495, upload-time = "2025-10-02T14:35:12.971Z" },
{ url = "https://files.pythonhosted.org/packages/e9/3a/6797e0114c21d1725e2577508e24006fd7ff1d8c0c502d3b52e45c1771d8/xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799", size = 30620, upload-time = "2025-10-02T14:35:14.129Z" },
{ url = "https://files.pythonhosted.org/packages/86/15/9bc32671e9a38b413a76d24722a2bf8784a132c043063a8f5152d390b0f9/xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392", size = 31542, upload-time = "2025-10-02T14:35:15.21Z" },
{ url = "https://files.pythonhosted.org/packages/39/c5/cc01e4f6188656e56112d6a8e0dfe298a16934b8c47a247236549a3f7695/xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6", size = 27880, upload-time = "2025-10-02T14:35:16.315Z" },
{ url = "https://files.pythonhosted.org/packages/f3/30/25e5321c8732759e930c555176d37e24ab84365482d257c3b16362235212/xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702", size = 32956, upload-time = "2025-10-02T14:35:17.413Z" },
{ url = "https://files.pythonhosted.org/packages/9f/3c/0573299560d7d9f8ab1838f1efc021a280b5ae5ae2e849034ef3dee18810/xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db", size = 31072, upload-time = "2025-10-02T14:35:18.844Z" },
{ url = "https://files.pythonhosted.org/packages/7a/1c/52d83a06e417cd9d4137722693424885cc9878249beb3a7c829e74bf7ce9/xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54", size = 196409, upload-time = "2025-10-02T14:35:20.31Z" },
{ url = "https://files.pythonhosted.org/packages/e3/8e/c6d158d12a79bbd0b878f8355432075fc82759e356ab5a111463422a239b/xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f", size = 215736, upload-time = "2025-10-02T14:35:21.616Z" },
{ url = "https://files.pythonhosted.org/packages/bc/68/c4c80614716345d55071a396cf03d06e34b5f4917a467faf43083c995155/xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5", size = 214833, upload-time = "2025-10-02T14:35:23.32Z" },
{ url = "https://files.pythonhosted.org/packages/7e/e9/ae27c8ffec8b953efa84c7c4a6c6802c263d587b9fc0d6e7cea64e08c3af/xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1", size = 448348, upload-time = "2025-10-02T14:35:25.111Z" },
{ url = "https://files.pythonhosted.org/packages/d7/6b/33e21afb1b5b3f46b74b6bd1913639066af218d704cc0941404ca717fc57/xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee", size = 196070, upload-time = "2025-10-02T14:35:26.586Z" },
{ url = "https://files.pythonhosted.org/packages/96/b6/fcabd337bc5fa624e7203aa0fa7d0c49eed22f72e93229431752bddc83d9/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd", size = 212907, upload-time = "2025-10-02T14:35:28.087Z" },
{ url = "https://files.pythonhosted.org/packages/4b/d3/9ee6160e644d660fcf176c5825e61411c7f62648728f69c79ba237250143/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729", size = 200839, upload-time = "2025-10-02T14:35:29.857Z" },
{ url = "https://files.pythonhosted.org/packages/0d/98/e8de5baa5109394baf5118f5e72ab21a86387c4f89b0e77ef3e2f6b0327b/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292", size = 213304, upload-time = "2025-10-02T14:35:31.222Z" },
{ url = "https://files.pythonhosted.org/packages/7b/1d/71056535dec5c3177eeb53e38e3d367dd1d16e024e63b1cee208d572a033/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf", size = 416930, upload-time = "2025-10-02T14:35:32.517Z" },
{ url = "https://files.pythonhosted.org/packages/dc/6c/5cbde9de2cd967c322e651c65c543700b19e7ae3e0aae8ece3469bf9683d/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033", size = 193787, upload-time = "2025-10-02T14:35:33.827Z" },
{ url = "https://files.pythonhosted.org/packages/19/fa/0172e350361d61febcea941b0cc541d6e6c8d65d153e85f850a7b256ff8a/xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec", size = 30916, upload-time = "2025-10-02T14:35:35.107Z" },
{ url = "https://files.pythonhosted.org/packages/ad/e6/e8cf858a2b19d6d45820f072eff1bea413910592ff17157cabc5f1227a16/xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8", size = 31799, upload-time = "2025-10-02T14:35:36.165Z" },
{ url = "https://files.pythonhosted.org/packages/56/15/064b197e855bfb7b343210e82490ae672f8bc7cdf3ddb02e92f64304ee8a/xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746", size = 28044, upload-time = "2025-10-02T14:35:37.195Z" },
{ url = "https://files.pythonhosted.org/packages/7e/5e/0138bc4484ea9b897864d59fce9be9086030825bc778b76cb5a33a906d37/xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e", size = 32754, upload-time = "2025-10-02T14:35:38.245Z" },
{ url = "https://files.pythonhosted.org/packages/18/d7/5dac2eb2ec75fd771957a13e5dda560efb2176d5203f39502a5fc571f899/xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405", size = 30846, upload-time = "2025-10-02T14:35:39.6Z" },
{ url = "https://files.pythonhosted.org/packages/fe/71/8bc5be2bb00deb5682e92e8da955ebe5fa982da13a69da5a40a4c8db12fb/xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3", size = 194343, upload-time = "2025-10-02T14:35:40.69Z" },
{ url = "https://files.pythonhosted.org/packages/e7/3b/52badfb2aecec2c377ddf1ae75f55db3ba2d321c5e164f14461c90837ef3/xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6", size = 213074, upload-time = "2025-10-02T14:35:42.29Z" },
{ url = "https://files.pythonhosted.org/packages/a2/2b/ae46b4e9b92e537fa30d03dbc19cdae57ed407e9c26d163895e968e3de85/xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063", size = 212388, upload-time = "2025-10-02T14:35:43.929Z" },
{ url = "https://files.pythonhosted.org/packages/f5/80/49f88d3afc724b4ac7fbd664c8452d6db51b49915be48c6982659e0e7942/xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7", size = 445614, upload-time = "2025-10-02T14:35:45.216Z" },
{ url = "https://files.pythonhosted.org/packages/ed/ba/603ce3961e339413543d8cd44f21f2c80e2a7c5cfe692a7b1f2cccf58f3c/xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b", size = 194024, upload-time = "2025-10-02T14:35:46.959Z" },
{ url = "https://files.pythonhosted.org/packages/78/d1/8e225ff7113bf81545cfdcd79eef124a7b7064a0bba53605ff39590b95c2/xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd", size = 210541, upload-time = "2025-10-02T14:35:48.301Z" },
{ url = "https://files.pythonhosted.org/packages/6f/58/0f89d149f0bad89def1a8dd38feb50ccdeb643d9797ec84707091d4cb494/xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0", size = 198305, upload-time = "2025-10-02T14:35:49.584Z" },
{ url = "https://files.pythonhosted.org/packages/11/38/5eab81580703c4df93feb5f32ff8fa7fe1e2c51c1f183ee4e48d4bb9d3d7/xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152", size = 210848, upload-time = "2025-10-02T14:35:50.877Z" },
{ url = "https://files.pythonhosted.org/packages/5e/6b/953dc4b05c3ce678abca756416e4c130d2382f877a9c30a20d08ee6a77c0/xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11", size = 414142, upload-time = "2025-10-02T14:35:52.15Z" },
{ url = "https://files.pythonhosted.org/packages/08/a9/238ec0d4e81a10eb5026d4a6972677cbc898ba6c8b9dbaec12ae001b1b35/xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5", size = 191547, upload-time = "2025-10-02T14:35:53.547Z" },
{ url = "https://files.pythonhosted.org/packages/f1/ee/3cf8589e06c2164ac77c3bf0aa127012801128f1feebf2a079272da5737c/xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f", size = 31214, upload-time = "2025-10-02T14:35:54.746Z" },
{ url = "https://files.pythonhosted.org/packages/02/5d/a19552fbc6ad4cb54ff953c3908bbc095f4a921bc569433d791f755186f1/xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad", size = 32290, upload-time = "2025-10-02T14:35:55.791Z" },
{ url = "https://files.pythonhosted.org/packages/b1/11/dafa0643bc30442c887b55baf8e73353a344ee89c1901b5a5c54a6c17d39/xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679", size = 28795, upload-time = "2025-10-02T14:35:57.162Z" },
{ url = "https://files.pythonhosted.org/packages/2c/db/0e99732ed7f64182aef4a6fb145e1a295558deec2a746265dcdec12d191e/xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4", size = 32955, upload-time = "2025-10-02T14:35:58.267Z" },
{ url = "https://files.pythonhosted.org/packages/55/f4/2a7c3c68e564a099becfa44bb3d398810cc0ff6749b0d3cb8ccb93f23c14/xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67", size = 31072, upload-time = "2025-10-02T14:35:59.382Z" },
{ url = "https://files.pythonhosted.org/packages/c6/d9/72a29cddc7250e8a5819dad5d466facb5dc4c802ce120645630149127e73/xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad", size = 196579, upload-time = "2025-10-02T14:36:00.838Z" },
{ url = "https://files.pythonhosted.org/packages/63/93/b21590e1e381040e2ca305a884d89e1c345b347404f7780f07f2cdd47ef4/xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b", size = 215854, upload-time = "2025-10-02T14:36:02.207Z" },
{ url = "https://files.pythonhosted.org/packages/ce/b8/edab8a7d4fa14e924b29be877d54155dcbd8b80be85ea00d2be3413a9ed4/xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b", size = 214965, upload-time = "2025-10-02T14:36:03.507Z" },
{ url = "https://files.pythonhosted.org/packages/27/67/dfa980ac7f0d509d54ea0d5a486d2bb4b80c3f1bb22b66e6a05d3efaf6c0/xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca", size = 448484, upload-time = "2025-10-02T14:36:04.828Z" },
{ url = "https://files.pythonhosted.org/packages/8c/63/8ffc2cc97e811c0ca5d00ab36604b3ea6f4254f20b7bc658ca825ce6c954/xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a", size = 196162, upload-time = "2025-10-02T14:36:06.182Z" },
{ url = "https://files.pythonhosted.org/packages/4b/77/07f0e7a3edd11a6097e990f6e5b815b6592459cb16dae990d967693e6ea9/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99", size = 213007, upload-time = "2025-10-02T14:36:07.733Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d8/bc5fa0d152837117eb0bef6f83f956c509332ce133c91c63ce07ee7c4873/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3", size = 200956, upload-time = "2025-10-02T14:36:09.106Z" },
{ url = "https://files.pythonhosted.org/packages/26/a5/d749334130de9411783873e9b98ecc46688dad5db64ca6e04b02acc8b473/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6", size = 213401, upload-time = "2025-10-02T14:36:10.585Z" },
{ url = "https://files.pythonhosted.org/packages/89/72/abed959c956a4bfc72b58c0384bb7940663c678127538634d896b1195c10/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93", size = 417083, upload-time = "2025-10-02T14:36:12.276Z" },
{ url = "https://files.pythonhosted.org/packages/0c/b3/62fd2b586283b7d7d665fb98e266decadf31f058f1cf6c478741f68af0cb/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518", size = 193913, upload-time = "2025-10-02T14:36:14.025Z" },
{ url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" },
{ url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" },
{ url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" },
{ url = "https://files.pythonhosted.org/packages/93/1e/8aec23647a34a249f62e2398c42955acd9b4c6ed5cf08cbea94dc46f78d2/xxhash-3.6.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f7b7e2ec26c1666ad5fc9dbfa426a6a3367ceaf79db5dd76264659d509d73b0", size = 30662, upload-time = "2025-10-02T14:37:01.743Z" },
{ url = "https://files.pythonhosted.org/packages/b8/0b/b14510b38ba91caf43006209db846a696ceea6a847a0c9ba0a5b1adc53d6/xxhash-3.6.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5dc1e14d14fa0f5789ec29a7062004b5933964bb9b02aae6622b8f530dc40296", size = 41056, upload-time = "2025-10-02T14:37:02.879Z" },
{ url = "https://files.pythonhosted.org/packages/50/55/15a7b8a56590e66ccd374bbfa3f9ffc45b810886c8c3b614e3f90bd2367c/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:881b47fc47e051b37d94d13e7455131054b56749b91b508b0907eb07900d1c13", size = 36251, upload-time = "2025-10-02T14:37:04.44Z" },
{ url = "https://files.pythonhosted.org/packages/62/b2/5ac99a041a29e58e95f907876b04f7067a0242cb85b5f39e726153981503/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6dc31591899f5e5666f04cc2e529e69b4072827085c1ef15294d91a004bc1bd", size = 32481, upload-time = "2025-10-02T14:37:05.869Z" },
{ url = "https://files.pythonhosted.org/packages/7b/d9/8d95e906764a386a3d3b596f3c68bb63687dfca806373509f51ce8eea81f/xxhash-3.6.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:15e0dac10eb9309508bfc41f7f9deaa7755c69e35af835db9cb10751adebc35d", size = 31565, upload-time = "2025-10-02T14:37:06.966Z" },
]
[[package]]
name = "zstandard"
version = "0.25.0"
+5
View File
@@ -2,6 +2,11 @@
Implementation of LangGraph CheckpointSaver that uses SQLite DB (both sync and async, via `aiosqlite`)
## Security
> [!IMPORTANT]
> Set `LANGGRAPH_STRICT_MSGPACK=true` or pass an explicit `allowed_msgpack_modules` list when creating your checkpointer. This restricts checkpoint deserialization to known-safe types, preventing code execution if the database is compromised. See the [langgraph-checkpoint README](https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint#serde) for details.
## Usage
```python
@@ -4,7 +4,7 @@ import json
import random
import sqlite3
import threading
from collections.abc import AsyncIterator, Iterator, Sequence
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
from contextlib import closing, contextmanager
from typing import Any, cast
@@ -16,12 +16,19 @@ from langgraph.checkpoint.base import (
Checkpoint,
CheckpointMetadata,
CheckpointTuple,
DeltaChannelHistory,
SerializerProtocol,
get_checkpoint_id,
get_checkpoint_metadata,
)
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
from langgraph.checkpoint.sqlite._delta import (
DELTA_STAGE1_SQL,
build_delta_channels_writes_history,
build_delta_stage2_sql,
step_walk_with_row,
)
from langgraph.checkpoint.sqlite.utils import search_where
_AIO_ERROR_MSG = (
@@ -493,6 +500,88 @@ class SqliteSaver(BaseCheckpointSaver[str]):
(str(thread_id),),
)
def get_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Fast-path override of `BaseCheckpointSaver.get_delta_channel_history`.
Two-stage query:
* Stage 1 (paged): newest-first slice of `checkpoints` returning
`(checkpoint_id, parent_checkpoint_id, type, checkpoint)` per
ancestor. Sqlite has no JSONB, so we ship the full serialized
checkpoint blob and inspect `channel_values` in Python. Pages
newest-first by `checkpoint_id` with a `< cursor` predicate;
page size is `DELTA_PAGE_SIZE`. Stops paging when every channel
has found its seed or the chain is exhausted.
* Stage 2 (per-channel UNION ALL): one branch per channel reading
`writes` filtered to that channel's specific `chain_cids`. No
separate seed-blob fetch — sqlite stores `channel_values` inline
in the checkpoint blob, so seeds come back from stage 1.
"""
if not channels:
return {}
channels = list(channels)
thread_id = str(config["configurable"]["thread_id"])
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_id = get_checkpoint_id(config)
if checkpoint_id is None:
target = self.get_tuple(config)
if target is None:
return {ch: {"writes": []} for ch in channels}
checkpoint_id = target.config["configurable"]["checkpoint_id"]
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
seed_val_by_ch: dict[str, Any] = {}
walk_state: dict[str, Any] = {}
seeded: set[str] = set()
with self.cursor(transaction=False) as cur:
cur.execute(DELTA_STAGE1_SQL, (thread_id, checkpoint_ns, checkpoint_id))
for row in cur:
cid, parent_cid, type_tag, blob = row
if step_walk_with_row(
cid=cid,
parent_cid=parent_cid,
type_tag=type_tag,
blob=blob,
target_id=checkpoint_id,
serde=self.serde,
chain_by_ch=chain_by_ch,
seed_val_by_ch=seed_val_by_ch,
walk_state=walk_state,
seeded=seeded,
channels=channels,
):
break
channels_with_chain = [ch for ch in channels if chain_by_ch[ch]]
stage2_sql = build_delta_stage2_sql(
chain_lens=[len(chain_by_ch[ch]) for ch in channels_with_chain],
)
if stage2_sql:
stage2_params: list[Any] = []
for ch in channels_with_chain:
stage2_params.extend(
[thread_id, checkpoint_ns, ch, *chain_by_ch[ch]]
)
cur.execute(stage2_sql, stage2_params)
stage2_rows = cast(
"list[tuple[str, str, str, int, str, bytes]]", cur.fetchall()
)
else:
stage2_rows = []
return build_delta_channels_writes_history(
channels=channels,
chain_by_ch=chain_by_ch,
seed_val_by_ch=seed_val_by_ch,
seeded=seeded,
stage2_rows=stage2_rows,
serde=self.serde,
)
async def aget_tuple(self, config: RunnableConfig) -> CheckpointTuple | None:
"""Get a checkpoint tuple from the database asynchronously.
@@ -0,0 +1,172 @@
"""Shared helpers for `get_delta_channel_history` on sqlite savers.
Mirrors the two-stage shape of `BasePostgresSaver` (ancestor walk +
per-channel UNION ALL writes fetch), but adapted for sqlite's
constraints. The structural differences:
* No JSONB — to inspect `channel_values` for a checkpoint we must
deserialize the full blob. Stage 1 streams the cursor row-by-row and
deserializes only the rows the merged walk visits, freeing each blob
before advancing.
* No separate blob table — `channel_values` lives inline in the
checkpoint, 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.
The streaming design keeps peak in-flight memory at roughly one
deserialized checkpoint at a time, instead of holding the entire
ancestor chain's worth of raw blobs as a `fetchall()`-materialized list.
"""
from __future__ import annotations
from collections.abc import Mapping, Sequence
from typing import Any
from langgraph.checkpoint.base import DeltaChannelHistory, PendingWrite
# Stage 1 streams ancestors of `target_cid` newest-first. The `<=`
# predicate keeps target itself in the stream so we can read its
# `parent_checkpoint_id` from the first row without a separate lookup;
# the caller skips target's own writes/seed (matches the
# `BaseCheckpointSaver` contract).
DELTA_STAGE1_SQL = (
"SELECT checkpoint_id, parent_checkpoint_id, type, checkpoint "
"FROM checkpoints "
"WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id <= ? "
"ORDER BY checkpoint_id DESC"
)
def build_delta_stage2_sql(*, chain_lens: Sequence[int]) -> str:
"""Stage-2 per-channel UNION ALL fetching writes from `writes`.
One branch per channel with a non-empty chain. Each branch inlines its
own `IN (?, ?, ...)` placeholder list because sqlite has no array-bind
equivalent of postgres's `= ANY(%s)`. Caller passes parameters in
matching order: `[thread_id, checkpoint_ns, channel, *chain_cids]` per
branch.
Returns an empty string when no channel has a chain (caller skips
executing in that case). Per-channel UNION ALL avoids the over-fetch
of a single `channel = ANY(channels)` filter when channels have
different chain depths — same rationale as postgres.
"""
branches: list[str] = []
for n in chain_lens:
cid_placeholders = ",".join("?" * n)
branches.append(
"SELECT checkpoint_id, channel, task_id, idx, type, value "
"FROM writes "
"WHERE thread_id = ? AND checkpoint_ns = ? AND channel = ? "
f"AND checkpoint_id IN ({cid_placeholders})"
)
return " UNION ALL ".join(branches)
def step_walk_with_row(
*,
cid: str,
parent_cid: str | None,
type_tag: str,
blob: bytes,
target_id: str,
serde: Any,
chain_by_ch: dict[str, list[str]],
seed_val_by_ch: dict[str, Any],
walk_state: dict[str, Any],
seeded: set[str],
channels: Sequence[str],
) -> bool:
"""Process one streamed stage-1 row in the merged ancestor walk.
The cursor returns (cid, parent_cid, type, blob) rows in
`checkpoint_id` DESC order starting at target. The first row is
target itself; we read its parent_cid to seed the walk and otherwise
skip it (target's own writes/seed are not part of the contract).
For each subsequent row, if `cid` matches the walk's current
position, we deserialize the blob, append the cid to every
not-yet-seeded channel's chain, and check `channel_values` for
seeds. The deserialized checkpoint is dropped before advancing — no
cross-row cache, so peak in-flight is one deserialized checkpoint.
Off-path rows (different branch on the same thread) advance the
cursor without doing any work.
Returns True when every requested channel is seeded — the caller
can stop iterating and close the cursor.
"""
if "started" not in walk_state:
if cid == target_id:
walk_state["started"] = True
walk_state["cur_cid"] = parent_cid
walk_state["active"] = {ch for ch in channels if ch not in seeded}
# Not target yet (or target not present): keep streaming.
return False
active: set[str] = walk_state["active"]
if not active:
return True
if cid != walk_state["cur_cid"]:
# Off-path row from a sibling branch — skip without deserializing.
return False
for ch in active:
chain_by_ch[ch].append(cid)
ckpt = serde.loads_typed((type_tag, blob))
channel_values: Mapping[str, Any] = ckpt.get("channel_values") or {}
for ch in [ch for ch in active if ch in channel_values]:
seed_val_by_ch[ch] = channel_values[ch]
seeded.add(ch)
active.discard(ch)
del ckpt, channel_values
walk_state["cur_cid"] = parent_cid
return not active
def build_delta_channels_writes_history(
*,
channels: Sequence[str],
chain_by_ch: Mapping[str, list[str]],
seed_val_by_ch: Mapping[str, Any],
seeded: set[str],
stage2_rows: Sequence[tuple[str, str, str, int, str, bytes]],
serde: Any,
) -> dict[str, DeltaChannelHistory]:
"""Demux stage-2 rows per channel; produce per-channel histories.
Stage-2 rows are `(checkpoint_id, channel, task_id, idx, type, value)`.
Final write order is oldest→newest globally and `(task_id, idx)` within
a checkpoint, matching the contract on `DeltaChannelHistory.writes`.
`seed` is omitted when the walk reached a true root with no snapshot
found (channel never entered `seeded`); consumers treat absence as
"start empty".
"""
writes_by_ch_by_cid: dict[str, dict[str, list[tuple[str, bytes, str, int]]]] = {
ch: {} for ch in channels
}
for cid, ch, task_id, idx, type_tag, value_blob in stage2_rows:
writes_by_ch_by_cid.setdefault(ch, {}).setdefault(cid, []).append(
(type_tag, value_blob, task_id, idx)
)
for cid_map in writes_by_ch_by_cid.values():
for ws in cid_map.values():
ws.sort(key=lambda w: (w[2], w[3]))
result: dict[str, DeltaChannelHistory] = {}
for ch in channels:
chain_cids = chain_by_ch.get(ch, [])
cid_writes = writes_by_ch_by_cid.get(ch, {})
collected: list[PendingWrite] = []
# Chain is newest-first; iterate oldest-first for the public order.
for cid in reversed(chain_cids):
for type_tag, value_blob, task_id, _idx in cid_writes.get(cid, []):
collected.append(
(task_id, ch, serde.loads_typed((type_tag, value_blob)))
)
entry: DeltaChannelHistory = {"writes": collected}
if ch in seeded:
entry["seed"] = seed_val_by_ch[ch]
result[ch] = entry
return result
@@ -4,7 +4,7 @@ import asyncio
import json
import random
import threading
from collections.abc import AsyncIterator, Callable, Iterator, Sequence
from collections.abc import AsyncIterator, Callable, Iterator, Mapping, Sequence
from contextlib import asynccontextmanager
from typing import Any, TypeVar, cast
@@ -17,12 +17,19 @@ from langgraph.checkpoint.base import (
Checkpoint,
CheckpointMetadata,
CheckpointTuple,
DeltaChannelHistory,
SerializerProtocol,
get_checkpoint_id,
get_checkpoint_metadata,
)
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
from langgraph.checkpoint.sqlite._delta import (
DELTA_STAGE1_SQL,
build_delta_channels_writes_history,
build_delta_stage2_sql,
step_walk_with_row,
)
from langgraph.checkpoint.sqlite.utils import search_where
T = TypeVar("T", bound=Callable)
@@ -272,6 +279,29 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
self.adelete_thread(thread_id), self.loop
).result()
def get_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Sync bridge to `aget_delta_channel_history`.
Mirrors the same cross-thread guard as `get_tuple` /
`delete_thread` — calling from the loop thread raises rather than
deadlocking.
"""
try:
if asyncio.get_running_loop() is self.loop:
raise asyncio.InvalidStateError(
"Synchronous calls to AsyncSqliteSaver are only allowed from a "
"different thread. From the main thread, use the async interface. "
"For example, use `await checkpointer.aget_delta_channel_history(...)`."
)
except RuntimeError:
pass
return asyncio.run_coroutine_threadsafe(
self.aget_delta_channel_history(config=config, channels=channels),
self.loop,
).result()
async def setup(self) -> None:
"""Set up the checkpoint database asynchronously.
@@ -589,6 +619,83 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
)
await self.conn.commit()
async def aget_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Fast-path override of `BaseCheckpointSaver.aget_delta_channel_history`.
See `SqliteSaver.get_delta_channel_history` for design notes; this
is the async equivalent using `aiosqlite` cursors. Stage 1 pages
the parent chain newest-first and Python-deserializes each
checkpoint blob to find per-channel snapshots; stage 2 fetches
only the relevant writes via per-channel UNION ALL.
"""
if not channels:
return {}
channels = list(channels)
await self.setup()
thread_id = str(config["configurable"]["thread_id"])
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_id = get_checkpoint_id(config)
if checkpoint_id is None:
target = await self.aget_tuple(config)
if target is None:
return {ch: {"writes": []} for ch in channels}
checkpoint_id = target.config["configurable"]["checkpoint_id"]
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
seed_val_by_ch: dict[str, Any] = {}
walk_state: dict[str, Any] = {}
seeded: set[str] = set()
async with self.lock, self.conn.cursor() as cur:
await cur.execute(
DELTA_STAGE1_SQL, (thread_id, checkpoint_ns, checkpoint_id)
)
async for row in cur:
cid, parent_cid, type_tag, blob = row
if step_walk_with_row(
cid=cid,
parent_cid=parent_cid,
type_tag=type_tag,
blob=blob,
target_id=checkpoint_id,
serde=self.serde,
chain_by_ch=chain_by_ch,
seed_val_by_ch=seed_val_by_ch,
walk_state=walk_state,
seeded=seeded,
channels=channels,
):
break
channels_with_chain = [ch for ch in channels if chain_by_ch[ch]]
stage2_sql = build_delta_stage2_sql(
chain_lens=[len(chain_by_ch[ch]) for ch in channels_with_chain],
)
if stage2_sql:
stage2_params: list[Any] = []
for ch in channels_with_chain:
stage2_params.extend(
[thread_id, checkpoint_ns, ch, *chain_by_ch[ch]]
)
await cur.execute(stage2_sql, stage2_params)
stage2_rows = cast(
"list[tuple[str, str, str, int, str, bytes]]",
await cur.fetchall(),
)
else:
stage2_rows = []
return build_delta_channels_writes_history(
channels=channels,
chain_by_ch=chain_by_ch,
seed_val_by_ch=seed_val_by_ch,
seeded=seeded,
stage2_rows=stage2_rows,
serde=self.serde,
)
def get_next_version(self, current: str | None, channel: None) -> str:
"""Generate the next version ID for a channel.
+3 -3
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint-sqlite"
version = "3.0.3"
version = "3.1.0"
description = "Library with a SQLite implementation of LangGraph checkpoint saver."
authors = []
requires-python = ">=3.10"
@@ -12,14 +12,14 @@ readme = "README.md"
license = "MIT"
license-files = ['LICENSE']
dependencies = [
"langgraph-checkpoint>=3,<5.0.0",
"langgraph-checkpoint>=4.1.0,<5.0.0",
"aiosqlite>=0.20",
"sqlite-vec>=0.1.6",
]
[project.urls]
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-sqlite"
Twitter = "https://x.com/LangChain"
Twitter = "https://x.com/langchain_oss"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
@@ -0,0 +1,35 @@
"""Run delta-channel conformance capabilities against AsyncSqliteSaver."""
from __future__ import annotations
import pytest
pytest.importorskip(
"langgraph.checkpoint.conformance",
reason="langgraph-checkpoint-conformance not installed",
)
pytest.importorskip("aiosqlite", reason="aiosqlite not installed")
@pytest.mark.asyncio
async def test_delta_channel_conformance():
from langgraph.checkpoint.conformance import validate
from langgraph.checkpoint.conformance.initializer import checkpointer_test
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
@checkpointer_test(name="AsyncSqliteSaver")
async def sqlite_saver():
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
yield saver
report = await validate(
sqlite_saver,
capabilities={
"delta_channel_history",
},
)
for cap, result in report.results.items():
if result.passed is False:
details = "\n".join(result.failures or [])
pytest.fail(f"Capability {cap} failed:\n{details}")
@@ -0,0 +1,170 @@
"""Sqlite-specific migration smoke tests: BinaryOperatorAggregate -> DeltaChannel.
Mirrors `libs/langgraph/tests/test_delta_channel_migration.py` (which
covers `InMemorySaver` + a third-party fallback to the base default
impl). This file exercises the same migration scenario through the
sqlite-specific `SqliteSaver.get_delta_channel_history` override —
specifically that the streaming ancestor walk finds a pre-migration
plain `channel_values[ch]` entry and surfaces it as the `seed`, with
post-migration writes folding on top through the reducer.
Pre-migration checkpoints under `BinaryOperatorAggregate` carry the
full accumulated value at every settled super-step boundary. The
override has to identify those as "real" seeds (not `_DeltaSnapshot`
sentinels) — the saver layer is intentionally delta-agnostic and just
returns whatever is stored in `channel_values[ch]`.
"""
from __future__ import annotations
import operator
from typing import Annotated, Any
import pytest
from langchain_core.runnables import RunnableConfig
# `langgraph` core isn't a dep of `langgraph-checkpoint-sqlite`. Skip the
# whole module rather than importerror-ing in the standalone CI shape.
pytest.importorskip("langgraph.channels.delta", reason="langgraph core not installed")
pytest.importorskip("langgraph.channels.binop", reason="langgraph core not installed")
pytest.importorskip("langgraph.graph", reason="langgraph core not installed")
from langgraph.channels.binop import BinaryOperatorAggregate # type: ignore[import-untyped] # noqa: E402,I001
from langgraph.channels.delta import DeltaChannel # type: ignore[import-untyped] # noqa: E402
from langgraph.graph import END, START, StateGraph # type: ignore[import-untyped] # noqa: E402
from typing_extensions import TypedDict # noqa: E402
from langgraph.checkpoint.sqlite import SqliteSaver # noqa: E402
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver # noqa: E402
pytestmark = pytest.mark.anyio
def _noop(_state: Any) -> dict:
return {}
def _list_concat(state: list, writes: list) -> list:
result = list(state)
for w in writes:
result.extend(w if isinstance(w, list) else [w])
return result
def _binop_graph(checkpointer: Any) -> Any:
class BinopState(TypedDict):
items: Annotated[list, BinaryOperatorAggregate(list, operator.add)]
return (
StateGraph(BinopState)
.add_node("noop", _noop)
.add_edge(START, "noop")
.add_edge("noop", END)
.compile(checkpointer=checkpointer)
)
def _delta_graph(checkpointer: Any) -> Any:
class DeltaState(TypedDict):
items: Annotated[list, DeltaChannel(_list_concat)]
return (
StateGraph(DeltaState)
.add_node("noop", _noop)
.add_edge(START, "noop")
.add_edge("noop", END)
.compile(checkpointer=checkpointer)
)
def _drive(graph: Any, config: RunnableConfig, tag: str, n: int) -> None:
for i in range(n):
graph.invoke({"items": [f"{tag}{i}"]}, config)
async def _adrive(graph: Any, config: RunnableConfig, tag: str, n: int) -> None:
for i in range(n):
await graph.ainvoke({"items": [f"{tag}{i}"]}, config)
def _settled_boundaries(history: list) -> list[tuple[RunnableConfig, list]]:
"""`(config, items)` for every checkpoint with `next == ('__start__',)`
— the stable inter-invoke boundaries that round-trip predictably.
"""
return [
(s.config, list(s.values.get("items", [])))
for s in history
if s.next == ("__start__",)
]
def test_migration_preserves_pre_migration_state_sync() -> None:
"""Drive 3 invokes under `BinaryOperatorAggregate`, swap the
annotation to `DeltaChannel` on the same sqlite-backed thread, and
verify every settled pre-migration boundary round-trips exactly.
The override's streaming walk must identify the plain accumulated
list at each pre-migration ancestor as a valid `seed` even though
no `_DeltaSnapshot` was ever written there.
"""
with SqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "mig-sync"}}
binop = _binop_graph(saver)
_drive(binop, config, "u", 3)
pre_boundaries = _settled_boundaries(list(binop.get_state_history(config)))
assert len(pre_boundaries) >= 2, "expected multiple settled boundaries"
delta = _delta_graph(saver)
for cfg, items in pre_boundaries:
snap = delta.get_state(cfg)
assert list(snap.values.get("items", [])) == items, (
f"snapshot mismatch at {cfg['configurable']['checkpoint_id']}: "
f"expected {items}, got {snap.values.get('items', [])}"
)
def test_migration_continued_thread_folds_deltas_on_seed_sync() -> None:
"""After migration, driving one more super-step extends the
pre-migration accumulated state via the delta reducer — the seed
plus a single new write.
"""
with SqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "mig-continue-sync"}}
binop = _binop_graph(saver)
_drive(binop, config, "u", 3)
pre_history = list(binop.get_state_history(config))
pre_boundaries = _settled_boundaries(pre_history)
# Latest settled boundary — the leaf pre-migration state.
leaf_cfg, leaf_items = pre_boundaries[0]
assert leaf_items, "expected non-empty pre-migration leaf"
delta = _delta_graph(saver)
delta.invoke({"items": ["after-migration"]}, leaf_cfg)
new_state = delta.get_state(config).values["items"]
assert new_state[: len(leaf_items)] == leaf_items
assert "after-migration" in new_state
async def test_migration_preserves_pre_migration_state_async() -> None:
"""Async equivalent of the basic-migration round-trip check on
`AsyncSqliteSaver`."""
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "mig-async"}}
binop = _binop_graph(saver)
await _adrive(binop, config, "u", 3)
pre_history = [s async for s in binop.aget_state_history(config)]
pre_boundaries = _settled_boundaries(pre_history)
assert len(pre_boundaries) >= 2
delta = _delta_graph(saver)
for cfg, items in pre_boundaries:
snap = await delta.aget_state(cfg)
assert list(snap.values.get("items", [])) == items, (
f"async snapshot mismatch at {cfg['configurable']['checkpoint_id']}"
)
@@ -0,0 +1,271 @@
"""Smoke tests for `BaseCheckpointSaver.get_delta_channel_history` on sqlite.
`SqliteSaver` (and `AsyncSqliteSaver`) deliberately don't override the
default implementation in `BaseCheckpointSaver` — these tests pin the
default impl to behave correctly end-to-end against a real persistent
saver and a real `DeltaChannel`-backed graph.
Scenarios covered:
* Empty `channels` argument returns an empty mapping (no I/O).
* On a non-trivial multi-checkpoint thread, per-channel writes come back
oldest→newest.
* When the walk reaches the root without ever finding a stored value,
`seed` is omitted from the entry (consumer treats absence as "start
empty").
* When a `_DeltaSnapshot` blob is present at an ancestor, it is returned
as the `seed`.
* The async saver returns the same shape via `aget_delta_channel_history`.
"""
from __future__ import annotations
import operator
from typing import Annotated, Any
import pytest
from langchain_core.runnables import RunnableConfig
# `langgraph` is not a dep of `langgraph-checkpoint-sqlite`. When tests run
# in the sqlite lib's standalone CI environment without it installed, skip
# the whole module rather than failing at import.
pytest.importorskip("langgraph.channels.delta", reason="langgraph core not installed")
pytest.importorskip("langgraph.graph", reason="langgraph core not installed")
from langgraph.channels.delta import DeltaChannel # type: ignore[import-untyped] # noqa: E402,I001
from langgraph.checkpoint.serde.types import _DeltaSnapshot # noqa: E402
from langgraph.graph import END, START, StateGraph # type: ignore[import-untyped] # noqa: E402
from typing_extensions import TypedDict # noqa: E402
from langgraph.checkpoint.sqlite import SqliteSaver # noqa: E402
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver # noqa: E402
pytestmark = pytest.mark.anyio
# ---------------------------------------------------------------------------
# Graph helpers
# ---------------------------------------------------------------------------
def _noop(_state: Any) -> dict[str, Any]:
return {}
class _DeltaState(TypedDict):
items: Annotated[list, DeltaChannel(operator.add)]
def _delta_graph(checkpointer: Any) -> Any:
return (
StateGraph(_DeltaState)
.add_node("noop", _noop)
.add_edge(START, "noop")
.add_edge("noop", END)
.compile(checkpointer=checkpointer)
)
def _drive(graph: Any, config: RunnableConfig, n: int) -> None:
for i in range(n):
graph.invoke({"items": [f"v{i}"]}, config)
async def _adrive(graph: Any, config: RunnableConfig, n: int) -> None:
for i in range(n):
await graph.ainvoke({"items": [f"v{i}"]}, config)
def _pick_non_root(saver: Any, config: RunnableConfig) -> RunnableConfig:
"""Return a config pointing at a checkpoint that has at least one ancestor.
`get_delta_channel_history` walks the parent chain — calling it on the root
checkpoint produces `writes=[]` and no `seed`, which is uninteresting
for the multi-step assertions below.
"""
history = list(saver.list(config))
assert history, "expected non-empty history"
# `list` yields newest→oldest; the second entry has the first entry
# as its parent, so its parent_config is non-None.
for tup in history:
if tup.parent_config is not None:
return tup.config
raise AssertionError("no checkpoint with a parent in history")
async def _apick_non_root(saver: Any, config: RunnableConfig) -> RunnableConfig:
history = [tup async for tup in saver.alist(config)]
assert history, "expected non-empty history"
for tup in history:
if tup.parent_config is not None:
return tup.config
raise AssertionError("no checkpoint with a parent in history")
# ---------------------------------------------------------------------------
# Sync: SqliteSaver
# ---------------------------------------------------------------------------
def test_empty_channels_returns_empty_mapping_sync() -> None:
"""Empty `channels` short-circuits to `{}` without touching storage."""
with SqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "empty"}}
assert saver.get_delta_channel_history(config=config, channels=[]) == {}
def test_writes_history_oldest_to_newest_sync() -> None:
"""Per-channel writes accumulated across the walk come back oldest→newest."""
with SqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "history-sync"}}
graph = _delta_graph(saver)
_drive(graph, config, 3)
target_cfg = _pick_non_root(saver, config)
result = saver.get_delta_channel_history(config=target_cfg, channels=["items"])
assert "items" in result
entry = result["items"]
assert isinstance(entry["writes"], list)
# If any writes were collected, their values should be in oldest→newest
# order — i.e. tagged 'v0', 'v1', ... matching invoke order.
write_values: list[Any] = []
for _task_id, channel, value in entry["writes"]:
assert channel == "items"
write_values.extend(value if isinstance(value, list) else [value])
# `_drive` invokes with payloads ['v0'], ['v1'], ['v2']. Whatever
# subset shows up in the chain must be a contiguous prefix in order.
for idx, val in enumerate(write_values):
assert val == f"v{idx}", (
f"writes not in oldest→newest order: {write_values}"
)
def test_seed_present_when_snapshot_in_ancestor_sync() -> None:
"""Inserting a `_DeltaSnapshot` blob at an ancestor → walk returns it as `seed`."""
with SqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "seed-sync"}}
graph = _delta_graph(saver)
_drive(graph, config, 2)
# Find the oldest non-root checkpoint, then walk to its parent and
# rewrite that parent's `channel_values["items"]` to a real
# `_DeltaSnapshot`. After this surgery, calling `get_delta_channel_history`
# at the leaf must return the snapshot value as `seed`.
history = list(saver.list(config))
assert len(history) >= 2
leaf_tup = history[0]
# Walk to an ancestor with a parent_config (any non-root will do).
ancestor_tup = next(
(tup for tup in history if tup.parent_config is not None), None
)
assert ancestor_tup is not None
parent_cfg = ancestor_tup.parent_config
assert parent_cfg is not None
parent_tup = saver.get_tuple(parent_cfg)
assert parent_tup is not None
snapshot_value = ["seeded", "items"]
parent_tup.checkpoint["channel_values"]["items"] = _DeltaSnapshot(
snapshot_value
)
# Make sure the channel has a version so the optimized blob lookup
# in any future override has something to hit.
parent_tup.checkpoint["channel_versions"].setdefault("items", 1)
saver.put(
parent_tup.parent_config or {"configurable": parent_cfg["configurable"]},
parent_tup.checkpoint,
parent_tup.metadata,
{},
)
result = saver.get_delta_channel_history(
config=leaf_tup.config, channels=["items"]
)
entry = result["items"]
assert "seed" in entry, f"expected seed to be present, got {entry}"
seed = entry["seed"]
assert isinstance(seed, _DeltaSnapshot), (
f"expected _DeltaSnapshot, got {seed!r}"
)
assert seed.value == snapshot_value
def test_seed_omitted_when_walk_reaches_root_sync() -> None:
"""`get_delta_channel_history` on the root checkpoint → no `seed` key, no writes."""
with SqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "root-sync"}}
graph = _delta_graph(saver)
_drive(graph, config, 1)
history = list(saver.list(config))
# Root is the oldest checkpoint (no parent_config).
root_tup = history[-1]
assert root_tup.parent_config is None
result = saver.get_delta_channel_history(
config=root_tup.config, channels=["items"]
)
entry = result["items"]
assert "seed" not in entry, f"root-walk should have no seed, got {entry}"
assert entry["writes"] == []
# ---------------------------------------------------------------------------
# Async: AsyncSqliteSaver
# ---------------------------------------------------------------------------
async def test_empty_channels_returns_empty_mapping_async() -> None:
"""Async equivalent of the empty-channels short-circuit."""
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "empty-async"}}
assert await saver.aget_delta_channel_history(config=config, channels=[]) == {}
async def test_writes_history_oldest_to_newest_async() -> None:
"""Async equivalent of the oldest→newest ordering check."""
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "history-async"}}
graph = _delta_graph(saver)
await _adrive(graph, config, 3)
target_cfg = await _apick_non_root(saver, config)
result = await saver.aget_delta_channel_history(
config=target_cfg, channels=["items"]
)
assert "items" in result
entry = result["items"]
assert isinstance(entry["writes"], list)
write_values: list[Any] = []
for _task_id, channel, value in entry["writes"]:
assert channel == "items"
write_values.extend(value if isinstance(value, list) else [value])
for idx, val in enumerate(write_values):
assert val == f"v{idx}", (
f"writes not in oldest→newest order: {write_values}"
)
async def test_seed_omitted_when_walk_reaches_root_async() -> None:
"""Async equivalent of the root-walk seed-absence check."""
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
config: RunnableConfig = {"configurable": {"thread_id": "root-async"}}
graph = _delta_graph(saver)
await _adrive(graph, config, 1)
history = [tup async for tup in saver.alist(config)]
root_tup = history[-1]
assert root_tup.parent_config is None
result = await saver.aget_delta_channel_history(
config=root_tup.config, channels=["items"]
)
entry = result["items"]
assert "seed" not in entry, f"root-walk should have no seed, got {entry}"
assert entry["writes"] == []
+214 -78
View File
@@ -1,6 +1,10 @@
version = 1
revision = 3
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.15'",
"python_full_version < '3.15'",
]
[[package]]
name = "aiosqlite"
@@ -249,10 +253,11 @@ wheels = [
[[package]]
name = "langchain-core"
version = "1.2.22"
version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsonpatch" },
{ name = "langchain-protocol" },
{ name = "langsmith" },
{ name = "packaging" },
{ name = "pydantic" },
@@ -261,14 +266,26 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "uuid-utils" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/a3/c4cd6827a1df46c821e7214b7f7b7a28b189e6c9b84ef15c6d629c5e3179/langchain_core-1.2.22.tar.gz", hash = "sha256:8d8f726d03d3652d403da915126626bb6250747e8ba406537d849e68b9f5d058", size = 842487, upload-time = "2026-03-24T18:48:44.9Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/ae/8b74458fc3850ec3d150eb9f45e857db129dafa801fb5cf173dfc9f8bbf3/langchain_core-1.3.3.tar.gz", hash = "sha256:fa510a5db8efdc0c6ff41c0939fb5c00a0183c11f6b84233e892e3227ff69182", size = 915041, upload-time = "2026-05-05T19:02:36.612Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/a6/2ffacf0f1a3788f250e75d0b52a24896c413be11be3a6d42bcdf46fbea48/langchain_core-1.2.22-py3-none-any.whl", hash = "sha256:7e30d586b75918e828833b9ec1efc25465723566845dd652c277baf751e9c04b", size = 506829, upload-time = "2026-03-24T18:48:43.286Z" },
{ url = "https://files.pythonhosted.org/packages/1f/01/4771b7ab2af1d1aba5b710bd8f13d9225c609425214b357590a17b01be77/langchain_core-1.3.3-py3-none-any.whl", hash = "sha256:18aae8506f37da7f74398492279a7d6efcee4f8e23c4c41c7af080eeb7ef7bd1", size = 543857, upload-time = "2026-05-05T19:02:34.52Z" },
]
[[package]]
name = "langchain-protocol"
version = "0.0.15"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4f/24/9777489d6fbbee64af0c8f96d4f840239c408cf694f3394672807dafc490/langchain_protocol-0.0.15.tar.gz", hash = "sha256:9ab2d11ee73944754f10e037e717098d3a6796f0e58afa9cadda6154e7655ade", size = 5862, upload-time = "2026-05-01T22:30:04.748Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/7a/9c97a7b9cbe4c5dc6a44cdb1545450c28f0c8ce89b9c1f0ee7fbad896263/langchain_protocol-0.0.15-py3-none-any.whl", hash = "sha256:461eb794358f83d5e42635a5797799ffec7b4702314e34edf73ac21e75d3ef79", size = 6982, upload-time = "2026-05-01T22:30:03.877Z" },
]
[[package]]
name = "langgraph-checkpoint"
version = "4.0.1"
version = "4.1.0"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -316,7 +333,7 @@ test = [
[[package]]
name = "langgraph-checkpoint-sqlite"
version = "3.0.3"
version = "3.1.0"
source = { editable = "." }
dependencies = [
{ name = "aiosqlite" },
@@ -385,7 +402,7 @@ test = [
[[package]]
name = "langsmith"
version = "0.6.4"
version = "0.7.31"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -395,11 +412,12 @@ dependencies = [
{ name = "requests" },
{ name = "requests-toolbelt" },
{ name = "uuid-utils" },
{ name = "xxhash" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e7/85/9c7933052a997da1b85bc5c774f3865e9b1da1c8d71541ea133178b13229/langsmith-0.6.4.tar.gz", hash = "sha256:36f7223a01c218079fbb17da5e536ebbaf5c1468c028abe070aa3ae59bc99ec8", size = 919964, upload-time = "2026-01-15T20:02:28.873Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e6/11/696019490992db5c87774dc20515529ef42a01e1d770fb754ed6d9b12fb0/langsmith-0.7.31.tar.gz", hash = "sha256:331ee4f7c26bb5be4022b9859b7d7b122cbf8c9d01d9f530114c1914b0349ffb", size = 1178480, upload-time = "2026-04-14T17:55:41.242Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/66/0f/09a6637a7ba777eb307b7c80852d9ee26438e2bdafbad6fcc849ff9d9192/langsmith-0.6.4-py3-none-any.whl", hash = "sha256:ac4835860160be371042c7adbba3cb267bcf8d96a5ea976c33a8a4acad6c5486", size = 283503, upload-time = "2026-01-15T20:02:26.662Z" },
{ url = "https://files.pythonhosted.org/packages/1d/a1/a013cf458c301cda86a213dd153ce0a01c93f1ab5833f951e6a44c9763ce/langsmith-0.7.31-py3-none-any.whl", hash = "sha256:0291d49203f6e80dda011af1afda61eb0595a4d697adb684590a8805e1d61fb6", size = 373276, upload-time = "2026-04-14T17:55:39.677Z" },
]
[[package]]
@@ -489,7 +507,7 @@ wheels = [
[[package]]
name = "mypy"
version = "1.20.0"
version = "1.20.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
@@ -498,51 +516,51 @@ dependencies = [
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/b0089fe7fef0a994ae5ee07029ced0526082c6cfaaa4c10d40a10e33b097/mypy-1.20.0.tar.gz", hash = "sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3", size = 3815028, upload-time = "2026-03-31T16:55:14.959Z" }
sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/a2/a965c8c3fcd4fa8b84ba0d46606181b0d0a1d50f274c67877f3e9ed4882c/mypy-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8", size = 14430138, upload-time = "2026-03-31T16:52:37.843Z" },
{ url = "https://files.pythonhosted.org/packages/53/6e/043477501deeb8eabbab7f1a2f6cac62cfb631806dc1d6862a04a7f5011b/mypy-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a", size = 13311282, upload-time = "2026-03-31T16:55:11.021Z" },
{ url = "https://files.pythonhosted.org/packages/65/aa/bd89b247b83128197a214f29f0632ff3c14f54d4cd70d144d157bd7d7d6e/mypy-1.20.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865", size = 13750889, upload-time = "2026-03-31T16:52:02.909Z" },
{ url = "https://files.pythonhosted.org/packages/fa/9d/2860be7355c45247ccc0be1501c91176318964c2a137bd4743f58ce6200e/mypy-1.20.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca", size = 14619788, upload-time = "2026-03-31T16:50:48.928Z" },
{ url = "https://files.pythonhosted.org/packages/75/7f/3ef3e360c91f3de120f205c8ce405e9caf9fc52ef14b65d37073e322c114/mypy-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018", size = 14918849, upload-time = "2026-03-31T16:51:10.478Z" },
{ url = "https://files.pythonhosted.org/packages/ae/72/af970dfe167ef788df7c5e6109d2ed0229f164432ce828bc9741a4250e64/mypy-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13", size = 10822007, upload-time = "2026-03-31T16:50:25.268Z" },
{ url = "https://files.pythonhosted.org/packages/93/94/ba9065c2ebe5421619aff684b793d953e438a8bfe31a320dd6d1e0706e81/mypy-1.20.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281", size = 9756158, upload-time = "2026-03-31T16:48:36.213Z" },
{ url = "https://files.pythonhosted.org/packages/6e/1c/74cb1d9993236910286865679d1c616b136b2eae468493aa939431eda410/mypy-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b", size = 14343972, upload-time = "2026-03-31T16:49:04.887Z" },
{ url = "https://files.pythonhosted.org/packages/d5/0d/01399515eca280386e308cf57901e68d3a52af18691941b773b3380c1df8/mypy-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367", size = 13225007, upload-time = "2026-03-31T16:50:08.151Z" },
{ url = "https://files.pythonhosted.org/packages/56/ac/b4ba5094fb2d7fe9d2037cd8d18bbe02bcf68fd22ab9ff013f55e57ba095/mypy-1.20.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62", size = 13663752, upload-time = "2026-03-31T16:49:26.064Z" },
{ url = "https://files.pythonhosted.org/packages/db/a7/460678d3cf7da252d2288dad0c602294b6ec22a91932ec368cc11e44bb6e/mypy-1.20.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0", size = 14532265, upload-time = "2026-03-31T16:53:55.077Z" },
{ url = "https://files.pythonhosted.org/packages/a3/3e/051cca8166cf0438ae3ea80e0e7c030d7a8ab98dffc93f80a1aa3f23c1a2/mypy-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f", size = 14768476, upload-time = "2026-03-31T16:50:34.587Z" },
{ url = "https://files.pythonhosted.org/packages/be/66/8e02ec184f852ed5c4abb805583305db475930854e09964b55e107cdcbc4/mypy-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e", size = 10818226, upload-time = "2026-03-31T16:53:15.624Z" },
{ url = "https://files.pythonhosted.org/packages/13/4b/383ad1924b28f41e4879a74151e7a5451123330d45652da359f9183bcd45/mypy-1.20.0-cp311-cp311-win_arm64.whl", hash = "sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442", size = 9750091, upload-time = "2026-03-31T16:54:12.162Z" },
{ url = "https://files.pythonhosted.org/packages/be/dd/3afa29b58c2e57c79116ed55d700721c3c3b15955e2b6251dd165d377c0e/mypy-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214", size = 14509525, upload-time = "2026-03-31T16:55:01.824Z" },
{ url = "https://files.pythonhosted.org/packages/54/eb/227b516ab8cad9f2a13c5e7a98d28cd6aa75e9c83e82776ae6c1c4c046c7/mypy-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e", size = 13326469, upload-time = "2026-03-31T16:51:41.23Z" },
{ url = "https://files.pythonhosted.org/packages/57/d4/1ddb799860c1b5ac6117ec307b965f65deeb47044395ff01ab793248a591/mypy-1.20.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651", size = 13705953, upload-time = "2026-03-31T16:48:55.69Z" },
{ url = "https://files.pythonhosted.org/packages/c5/b7/54a720f565a87b893182a2a393370289ae7149e4715859e10e1c05e49154/mypy-1.20.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5", size = 14710363, upload-time = "2026-03-31T16:53:26.948Z" },
{ url = "https://files.pythonhosted.org/packages/b2/2a/74810274848d061f8a8ea4ac23aaad43bd3d8c1882457999c2e568341c57/mypy-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78", size = 14947005, upload-time = "2026-03-31T16:50:17.591Z" },
{ url = "https://files.pythonhosted.org/packages/77/91/21b8ba75f958bcda75690951ce6fa6b7138b03471618959529d74b8544e2/mypy-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489", size = 10880616, upload-time = "2026-03-31T16:52:19.986Z" },
{ url = "https://files.pythonhosted.org/packages/8a/15/3d8198ef97c1ca03aea010cce4f1d4f3bc5d9849e8c0140111ca2ead9fdd/mypy-1.20.0-cp312-cp312-win_arm64.whl", hash = "sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33", size = 9813091, upload-time = "2026-03-31T16:53:44.385Z" },
{ url = "https://files.pythonhosted.org/packages/d6/a7/f64ea7bd592fa431cb597418b6dec4a47f7d0c36325fec7ac67bc8402b94/mypy-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134", size = 14485344, upload-time = "2026-03-31T16:49:16.78Z" },
{ url = "https://files.pythonhosted.org/packages/bb/72/8927d84cfc90c6abea6e96663576e2e417589347eb538749a464c4c218a0/mypy-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c", size = 13327400, upload-time = "2026-03-31T16:53:08.02Z" },
{ url = "https://files.pythonhosted.org/packages/ab/4a/11ab99f9afa41aa350178d24a7d2da17043228ea10f6456523f64b5a6cf6/mypy-1.20.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe", size = 13706384, upload-time = "2026-03-31T16:52:28.577Z" },
{ url = "https://files.pythonhosted.org/packages/42/79/694ca73979cfb3535ebfe78733844cd5aff2e63304f59bf90585110d975a/mypy-1.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f", size = 14700378, upload-time = "2026-03-31T16:48:45.527Z" },
{ url = "https://files.pythonhosted.org/packages/84/24/a022ccab3a46e3d2cdf2e0e260648633640eb396c7e75d5a42818a8d3971/mypy-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726", size = 14932170, upload-time = "2026-03-31T16:49:36.038Z" },
{ url = "https://files.pythonhosted.org/packages/d8/9b/549228d88f574d04117e736f55958bd4908f980f9f5700a07aeb85df005b/mypy-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69", size = 10888526, upload-time = "2026-03-31T16:50:59.827Z" },
{ url = "https://files.pythonhosted.org/packages/91/17/15095c0e54a8bc04d22d4ff06b2139d5f142c2e87520b4e39010c4862771/mypy-1.20.0-cp313-cp313-win_arm64.whl", hash = "sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e", size = 9816456, upload-time = "2026-03-31T16:49:59.537Z" },
{ url = "https://files.pythonhosted.org/packages/4e/0e/6ca4a84cbed9e62384bc0b2974c90395ece5ed672393e553996501625fc5/mypy-1.20.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948", size = 14483331, upload-time = "2026-03-31T16:52:57.999Z" },
{ url = "https://files.pythonhosted.org/packages/7d/c5/5fe9d8a729dd9605064691816243ae6c49fde0bd28f6e5e17f6a24203c43/mypy-1.20.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5", size = 13342047, upload-time = "2026-03-31T16:54:21.555Z" },
{ url = "https://files.pythonhosted.org/packages/4c/33/e18bcfa338ca4e6b2771c85d4c5203e627d0c69d9de5c1a2cf2ba13320ba/mypy-1.20.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188", size = 13719585, upload-time = "2026-03-31T16:51:53.89Z" },
{ url = "https://files.pythonhosted.org/packages/6b/8d/93491ff7b79419edc7eabf95cb3b3f7490e2e574b2855c7c7e7394ff933f/mypy-1.20.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83", size = 14685075, upload-time = "2026-03-31T16:54:04.464Z" },
{ url = "https://files.pythonhosted.org/packages/b5/9d/d924b38a4923f8d164bf2b4ec98bf13beaf6e10a5348b4b137eadae40a6e/mypy-1.20.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2", size = 14919141, upload-time = "2026-03-31T16:54:51.785Z" },
{ url = "https://files.pythonhosted.org/packages/59/98/1da9977016678c0b99d43afe52ed00bb3c1a0c4c995d3e6acca1a6ebb9b4/mypy-1.20.0-cp314-cp314-win_amd64.whl", hash = "sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732", size = 11050925, upload-time = "2026-03-31T16:51:30.758Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e3/ba0b7a3143e49a9c4f5967dde6ea4bf8e0b10ecbbcca69af84027160ee89/mypy-1.20.0-cp314-cp314-win_arm64.whl", hash = "sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef", size = 10001089, upload-time = "2026-03-31T16:49:43.632Z" },
{ url = "https://files.pythonhosted.org/packages/12/28/e617e67b3be9d213cda7277913269c874eb26472489f95d09d89765ce2d8/mypy-1.20.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1", size = 15534710, upload-time = "2026-03-31T16:52:12.506Z" },
{ url = "https://files.pythonhosted.org/packages/6e/0c/3b5f2d3e45dc7169b811adce8451679d9430399d03b168f9b0489f43adaa/mypy-1.20.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436", size = 14393013, upload-time = "2026-03-31T16:54:41.186Z" },
{ url = "https://files.pythonhosted.org/packages/a3/49/edc8b0aa145cc09c1c74f7ce2858eead9329931dcbbb26e2ad40906daa4e/mypy-1.20.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6", size = 15047240, upload-time = "2026-03-31T16:54:31.955Z" },
{ url = "https://files.pythonhosted.org/packages/42/37/a946bb416e37a57fa752b3100fd5ede0e28df94f92366d1716555d47c454/mypy-1.20.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526", size = 15858565, upload-time = "2026-03-31T16:53:36.997Z" },
{ url = "https://files.pythonhosted.org/packages/2f/99/7690b5b5b552db1bd4ff362e4c0eb3107b98d680835e65823fbe888c8b78/mypy-1.20.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787", size = 16087874, upload-time = "2026-03-31T16:52:48.313Z" },
{ url = "https://files.pythonhosted.org/packages/aa/76/53e893a498138066acd28192b77495c9357e5a58cc4be753182846b43315/mypy-1.20.0-cp314-cp314t-win_amd64.whl", hash = "sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb", size = 12572380, upload-time = "2026-03-31T16:49:52.454Z" },
{ url = "https://files.pythonhosted.org/packages/76/9c/6dbdae21f01b7aacddc2c0bbf3c5557aa547827fdf271770fe1e521e7093/mypy-1.20.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd", size = 10381174, upload-time = "2026-03-31T16:51:20.179Z" },
{ url = "https://files.pythonhosted.org/packages/21/66/4d734961ce167f0fd8380769b3b7c06dbdd6ff54c2190f3f2ecd22528158/mypy-1.20.0-py3-none-any.whl", hash = "sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e", size = 2636365, upload-time = "2026-03-31T16:51:44.911Z" },
{ url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" },
{ url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" },
{ url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" },
{ url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" },
{ url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" },
{ url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" },
{ url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" },
{ url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" },
{ url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" },
{ url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" },
{ url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" },
{ url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" },
{ url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" },
{ url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" },
{ url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" },
{ url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" },
{ url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" },
{ url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" },
{ url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" },
{ url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" },
{ url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" },
{ url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" },
{ url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" },
{ url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" },
{ url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" },
{ url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" },
{ url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" },
{ url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" },
{ url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" },
{ url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" },
{ url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" },
{ url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" },
{ url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" },
{ url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" },
{ url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" },
{ url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" },
{ url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" },
{ url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" },
{ url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" },
{ url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" },
{ url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" },
{ url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" },
]
[[package]]
@@ -862,7 +880,7 @@ wheels = [
[[package]]
name = "pytest"
version = "9.0.2"
version = "9.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -873,9 +891,9 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
]
[[package]]
@@ -1022,27 +1040,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.15.8"
version = "0.15.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" }
sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" },
{ url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" },
{ url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" },
{ url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" },
{ url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" },
{ url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" },
{ url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" },
{ url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" },
{ url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" },
{ url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" },
{ url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" },
{ url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" },
{ url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" },
{ url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" },
{ url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" },
{ url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" },
{ url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" },
{ url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" },
{ url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" },
{ url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" },
{ url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" },
{ url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" },
{ url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" },
{ url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" },
{ url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" },
{ url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" },
{ url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" },
{ url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" },
{ url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" },
{ url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" },
{ url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" },
{ url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" },
{ url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" },
{ url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" },
]
[[package]]
@@ -1143,11 +1161,11 @@ wheels = [
[[package]]
name = "urllib3"
version = "2.6.3"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
]
[[package]]
@@ -1211,6 +1229,124 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
]
[[package]]
name = "xxhash"
version = "3.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" },
{ url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" },
{ url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" },
{ url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" },
{ url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" },
{ url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" },
{ url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" },
{ url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" },
{ url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" },
{ url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" },
{ url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" },
{ url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" },
{ url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" },
{ url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" },
{ url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" },
{ url = "https://files.pythonhosted.org/packages/17/d4/cc2f0400e9154df4b9964249da78ebd72f318e35ccc425e9f403c392f22a/xxhash-3.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b47bbd8cf2d72797f3c2772eaaac0ded3d3af26481a26d7d7d41dc2d3c46b04a", size = 32844, upload-time = "2025-10-02T14:34:14.037Z" },
{ url = "https://files.pythonhosted.org/packages/5e/ec/1cc11cd13e26ea8bc3cb4af4eaadd8d46d5014aebb67be3f71fb0b68802a/xxhash-3.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2b6821e94346f96db75abaa6e255706fb06ebd530899ed76d32cd99f20dc52fa", size = 30809, upload-time = "2025-10-02T14:34:15.484Z" },
{ url = "https://files.pythonhosted.org/packages/04/5f/19fe357ea348d98ca22f456f75a30ac0916b51c753e1f8b2e0e6fb884cce/xxhash-3.6.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d0a9751f71a1a65ce3584e9cae4467651c7e70c9d31017fa57574583a4540248", size = 194665, upload-time = "2025-10-02T14:34:16.541Z" },
{ url = "https://files.pythonhosted.org/packages/90/3b/d1f1a8f5442a5fd8beedae110c5af7604dc37349a8e16519c13c19a9a2de/xxhash-3.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b29ee68625ab37b04c0b40c3fafdf24d2f75ccd778333cfb698f65f6c463f62", size = 213550, upload-time = "2025-10-02T14:34:17.878Z" },
{ url = "https://files.pythonhosted.org/packages/c4/ef/3a9b05eb527457d5db13a135a2ae1a26c80fecd624d20f3e8dcc4cb170f3/xxhash-3.6.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6812c25fe0d6c36a46ccb002f40f27ac903bf18af9f6dd8f9669cb4d176ab18f", size = 212384, upload-time = "2025-10-02T14:34:19.182Z" },
{ url = "https://files.pythonhosted.org/packages/0f/18/ccc194ee698c6c623acbf0f8c2969811a8a4b6185af5e824cd27b9e4fd3e/xxhash-3.6.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4ccbff013972390b51a18ef1255ef5ac125c92dc9143b2d1909f59abc765540e", size = 445749, upload-time = "2025-10-02T14:34:20.659Z" },
{ url = "https://files.pythonhosted.org/packages/a5/86/cf2c0321dc3940a7aa73076f4fd677a0fb3e405cb297ead7d864fd90847e/xxhash-3.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:297b7fbf86c82c550e12e8fb71968b3f033d27b874276ba3624ea868c11165a8", size = 193880, upload-time = "2025-10-02T14:34:22.431Z" },
{ url = "https://files.pythonhosted.org/packages/82/fb/96213c8560e6f948a1ecc9a7613f8032b19ee45f747f4fca4eb31bb6d6ed/xxhash-3.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dea26ae1eb293db089798d3973a5fc928a18fdd97cc8801226fae705b02b14b0", size = 210912, upload-time = "2025-10-02T14:34:23.937Z" },
{ url = "https://files.pythonhosted.org/packages/40/aa/4395e669b0606a096d6788f40dbdf2b819d6773aa290c19e6e83cbfc312f/xxhash-3.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7a0b169aafb98f4284f73635a8e93f0735f9cbde17bd5ec332480484241aaa77", size = 198654, upload-time = "2025-10-02T14:34:25.644Z" },
{ url = "https://files.pythonhosted.org/packages/67/74/b044fcd6b3d89e9b1b665924d85d3f400636c23590226feb1eb09e1176ce/xxhash-3.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:08d45aef063a4531b785cd72de4887766d01dc8f362a515693df349fdb825e0c", size = 210867, upload-time = "2025-10-02T14:34:27.203Z" },
{ url = "https://files.pythonhosted.org/packages/bc/fd/3ce73bf753b08cb19daee1eb14aa0d7fe331f8da9c02dd95316ddfe5275e/xxhash-3.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:929142361a48ee07f09121fe9e96a84950e8d4df3bb298ca5d88061969f34d7b", size = 414012, upload-time = "2025-10-02T14:34:28.409Z" },
{ url = "https://files.pythonhosted.org/packages/ba/b3/5a4241309217c5c876f156b10778f3ab3af7ba7e3259e6d5f5c7d0129eb2/xxhash-3.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51312c768403d8540487dbbfb557454cfc55589bbde6424456951f7fcd4facb3", size = 191409, upload-time = "2025-10-02T14:34:29.696Z" },
{ url = "https://files.pythonhosted.org/packages/c0/01/99bfbc15fb9abb9a72b088c1d95219fc4782b7d01fc835bd5744d66dd0b8/xxhash-3.6.0-cp311-cp311-win32.whl", hash = "sha256:d1927a69feddc24c987b337ce81ac15c4720955b667fe9b588e02254b80446fd", size = 30574, upload-time = "2025-10-02T14:34:31.028Z" },
{ url = "https://files.pythonhosted.org/packages/65/79/9d24d7f53819fe301b231044ea362ce64e86c74f6e8c8e51320de248b3e5/xxhash-3.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:26734cdc2d4ffe449b41d186bbeac416f704a482ed835d375a5c0cb02bc63fef", size = 31481, upload-time = "2025-10-02T14:34:32.062Z" },
{ url = "https://files.pythonhosted.org/packages/30/4e/15cd0e3e8772071344eab2961ce83f6e485111fed8beb491a3f1ce100270/xxhash-3.6.0-cp311-cp311-win_arm64.whl", hash = "sha256:d72f67ef8bf36e05f5b6c65e8524f265bd61071471cd4cf1d36743ebeeeb06b7", size = 27861, upload-time = "2025-10-02T14:34:33.555Z" },
{ url = "https://files.pythonhosted.org/packages/9a/07/d9412f3d7d462347e4511181dea65e47e0d0e16e26fbee2ea86a2aefb657/xxhash-3.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01362c4331775398e7bb34e3ab403bc9ee9f7c497bc7dee6272114055277dd3c", size = 32744, upload-time = "2025-10-02T14:34:34.622Z" },
{ url = "https://files.pythonhosted.org/packages/79/35/0429ee11d035fc33abe32dca1b2b69e8c18d236547b9a9b72c1929189b9a/xxhash-3.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b2df81a23f8cb99656378e72501b2cb41b1827c0f5a86f87d6b06b69f9f204", size = 30816, upload-time = "2025-10-02T14:34:36.043Z" },
{ url = "https://files.pythonhosted.org/packages/b7/f2/57eb99aa0f7d98624c0932c5b9a170e1806406cdbcdb510546634a1359e0/xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490", size = 194035, upload-time = "2025-10-02T14:34:37.354Z" },
{ url = "https://files.pythonhosted.org/packages/4c/ed/6224ba353690d73af7a3f1c7cdb1fc1b002e38f783cb991ae338e1eb3d79/xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2", size = 212914, upload-time = "2025-10-02T14:34:38.6Z" },
{ url = "https://files.pythonhosted.org/packages/38/86/fb6b6130d8dd6b8942cc17ab4d90e223653a89aa32ad2776f8af7064ed13/xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa", size = 212163, upload-time = "2025-10-02T14:34:39.872Z" },
{ url = "https://files.pythonhosted.org/packages/ee/dc/e84875682b0593e884ad73b2d40767b5790d417bde603cceb6878901d647/xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0", size = 445411, upload-time = "2025-10-02T14:34:41.569Z" },
{ url = "https://files.pythonhosted.org/packages/11/4f/426f91b96701ec2f37bb2b8cec664eff4f658a11f3fa9d94f0a887ea6d2b/xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2", size = 193883, upload-time = "2025-10-02T14:34:43.249Z" },
{ url = "https://files.pythonhosted.org/packages/53/5a/ddbb83eee8e28b778eacfc5a85c969673e4023cdeedcfcef61f36731610b/xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9", size = 210392, upload-time = "2025-10-02T14:34:45.042Z" },
{ url = "https://files.pythonhosted.org/packages/1e/c2/ff69efd07c8c074ccdf0a4f36fcdd3d27363665bcdf4ba399abebe643465/xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e", size = 197898, upload-time = "2025-10-02T14:34:46.302Z" },
{ url = "https://files.pythonhosted.org/packages/58/ca/faa05ac19b3b622c7c9317ac3e23954187516298a091eb02c976d0d3dd45/xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374", size = 210655, upload-time = "2025-10-02T14:34:47.571Z" },
{ url = "https://files.pythonhosted.org/packages/d4/7a/06aa7482345480cc0cb597f5c875b11a82c3953f534394f620b0be2f700c/xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d", size = 414001, upload-time = "2025-10-02T14:34:49.273Z" },
{ url = "https://files.pythonhosted.org/packages/23/07/63ffb386cd47029aa2916b3d2f454e6cc5b9f5c5ada3790377d5430084e7/xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae", size = 191431, upload-time = "2025-10-02T14:34:50.798Z" },
{ url = "https://files.pythonhosted.org/packages/0f/93/14fde614cadb4ddf5e7cebf8918b7e8fac5ae7861c1875964f17e678205c/xxhash-3.6.0-cp312-cp312-win32.whl", hash = "sha256:50fc255f39428a27299c20e280d6193d8b63b8ef8028995323bf834a026b4fbb", size = 30617, upload-time = "2025-10-02T14:34:51.954Z" },
{ url = "https://files.pythonhosted.org/packages/13/5d/0d125536cbe7565a83d06e43783389ecae0c0f2ed037b48ede185de477c0/xxhash-3.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0f2ab8c715630565ab8991b536ecded9416d615538be8ecddce43ccf26cbc7c", size = 31534, upload-time = "2025-10-02T14:34:53.276Z" },
{ url = "https://files.pythonhosted.org/packages/54/85/6ec269b0952ec7e36ba019125982cf11d91256a778c7c3f98a4c5043d283/xxhash-3.6.0-cp312-cp312-win_arm64.whl", hash = "sha256:eae5c13f3bc455a3bbb68bdc513912dc7356de7e2280363ea235f71f54064829", size = 27876, upload-time = "2025-10-02T14:34:54.371Z" },
{ url = "https://files.pythonhosted.org/packages/33/76/35d05267ac82f53ae9b0e554da7c5e281ee61f3cad44c743f0fcd354f211/xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec", size = 32738, upload-time = "2025-10-02T14:34:55.839Z" },
{ url = "https://files.pythonhosted.org/packages/31/a8/3fbce1cd96534a95e35d5120637bf29b0d7f5d8fa2f6374e31b4156dd419/xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1", size = 30821, upload-time = "2025-10-02T14:34:57.219Z" },
{ url = "https://files.pythonhosted.org/packages/0c/ea/d387530ca7ecfa183cb358027f1833297c6ac6098223fd14f9782cd0015c/xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6", size = 194127, upload-time = "2025-10-02T14:34:59.21Z" },
{ url = "https://files.pythonhosted.org/packages/ba/0c/71435dcb99874b09a43b8d7c54071e600a7481e42b3e3ce1eb5226a5711a/xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263", size = 212975, upload-time = "2025-10-02T14:35:00.816Z" },
{ url = "https://files.pythonhosted.org/packages/84/7a/c2b3d071e4bb4a90b7057228a99b10d51744878f4a8a6dd643c8bd897620/xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546", size = 212241, upload-time = "2025-10-02T14:35:02.207Z" },
{ url = "https://files.pythonhosted.org/packages/81/5f/640b6eac0128e215f177df99eadcd0f1b7c42c274ab6a394a05059694c5a/xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89", size = 445471, upload-time = "2025-10-02T14:35:03.61Z" },
{ url = "https://files.pythonhosted.org/packages/5e/1e/3c3d3ef071b051cc3abbe3721ffb8365033a172613c04af2da89d5548a87/xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d", size = 193936, upload-time = "2025-10-02T14:35:05.013Z" },
{ url = "https://files.pythonhosted.org/packages/2c/bd/4a5f68381939219abfe1c22a9e3a5854a4f6f6f3c4983a87d255f21f2e5d/xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7", size = 210440, upload-time = "2025-10-02T14:35:06.239Z" },
{ url = "https://files.pythonhosted.org/packages/eb/37/b80fe3d5cfb9faff01a02121a0f4d565eb7237e9e5fc66e73017e74dcd36/xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db", size = 197990, upload-time = "2025-10-02T14:35:07.735Z" },
{ url = "https://files.pythonhosted.org/packages/d7/fd/2c0a00c97b9e18f72e1f240ad4e8f8a90fd9d408289ba9c7c495ed7dc05c/xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42", size = 210689, upload-time = "2025-10-02T14:35:09.438Z" },
{ url = "https://files.pythonhosted.org/packages/93/86/5dd8076a926b9a95db3206aba20d89a7fc14dd5aac16e5c4de4b56033140/xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11", size = 414068, upload-time = "2025-10-02T14:35:11.162Z" },
{ url = "https://files.pythonhosted.org/packages/af/3c/0bb129170ee8f3650f08e993baee550a09593462a5cddd8e44d0011102b1/xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd", size = 191495, upload-time = "2025-10-02T14:35:12.971Z" },
{ url = "https://files.pythonhosted.org/packages/e9/3a/6797e0114c21d1725e2577508e24006fd7ff1d8c0c502d3b52e45c1771d8/xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799", size = 30620, upload-time = "2025-10-02T14:35:14.129Z" },
{ url = "https://files.pythonhosted.org/packages/86/15/9bc32671e9a38b413a76d24722a2bf8784a132c043063a8f5152d390b0f9/xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392", size = 31542, upload-time = "2025-10-02T14:35:15.21Z" },
{ url = "https://files.pythonhosted.org/packages/39/c5/cc01e4f6188656e56112d6a8e0dfe298a16934b8c47a247236549a3f7695/xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6", size = 27880, upload-time = "2025-10-02T14:35:16.315Z" },
{ url = "https://files.pythonhosted.org/packages/f3/30/25e5321c8732759e930c555176d37e24ab84365482d257c3b16362235212/xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702", size = 32956, upload-time = "2025-10-02T14:35:17.413Z" },
{ url = "https://files.pythonhosted.org/packages/9f/3c/0573299560d7d9f8ab1838f1efc021a280b5ae5ae2e849034ef3dee18810/xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db", size = 31072, upload-time = "2025-10-02T14:35:18.844Z" },
{ url = "https://files.pythonhosted.org/packages/7a/1c/52d83a06e417cd9d4137722693424885cc9878249beb3a7c829e74bf7ce9/xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54", size = 196409, upload-time = "2025-10-02T14:35:20.31Z" },
{ url = "https://files.pythonhosted.org/packages/e3/8e/c6d158d12a79bbd0b878f8355432075fc82759e356ab5a111463422a239b/xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f", size = 215736, upload-time = "2025-10-02T14:35:21.616Z" },
{ url = "https://files.pythonhosted.org/packages/bc/68/c4c80614716345d55071a396cf03d06e34b5f4917a467faf43083c995155/xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5", size = 214833, upload-time = "2025-10-02T14:35:23.32Z" },
{ url = "https://files.pythonhosted.org/packages/7e/e9/ae27c8ffec8b953efa84c7c4a6c6802c263d587b9fc0d6e7cea64e08c3af/xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1", size = 448348, upload-time = "2025-10-02T14:35:25.111Z" },
{ url = "https://files.pythonhosted.org/packages/d7/6b/33e21afb1b5b3f46b74b6bd1913639066af218d704cc0941404ca717fc57/xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee", size = 196070, upload-time = "2025-10-02T14:35:26.586Z" },
{ url = "https://files.pythonhosted.org/packages/96/b6/fcabd337bc5fa624e7203aa0fa7d0c49eed22f72e93229431752bddc83d9/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd", size = 212907, upload-time = "2025-10-02T14:35:28.087Z" },
{ url = "https://files.pythonhosted.org/packages/4b/d3/9ee6160e644d660fcf176c5825e61411c7f62648728f69c79ba237250143/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729", size = 200839, upload-time = "2025-10-02T14:35:29.857Z" },
{ url = "https://files.pythonhosted.org/packages/0d/98/e8de5baa5109394baf5118f5e72ab21a86387c4f89b0e77ef3e2f6b0327b/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292", size = 213304, upload-time = "2025-10-02T14:35:31.222Z" },
{ url = "https://files.pythonhosted.org/packages/7b/1d/71056535dec5c3177eeb53e38e3d367dd1d16e024e63b1cee208d572a033/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf", size = 416930, upload-time = "2025-10-02T14:35:32.517Z" },
{ url = "https://files.pythonhosted.org/packages/dc/6c/5cbde9de2cd967c322e651c65c543700b19e7ae3e0aae8ece3469bf9683d/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033", size = 193787, upload-time = "2025-10-02T14:35:33.827Z" },
{ url = "https://files.pythonhosted.org/packages/19/fa/0172e350361d61febcea941b0cc541d6e6c8d65d153e85f850a7b256ff8a/xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec", size = 30916, upload-time = "2025-10-02T14:35:35.107Z" },
{ url = "https://files.pythonhosted.org/packages/ad/e6/e8cf858a2b19d6d45820f072eff1bea413910592ff17157cabc5f1227a16/xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8", size = 31799, upload-time = "2025-10-02T14:35:36.165Z" },
{ url = "https://files.pythonhosted.org/packages/56/15/064b197e855bfb7b343210e82490ae672f8bc7cdf3ddb02e92f64304ee8a/xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746", size = 28044, upload-time = "2025-10-02T14:35:37.195Z" },
{ url = "https://files.pythonhosted.org/packages/7e/5e/0138bc4484ea9b897864d59fce9be9086030825bc778b76cb5a33a906d37/xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e", size = 32754, upload-time = "2025-10-02T14:35:38.245Z" },
{ url = "https://files.pythonhosted.org/packages/18/d7/5dac2eb2ec75fd771957a13e5dda560efb2176d5203f39502a5fc571f899/xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405", size = 30846, upload-time = "2025-10-02T14:35:39.6Z" },
{ url = "https://files.pythonhosted.org/packages/fe/71/8bc5be2bb00deb5682e92e8da955ebe5fa982da13a69da5a40a4c8db12fb/xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3", size = 194343, upload-time = "2025-10-02T14:35:40.69Z" },
{ url = "https://files.pythonhosted.org/packages/e7/3b/52badfb2aecec2c377ddf1ae75f55db3ba2d321c5e164f14461c90837ef3/xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6", size = 213074, upload-time = "2025-10-02T14:35:42.29Z" },
{ url = "https://files.pythonhosted.org/packages/a2/2b/ae46b4e9b92e537fa30d03dbc19cdae57ed407e9c26d163895e968e3de85/xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063", size = 212388, upload-time = "2025-10-02T14:35:43.929Z" },
{ url = "https://files.pythonhosted.org/packages/f5/80/49f88d3afc724b4ac7fbd664c8452d6db51b49915be48c6982659e0e7942/xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7", size = 445614, upload-time = "2025-10-02T14:35:45.216Z" },
{ url = "https://files.pythonhosted.org/packages/ed/ba/603ce3961e339413543d8cd44f21f2c80e2a7c5cfe692a7b1f2cccf58f3c/xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b", size = 194024, upload-time = "2025-10-02T14:35:46.959Z" },
{ url = "https://files.pythonhosted.org/packages/78/d1/8e225ff7113bf81545cfdcd79eef124a7b7064a0bba53605ff39590b95c2/xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd", size = 210541, upload-time = "2025-10-02T14:35:48.301Z" },
{ url = "https://files.pythonhosted.org/packages/6f/58/0f89d149f0bad89def1a8dd38feb50ccdeb643d9797ec84707091d4cb494/xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0", size = 198305, upload-time = "2025-10-02T14:35:49.584Z" },
{ url = "https://files.pythonhosted.org/packages/11/38/5eab81580703c4df93feb5f32ff8fa7fe1e2c51c1f183ee4e48d4bb9d3d7/xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152", size = 210848, upload-time = "2025-10-02T14:35:50.877Z" },
{ url = "https://files.pythonhosted.org/packages/5e/6b/953dc4b05c3ce678abca756416e4c130d2382f877a9c30a20d08ee6a77c0/xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11", size = 414142, upload-time = "2025-10-02T14:35:52.15Z" },
{ url = "https://files.pythonhosted.org/packages/08/a9/238ec0d4e81a10eb5026d4a6972677cbc898ba6c8b9dbaec12ae001b1b35/xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5", size = 191547, upload-time = "2025-10-02T14:35:53.547Z" },
{ url = "https://files.pythonhosted.org/packages/f1/ee/3cf8589e06c2164ac77c3bf0aa127012801128f1feebf2a079272da5737c/xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f", size = 31214, upload-time = "2025-10-02T14:35:54.746Z" },
{ url = "https://files.pythonhosted.org/packages/02/5d/a19552fbc6ad4cb54ff953c3908bbc095f4a921bc569433d791f755186f1/xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad", size = 32290, upload-time = "2025-10-02T14:35:55.791Z" },
{ url = "https://files.pythonhosted.org/packages/b1/11/dafa0643bc30442c887b55baf8e73353a344ee89c1901b5a5c54a6c17d39/xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679", size = 28795, upload-time = "2025-10-02T14:35:57.162Z" },
{ url = "https://files.pythonhosted.org/packages/2c/db/0e99732ed7f64182aef4a6fb145e1a295558deec2a746265dcdec12d191e/xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4", size = 32955, upload-time = "2025-10-02T14:35:58.267Z" },
{ url = "https://files.pythonhosted.org/packages/55/f4/2a7c3c68e564a099becfa44bb3d398810cc0ff6749b0d3cb8ccb93f23c14/xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67", size = 31072, upload-time = "2025-10-02T14:35:59.382Z" },
{ url = "https://files.pythonhosted.org/packages/c6/d9/72a29cddc7250e8a5819dad5d466facb5dc4c802ce120645630149127e73/xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad", size = 196579, upload-time = "2025-10-02T14:36:00.838Z" },
{ url = "https://files.pythonhosted.org/packages/63/93/b21590e1e381040e2ca305a884d89e1c345b347404f7780f07f2cdd47ef4/xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b", size = 215854, upload-time = "2025-10-02T14:36:02.207Z" },
{ url = "https://files.pythonhosted.org/packages/ce/b8/edab8a7d4fa14e924b29be877d54155dcbd8b80be85ea00d2be3413a9ed4/xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b", size = 214965, upload-time = "2025-10-02T14:36:03.507Z" },
{ url = "https://files.pythonhosted.org/packages/27/67/dfa980ac7f0d509d54ea0d5a486d2bb4b80c3f1bb22b66e6a05d3efaf6c0/xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca", size = 448484, upload-time = "2025-10-02T14:36:04.828Z" },
{ url = "https://files.pythonhosted.org/packages/8c/63/8ffc2cc97e811c0ca5d00ab36604b3ea6f4254f20b7bc658ca825ce6c954/xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a", size = 196162, upload-time = "2025-10-02T14:36:06.182Z" },
{ url = "https://files.pythonhosted.org/packages/4b/77/07f0e7a3edd11a6097e990f6e5b815b6592459cb16dae990d967693e6ea9/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99", size = 213007, upload-time = "2025-10-02T14:36:07.733Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d8/bc5fa0d152837117eb0bef6f83f956c509332ce133c91c63ce07ee7c4873/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3", size = 200956, upload-time = "2025-10-02T14:36:09.106Z" },
{ url = "https://files.pythonhosted.org/packages/26/a5/d749334130de9411783873e9b98ecc46688dad5db64ca6e04b02acc8b473/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6", size = 213401, upload-time = "2025-10-02T14:36:10.585Z" },
{ url = "https://files.pythonhosted.org/packages/89/72/abed959c956a4bfc72b58c0384bb7940663c678127538634d896b1195c10/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93", size = 417083, upload-time = "2025-10-02T14:36:12.276Z" },
{ url = "https://files.pythonhosted.org/packages/0c/b3/62fd2b586283b7d7d665fb98e266decadf31f058f1cf6c478741f68af0cb/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518", size = 193913, upload-time = "2025-10-02T14:36:14.025Z" },
{ url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" },
{ url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" },
{ url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" },
{ url = "https://files.pythonhosted.org/packages/93/1e/8aec23647a34a249f62e2398c42955acd9b4c6ed5cf08cbea94dc46f78d2/xxhash-3.6.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f7b7e2ec26c1666ad5fc9dbfa426a6a3367ceaf79db5dd76264659d509d73b0", size = 30662, upload-time = "2025-10-02T14:37:01.743Z" },
{ url = "https://files.pythonhosted.org/packages/b8/0b/b14510b38ba91caf43006209db846a696ceea6a847a0c9ba0a5b1adc53d6/xxhash-3.6.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5dc1e14d14fa0f5789ec29a7062004b5933964bb9b02aae6622b8f530dc40296", size = 41056, upload-time = "2025-10-02T14:37:02.879Z" },
{ url = "https://files.pythonhosted.org/packages/50/55/15a7b8a56590e66ccd374bbfa3f9ffc45b810886c8c3b614e3f90bd2367c/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:881b47fc47e051b37d94d13e7455131054b56749b91b508b0907eb07900d1c13", size = 36251, upload-time = "2025-10-02T14:37:04.44Z" },
{ url = "https://files.pythonhosted.org/packages/62/b2/5ac99a041a29e58e95f907876b04f7067a0242cb85b5f39e726153981503/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6dc31591899f5e5666f04cc2e529e69b4072827085c1ef15294d91a004bc1bd", size = 32481, upload-time = "2025-10-02T14:37:05.869Z" },
{ url = "https://files.pythonhosted.org/packages/7b/d9/8d95e906764a386a3d3b596f3c68bb63687dfca806373509f51ce8eea81f/xxhash-3.6.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:15e0dac10eb9309508bfc41f7f9deaa7755c69e35af835db9cb10751adebc35d", size = 31565, upload-time = "2025-10-02T14:37:06.966Z" },
]
[[package]]
name = "zstandard"
version = "0.25.0"
+3
View File
@@ -26,6 +26,9 @@ You must pass these when invoking the graph as part of the configurable part of
`langgraph_checkpoint` also defines protocol for serialization/deserialization (serde) and provides an default implementation (`langgraph.checkpoint.serde.jsonplus.JsonPlusSerializer`) that handles a wide variety of types, including LangChain and LangGraph primitives, datetimes, enums and more.
> [!IMPORTANT]
> **Checkpoint deserialization security:** By default the serializer allows any Python type found in checkpoint data. New applications should set the environment variable `LANGGRAPH_STRICT_MSGPACK=true` or pass an explicit `allowed_msgpack_modules` list to `JsonPlusSerializer` to restrict deserialization to known-safe types.
### Pending writes
When a graph node fails mid-execution at a given superstep, LangGraph stores pending checkpoint writes from any other nodes that completed successfully at that superstep, so that whenever we resume graph execution from that superstep we don't re-run the successful nodes.
@@ -3,7 +3,7 @@ from __future__ import annotations
import copy
import logging
from collections.abc import AsyncIterator, Collection, Iterator, Mapping, Sequence
from typing import ( # noqa: UP035
from typing import (
Any,
Generic,
Literal,
@@ -13,6 +13,7 @@ from typing import ( # noqa: UP035
)
from langchain_core.runnables import RunnableConfig
from typing_extensions import NotRequired
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.serde.base import SerializerProtocol, maybe_add_typed_methods
@@ -28,6 +29,8 @@ from langgraph.checkpoint.serde.types import (
V = TypeVar("V", int, float, str)
PendingWrite = tuple[str, str, Any]
logger = logging.getLogger(__name__)
@@ -57,6 +60,30 @@ class CheckpointMetadata(TypedDict, total=False):
"""
run_id: str
"""The ID of the run that created this checkpoint."""
counters_since_delta_snapshot: dict[str, tuple[int, int]]
"""Per-channel counters since the last `_DeltaSnapshot` was written.
!!! warning "Beta"
This metadata field backs `DeltaChannel` (beta). The key name and
contents may change while the delta-channel design stabilizes.
Maps channel name -> `(updates, supersteps)`:
- index 0 (`updates`): number of supersteps that wrote to this channel
since its last snapshot blob.
- index 1 (`supersteps`): total supersteps elapsed since this channel's
last snapshot, regardless of whether the channel was written.
A snapshot fires when EITHER `updates >= ch.snapshot_frequency` OR
`supersteps >= DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT` (system-wide bound,
default 5000, env `LANGGRAPH_DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`).
The supersteps bound prevents unbounded ancestor walks on threads where
a delta channel exists but is no longer being updated.
Absent on threads that don't use delta channels. Persisted as a
2-element list in JSON (no native tuple).
"""
ChannelVersions = dict[str, str | int | float]
@@ -119,6 +146,33 @@ class CheckpointTuple(NamedTuple):
pending_writes: list[PendingWrite] | None = None
class DeltaChannelHistory(TypedDict):
"""Per-channel result entry from `BaseCheckpointSaver.get_delta_channel_history`.
!!! warning "Beta"
Part of the `DeltaChannel` support surface; in beta. Field names and
semantics may change.
Storage-level view of what one channel contributed across the ancestor
chain of a target checkpoint:
* `writes` — on-path deltas oldest→newest as `PendingWrite` tuples.
Always present; possibly empty. Already filtered to one channel.
Writes stored at the target checkpoint itself are pending for the
next super-step and are excluded.
* `seed` — the stored value at the nearest ancestor whose
`channel_values[ch]` is populated. Omitted if the walk reached the
root without finding any stored value (consumer treats absence as
"start empty"). Typically a `_DeltaSnapshot` for delta channels with
finite snapshot frequency, or a plain value for threads migrated
from a pre-delta channel type.
"""
writes: list[PendingWrite]
seed: NotRequired[Any]
class BaseCheckpointSaver(Generic[V]):
"""Base class for creating a graph checkpointer.
@@ -282,6 +336,14 @@ class BaseCheckpointSaver(Generic[V]):
Args:
run_ids: The run IDs whose checkpoints should be deleted.
!!! warning "DeltaChannel"
Deleting a run that produced ancestor `checkpoint_writes` — or
the only `_DeltaSnapshot` blob — for a still-live thread will
break reconstruction of any `DeltaChannel` whose history
depended on those rows. See the `DeltaChannel` note on `prune`
for safe-recovery strategies.
"""
raise NotImplementedError
@@ -295,6 +357,17 @@ class BaseCheckpointSaver(Generic[V]):
Args:
source_thread_id: The thread ID to copy from.
target_thread_id: The thread ID to copy to.
!!! warning "DeltaChannel"
Implementations must copy the **complete** parent chain (all
ancestor checkpoints and their `checkpoint_writes`) — copying
only the head checkpoint will leave the target thread with
`DeltaChannel` state that cannot be reconstructed (no path back
to a `_DeltaSnapshot` ancestor). Equivalently, the copy must
include enough ancestors that every `DeltaChannel`-backed key
has either a `_DeltaSnapshot` in `channel_values` somewhere in
the chain, or a complete write history back to the chain root.
"""
raise NotImplementedError
@@ -310,6 +383,34 @@ class BaseCheckpointSaver(Generic[V]):
thread_ids: The thread IDs to prune.
strategy: The pruning strategy. `"keep_latest"` retains only the most
recent checkpoint per namespace. `"delete"` removes all checkpoints.
!!! warning "DeltaChannel"
Custom implementations must be `DeltaChannel`-aware. `DeltaChannel`
stores only a sentinel in `channel_values` for non-snapshot steps;
reconstruction walks the parent chain via
`get_delta_channel_history`, accumulating rows from
`checkpoint_writes` until it reaches an ancestor whose
`channel_values` contains a `_DeltaSnapshot` blob (written every
`snapshot_frequency` updates).
A naive `"keep_latest"` that drops intermediate checkpoints and
their writes can sever that chain: the surviving "latest"
checkpoint is rarely a snapshot point itself, so its delta
channels would silently reconstruct as empty (no error raised —
`get_delta_channel_history` simply returns no `seed`). Safe
options when the graph uses `DeltaChannel`:
* Walk back from each kept checkpoint and preserve every
ancestor (plus its `checkpoint_writes`) up to the nearest one
whose `channel_values` already contains a `_DeltaSnapshot` for
every `DeltaChannel`-backed key.
* Force a fresh snapshot on the kept checkpoint before deleting
ancestors — rewrite `channel_values[k] = _DeltaSnapshot(value)`
for each delta channel `k` (resolving `value` via the existing
ancestor walk first), then prune.
* Skip pruning threads whose graph uses `DeltaChannel` until one
of the above is implemented.
"""
raise NotImplementedError
@@ -426,6 +527,13 @@ class BaseCheckpointSaver(Generic[V]):
Args:
run_ids: The run IDs whose checkpoints should be deleted.
!!! warning "DeltaChannel"
See `delete_for_runs` — deleting rows a still-live thread's
`DeltaChannel` reconstruction depends on (writes between the
head and its nearest `_DeltaSnapshot` ancestor) will silently
corrupt that channel's state.
"""
raise NotImplementedError
@@ -439,6 +547,13 @@ class BaseCheckpointSaver(Generic[V]):
Args:
source_thread_id: The thread ID to copy from.
target_thread_id: The thread ID to copy to.
!!! warning "DeltaChannel"
See `copy_thread` — the copy must carry the complete parent
chain (or at least back to a `_DeltaSnapshot` ancestor for every
`DeltaChannel`) so the target thread can reconstruct delta
state.
"""
raise NotImplementedError
@@ -454,9 +569,126 @@ class BaseCheckpointSaver(Generic[V]):
thread_ids: The thread IDs to prune.
strategy: The pruning strategy. `"keep_latest"` retains only the most
recent checkpoint per namespace. `"delete"` removes all checkpoints.
!!! warning "DeltaChannel"
See `prune` for the full `DeltaChannel` caveat. In short:
`"keep_latest"` must not drop ancestor checkpoints / writes that
sit between the kept checkpoint and the nearest `_DeltaSnapshot`
ancestor, or delta channels will silently reconstruct as empty.
"""
raise NotImplementedError
def get_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Walk the parent chain returning per-channel writes + seed.
!!! warning "Beta"
This method is part of the `DeltaChannel` support surface and is
in beta. The signature, return shape (`DeltaChannelHistory`), and
interaction with `_DeltaSnapshot` blobs may change. Override at
your own risk; the default implementation will continue to work
against the public `BaseCheckpointSaver` contract.
For each requested channel, walks ancestors of the checkpoint
identified by `config` (following `parent_config`) and accumulates
`pending_writes` for that channel. The walk terminates per-channel
at the nearest ancestor whose `channel_values[ch]` is populated;
that value is returned as `seed`. If the walk reaches the root
without finding a stored value, `seed` is omitted from that
channel's entry — the consumer treats the absence as "start
empty."
Walks the **parent chain** (not `list(before=...)`): for forked
threads, only on-path ancestors contribute.
The default implementation walks `get_tuple` + `parent_config`
once for all channels — each ancestor visited once, not once per
channel. Savers with direct storage access (`InMemorySaver`,
`PostgresSaver`) override for performance; the return contract is
fixed here.
Args:
config: Configuration identifying the target checkpoint.
channels: Channel names to walk for. Empty → empty mapping.
Returns:
Per-channel `DeltaChannelHistory` for every name in `channels`.
"""
if not channels:
return {}
collected_by_ch: dict[str, list[PendingWrite]] = {c: [] for c in channels}
seed_by_ch: dict[str, Any] = {}
remaining: set[str] = set(channels)
target_tuple = self.get_tuple(config)
cursor_config: RunnableConfig | None = (
target_tuple.parent_config if target_tuple else None
)
while cursor_config is not None and remaining:
tup = self.get_tuple(cursor_config)
if tup is None:
break
if tup.pending_writes:
for write in reversed(tup.pending_writes):
ch = write[1]
if ch in remaining:
collected_by_ch[ch].append(write)
for ch in list(remaining):
if ch in tup.checkpoint["channel_values"]:
seed_by_ch[ch] = tup.checkpoint["channel_values"][ch]
remaining.discard(ch)
cursor_config = tup.parent_config
result: dict[str, DeltaChannelHistory] = {}
for ch in channels:
entry: DeltaChannelHistory = {"writes": list(reversed(collected_by_ch[ch]))}
if ch in seed_by_ch:
entry["seed"] = seed_by_ch[ch]
result[ch] = entry
return result
async def aget_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Async version of `get_delta_channel_history`.
!!! warning "Beta"
This method is part of the `DeltaChannel` support surface and is
in beta. See `get_delta_channel_history` for caveats.
"""
if not channels:
return {}
collected_by_ch: dict[str, list[PendingWrite]] = {c: [] for c in channels}
seed_by_ch: dict[str, Any] = {}
remaining: set[str] = set(channels)
target_tuple = await self.aget_tuple(config)
cursor_config: RunnableConfig | None = (
target_tuple.parent_config if target_tuple else None
)
while cursor_config is not None and remaining:
tup = await self.aget_tuple(cursor_config)
if tup is None:
break
if tup.pending_writes:
for write in reversed(tup.pending_writes):
ch = write[1]
if ch in remaining:
collected_by_ch[ch].append(write)
for ch in list(remaining):
if ch in tup.checkpoint["channel_values"]:
seed_by_ch[ch] = tup.checkpoint["channel_values"][ch]
remaining.discard(ch)
cursor_config = tup.parent_config
result: dict[str, DeltaChannelHistory] = {}
for ch in channels:
entry: DeltaChannelHistory = {"writes": list(reversed(collected_by_ch[ch]))}
if ch in seed_by_ch:
entry["seed"] = seed_by_ch[ch]
result[ch] = entry
return result
def get_next_version(self, current: V | None, channel: None) -> V:
"""Generate the next version ID for a channel.
@@ -6,7 +6,7 @@ import pickle
import random
import shutil
from collections import defaultdict
from collections.abc import AsyncIterator, Iterator, Sequence
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
from contextlib import AbstractAsyncContextManager, AbstractContextManager, ExitStack
from types import TracebackType
from typing import Any
@@ -20,6 +20,8 @@ from langgraph.checkpoint.base import (
Checkpoint,
CheckpointMetadata,
CheckpointTuple,
DeltaChannelHistory,
PendingWrite,
SerializerProtocol,
get_checkpoint_id,
get_checkpoint_metadata,
@@ -121,16 +123,115 @@ class InMemorySaver(
return self.stack.__exit__(__exc_type, __exc_value, __traceback)
def _load_blobs(
self, thread_id: str, checkpoint_ns: str, versions: ChannelVersions
self,
thread_id: str,
checkpoint_ns: str,
versions: ChannelVersions,
) -> dict[str, Any]:
channel_values: dict[str, Any] = {}
for k, v in versions.items():
kk = (thread_id, checkpoint_ns, k, v)
if kk in self.blobs:
vv = self.blobs[kk]
if vv[0] != "empty":
channel_values[k] = self.serde.loads_typed(vv)
return channel_values
result: dict[str, Any] = {}
for k, ver in versions.items():
kk = (thread_id, checkpoint_ns, k, ver)
if kk not in self.blobs:
continue
vv = self.blobs[kk]
if vv[0] == "empty":
continue
result[k] = self.serde.loads_typed(vv)
return result
def get_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
"""Override: walk the parent chain ONCE for all requested channels.
Each channel terminates independently at the nearest ancestor
whose stored blob is non-empty. Other channels keep walking until
they find their own terminator or hit the root.
Pre-delta plain-value blobs subsume their ancestor's pending
writes (the value already includes them); `_DeltaSnapshot` blobs
do not (snapshot is the value AT that ancestor, prior to its own
pending writes that produce the child).
"""
if not channels:
return {}
# Imported lazily to avoid a hard checkpoint→serde-types coupling at
# module import; only this override needs the runtime check.
from langgraph.checkpoint.serde.types import _DeltaSnapshot
thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_id = config["configurable"].get("checkpoint_id", "")
ns_storage = self.storage.get(thread_id, {}).get(checkpoint_ns, {})
chain: list[str] = []
target_entry = ns_storage.get(checkpoint_id)
current: str | None = target_entry[2] if target_entry is not None else None
while current is not None:
entry = ns_storage.get(current)
if entry is None:
break
chain.append(current)
_, _, parent = entry
current = parent
collected_by_ch: dict[str, list[PendingWrite]] = {c: [] for c in channels}
seed_by_ch: dict[str, Any] = {}
remaining: set[str] = set(channels)
for cp_id in chain:
if not remaining:
break
entry = ns_storage.get(cp_id)
ckpt = self.serde.loads_typed(entry[0]) if entry is not None else None
terminated_here: set[str] = set()
blob_value_by_ch: dict[str, Any] = {}
if ckpt is not None:
versions = ckpt.get("channel_versions", {})
for ch in remaining:
ver = versions.get(ch)
if ver is None:
continue
blob_entry = self.blobs.get((thread_id, checkpoint_ns, ch, ver))
if blob_entry is None or blob_entry[0] == "empty":
continue
blob_value_by_ch[ch] = self.serde.loads_typed(blob_entry)
terminated_here.add(ch)
step_writes = self.writes.get((thread_id, checkpoint_ns, cp_id), {})
for (_task_id, _idx), (tid, ch, serialized, _) in sorted(
step_writes.items(), reverse=True
):
if ch not in remaining:
continue
blob_value = blob_value_by_ch.get(ch)
if blob_value is not None and not isinstance(
blob_value, _DeltaSnapshot
):
continue
collected_by_ch[ch].append(
(tid, ch, self.serde.loads_typed(serialized))
)
for ch in terminated_here:
seed_by_ch[ch] = blob_value_by_ch[ch]
remaining.discard(ch)
result: dict[str, DeltaChannelHistory] = {}
for ch in channels:
entry_h: DeltaChannelHistory = {
"writes": list(reversed(collected_by_ch[ch]))
}
if ch in seed_by_ch:
entry_h["seed"] = seed_by_ch[ch]
result[ch] = entry_h
return result
async def aget_delta_channel_history(
self, *, config: RunnableConfig, channels: Sequence[str]
) -> Mapping[str, DeltaChannelHistory]:
return self.get_delta_channel_history(config=config, channels=channels)
def get_tuple(self, config: RunnableConfig) -> CheckpointTuple | None:
"""Get a checkpoint tuple from the in-memory storage.
@@ -1,3 +1,10 @@
"""Msgpack deserialization safety controls.
Set ``LANGGRAPH_STRICT_MSGPACK=true`` to restrict checkpoint deserialization
to the types listed in ``SAFE_MSGPACK_TYPES``. Without this, any Python
callable stored in checkpoint data will be imported and executed on load.
"""
import os
from collections.abc import Iterable
from typing import cast
@@ -66,6 +73,7 @@ SAFE_MSGPACK_TYPES: frozenset[tuple[str, ...]] = frozenset(
("langchain_core.documents.base", "Document"),
# langgraph
("langgraph.types", "Send"),
("langgraph.types", "TimeoutPolicy"),
("langgraph.types", "Interrupt"),
("langgraph.types", "Command"),
("langgraph.types", "StateSnapshot"),
@@ -33,19 +33,51 @@ from langchain_core.load.load import Reviver
from langgraph.checkpoint.serde import _msgpack as _lg_msgpack
from langgraph.checkpoint.serde.base import SerializerProtocol
from langgraph.checkpoint.serde.event_hooks import emit_serde_event
from langgraph.checkpoint.serde.types import SendProtocol
from langgraph.checkpoint.serde.types import (
SendProtocol,
_DeltaSnapshot,
)
from langgraph.store.base import Item
if TYPE_CHECKING:
from langgraph.checkpoint.serde._msgpack import (
AllowedMsgpackModules,
)
from langgraph.checkpoint.serde.types import SendProtocol
LC_REVIVER = Reviver()
LC_REVIVER = Reviver(allowed_objects="core")
EMPTY_BYTES = b""
logger = logging.getLogger(__name__)
# Dedup log warnings across process lifetime; cap bounds state if types are
# dynamically generated (also acts as a circuit breaker on warning volume).
# Dedup is best-effort: racing threads may each emit once for the same key,
# and warnings are silently dropped once _MAX_WARNED_TYPES is reached.
_MAX_WARNED_TYPES = 1000
_warned_unregistered_types: set[tuple[str, str]] = set()
_warned_blocked_types: set[tuple[str, str]] = set()
def _is_safe_json_type(id_list: list[str]) -> bool:
"""Return True if an lc=2 id refers to a type in SAFE_MSGPACK_TYPES.
Safe types bypass the ``allowed_json_modules`` gate so that old "json" format
checkpoints (written before the msgpack migration) can be resumed without
requiring users to configure an explicit allowlist.
"""
if len(id_list) < 2:
return False
module_name = ".".join(id_list[:-1])
return (module_name, id_list[-1]) in _lg_msgpack.SAFE_MSGPACK_TYPES
def _warn_once(
seen: set[tuple[str, str]], key: tuple[str, str], msg: str, *args: object
) -> None:
if key in seen or len(seen) >= _MAX_WARNED_TYPES:
return
seen.add(key)
logger.warning(msg, *args)
class JsonPlusSerializer(SerializerProtocol):
"""Serializer that uses ormsgpack, with optional fallbacks.
@@ -56,6 +88,10 @@ class JsonPlusSerializer(SerializerProtocol):
class and called within the Pregel loop. It should not be used on untrusted
python objects. If an attacker can write directly to your checkpoint database,
they may be able to trigger code execution when data is deserialized.
Set the environment variable ``LANGGRAPH_STRICT_MSGPACK=true`` to restrict
deserialization to a built-in allowlist of safe types. You can also pass
an explicit ``allowed_msgpack_modules`` to the constructor.
"""
def __init__(
@@ -70,8 +106,11 @@ class JsonPlusSerializer(SerializerProtocol):
) -> None:
if allowed_msgpack_modules is _lg_msgpack._SENTINEL:
if _lg_msgpack.STRICT_MSGPACK_ENABLED:
# Strict: only SAFE_MSGPACK_TYPES are allowed.
allowed_msgpack_modules = None
else:
# Permissive (default): all types allowed with a warning.
# Set LANGGRAPH_STRICT_MSGPACK=true to lock this down.
allowed_msgpack_modules = True
self.pickle_fallback = pickle_fallback
self._allowed_json_modules: set[tuple[str, ...]] | Literal[True] | None = (
@@ -140,19 +179,23 @@ class JsonPlusSerializer(SerializerProtocol):
return out
def _reviver(self, value: dict[str, Any]) -> Any:
if self._allowed_json_modules and (
if (
value.get("lc", None) == 2
and value.get("type", None) == "constructor"
and value.get("id", None) is not None
):
try:
return self._revive_lc2(value)
except InvalidModuleError as e:
logger.warning(
"Object %s is not in the deserialization allowlist.\n%s",
value["id"],
e.message,
)
id_list = value["id"]
is_safe = _is_safe_json_type(id_list)
if self._allowed_json_modules or is_safe:
try:
return self._revive_lc2(value)
except InvalidModuleError as e:
if not is_safe:
logger.warning(
"Object %s is not in the deserialization allowlist.\n%s",
value["id"],
e.message,
)
return LC_REVIVER(value)
@@ -200,6 +243,13 @@ class JsonPlusSerializer(SerializerProtocol):
method_display = "<init>"
dotted = ".".join(needed)
# Safe types (the same set already allowed for msgpack deserialization) are
# permitted without an explicit allowlist — they are known-safe LangGraph and
# LangChain types. This restores backwards-compat for old "json" checkpoints
# that pre-date the msgpack migration without reopening the broader security gate.
if _is_safe_json_type(list(needed)):
return
if not self._allowed_json_modules:
raise InvalidModuleError(
f"Refused to deserialize JSON constructor: {dotted} (method: {method_display}). "
@@ -269,10 +319,13 @@ EXT_METHOD_SINGLE_ARG = 3
EXT_PYDANTIC_V1 = 4
EXT_PYDANTIC_V2 = 5
EXT_NUMPY_ARRAY = 6
EXT_DELTA_SNAPSHOT = 7
def _msgpack_default(obj: Any) -> str | ormsgpack.Ext:
if hasattr(obj, "model_dump") and callable(obj.model_dump): # pydantic v2
if isinstance(obj, _DeltaSnapshot):
return ormsgpack.Ext(EXT_DELTA_SNAPSHOT, _msgpack_enc(obj.value))
elif hasattr(obj, "model_dump") and callable(obj.model_dump): # pydantic v2
return ormsgpack.Ext(
EXT_PYDANTIC_V2,
_msgpack_enc(
@@ -444,10 +497,13 @@ def _msgpack_default(obj: Any) -> str | ormsgpack.Ext:
),
)
elif isinstance(obj, SendProtocol):
args: tuple[Any, ...] = (obj.node, obj.arg)
if (timeout := getattr(obj, "timeout", None)) is not None:
args = (obj.node, obj.arg, timeout)
return ormsgpack.Ext(
EXT_CONSTRUCTOR_POS_ARGS,
_msgpack_enc(
(obj.__class__.__module__, obj.__class__.__name__, (obj.node, obj.arg)),
(obj.__class__.__module__, obj.__class__.__name__, args),
),
)
elif dataclasses.is_dataclass(obj):
@@ -498,6 +554,15 @@ def _msgpack_default(obj: Any) -> str | ormsgpack.Ext:
raise TypeError(f"Object of type {obj.__class__.__name__} is not serializable")
def _send_from_args(args: Sequence[Any]) -> Any:
# ya we have a cyclic import here ¯\_(ツ)_/¯
from langgraph.types import Send # type: ignore
if len(args) == 2:
return Send(*args)
return Send(args[0], args[1], timeout=args[2])
def _create_msgpack_ext_hook(
allowed_modules: set[tuple[str, ...]] | Literal[True] | None,
) -> Callable[[int, bytes], Any]:
@@ -527,10 +592,13 @@ def _create_msgpack_ext_hook(
"name": name,
}
)
logger.warning(
_warn_once(
_warned_unregistered_types,
key,
"Deserializing unregistered type %s.%s from checkpoint. "
"This will be blocked in a future version. "
"Add to allowed_msgpack_modules to silence: [(%r, %r)]",
"Set LANGGRAPH_STRICT_MSGPACK=true to block now, or add "
"to allowed_msgpack_modules to allow explicitly: [(%r, %r)]",
module,
name,
module,
@@ -548,7 +616,9 @@ def _create_msgpack_ext_hook(
"name": name,
}
)
logger.warning(
_warn_once(
_warned_blocked_types,
key,
"Blocked deserialization of %s.%s - not in allowed_msgpack_modules. "
"Add to allowed_msgpack_modules to allow: [(%r, %r)]",
module,
@@ -581,7 +651,13 @@ def _create_msgpack_ext_hook(
return False
def ext_hook(code: int, data: bytes) -> Any:
if code == EXT_CONSTRUCTOR_SINGLE_ARG:
if code == EXT_DELTA_SNAPSHOT:
return _DeltaSnapshot(
ormsgpack.unpackb(
data, ext_hook=ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
)
)
elif code == EXT_CONSTRUCTOR_SINGLE_ARG:
try:
tup = ormsgpack.unpackb(
data, ext_hook=ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
@@ -602,6 +678,8 @@ def _create_msgpack_ext_hook(
)
if not _check_allowed(tup[0], tup[1]):
return tup[2]
if tup[0] == "langgraph.types" and tup[1] == "Send":
return _send_from_args(tup[2])
# module, name, args
return getattr(importlib.import_module(tup[0]), tup[1])(*tup[2])
except Exception:
@@ -715,9 +793,7 @@ def _msgpack_ext_hook_to_json(code: int, data: bytes) -> Any:
option=ormsgpack.OPT_NON_STR_KEYS,
)
if tup[0] == "langgraph.types" and tup[1] == "Send":
from langgraph.types import Send # type: ignore
return Send(*tup[2])
return _send_from_args(tup[2])
# module, name, args
return tup[2]
except Exception:
@@ -1,6 +1,7 @@
from collections.abc import Sequence
from typing import (
Any,
NamedTuple,
Protocol,
TypeVar,
runtime_checkable,
@@ -14,6 +15,22 @@ INTERRUPT = "__interrupt__"
RESUME = "__resume__"
TASKS = "__pregel_tasks"
class _DeltaSnapshot(NamedTuple):
"""Snapshot blob for a DeltaChannel with finite snapshot_frequency.
Stored in checkpoint_blobs via the `EXT_DELTA_SNAPSHOT` msgpack ext code.
The ancestor walk in `BaseCheckpointSaver.get_delta_channel_history` terminates
when it encounters this type (any non-empty channel_values entry stops
the walk for that channel).
`from_checkpoint` reconstructs the channel value directly from `.value`
without replaying writes — the snapshot IS the accumulated state.
"""
value: Any
Value = TypeVar("Value", covariant=True)
Update = TypeVar("Update", contravariant=True)
C = TypeVar("C")
+2 -2
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint"
version = "4.0.1"
version = "4.1.0"
description = "Library with base interfaces for LangGraph checkpoint savers."
authors = []
requires-python = ">=3.10"
@@ -18,7 +18,7 @@ dependencies = [
[project.urls]
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint"
Twitter = "https://x.com/LangChain"
Twitter = "https://x.com/langchain_oss"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
@@ -0,0 +1,33 @@
"""Run delta-channel conformance capabilities against InMemorySaver."""
from __future__ import annotations
import pytest
conformance = pytest.importorskip(
"langgraph.checkpoint.conformance",
reason="langgraph-checkpoint-conformance not installed",
)
@pytest.mark.asyncio
async def test_delta_channel_conformance():
from langgraph.checkpoint.conformance import validate
from langgraph.checkpoint.conformance.initializer import checkpointer_test
from langgraph.checkpoint.memory import InMemorySaver
@checkpointer_test(name="InMemorySaver")
async def mem_saver():
yield InMemorySaver()
report = await validate(
mem_saver,
capabilities={
"delta_channel_history",
},
)
for cap, result in report.results.items():
if result.passed is False:
details = "\n".join(result.failures or [])
pytest.fail(f"Capability {cap} failed:\n{details}")
+9
View File
@@ -29,6 +29,8 @@ from langgraph.checkpoint.serde.jsonplus import (
EXT_METHOD_SINGLE_ARG,
JsonPlusSerializer,
_msgpack_enc,
_warned_blocked_types,
_warned_unregistered_types,
)
@@ -102,6 +104,13 @@ def test_msgpack_method_pathlib_blocked_encrypted_strict(
class TestEncryptedSerializerMsgpackAllowlist:
"""Test msgpack allowlist behavior through EncryptedSerializer."""
@pytest.fixture(autouse=True)
def _reset_warned_types(self) -> None:
# Warning dedup state is process-global; reset per-test so each case
# sees a fresh slate and assertions about warning emission are stable.
_warned_unregistered_types.clear()
_warned_blocked_types.clear()
def test_safe_types_no_warning(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test safe types deserialize without warnings through encryption."""
serde = _make_encrypted_serde()
+67 -2
View File
@@ -35,6 +35,8 @@ from langgraph.checkpoint.serde.jsonplus import (
JsonPlusSerializer,
_msgpack_enc,
_msgpack_ext_hook_to_json,
_warned_blocked_types,
_warned_unregistered_types,
)
from langgraph.store.base import Item
@@ -331,6 +333,57 @@ def test_serde_jsonplus_bytes() -> None:
assert serde.loads_typed(dumped) == some_bytes
def test_lc2_json_safe_type_revives_without_allowlist() -> None:
"""Old 'json' blobs with lc=2 for safe types must revive without an explicit allowlist.
Regression test for: https://github.com/langchain-ai/langgraph/issues/7498
Threads checkpointed before v1.0.1 (pre-msgpack) stored messages as lc=2 JSON
constructor dicts. Resuming those threads must reconstruct proper BaseMessage objects
rather than returning raw dicts that cause MESSAGE_COERCION_FAILURE in add_messages.
"""
from langchain_core.messages import AIMessage
serde = JsonPlusSerializer() # default: _allowed_json_modules=None
human_blob = {
"lc": 2,
"type": "constructor",
"id": ["langchain_core", "messages", "human", "HumanMessage"],
"kwargs": {"content": "hello", "type": "human"},
}
ai_blob = {
"lc": 2,
"type": "constructor",
"id": ["langchain_core", "messages", "ai", "AIMessage"],
"kwargs": {"content": "hi there", "type": "ai"},
}
result = serde.loads_typed(("json", json.dumps([human_blob, ai_blob]).encode()))
assert len(result) == 2
assert isinstance(result[0], HumanMessage), (
f"Expected HumanMessage, got {type(result[0])}: {result[0]!r}\n"
"lc=2 JSON blobs for safe types must deserialize without an explicit allowlist"
)
assert result[0].content == "hello"
assert isinstance(result[1], AIMessage)
assert result[1].content == "hi there"
def test_lc2_json_unknown_type_stays_blocked_without_allowlist() -> None:
"""lc=2 JSON blobs for types NOT in SAFE_MSGPACK_TYPES still require an allowlist."""
serde = JsonPlusSerializer()
load = {
"lc": 2,
"type": "constructor",
"id": ["pprint", "pprint"],
"kwargs": {"object": "HELLO"},
}
# No allowlist configured → raw dict returned (not raised, not reconstructed)
result = serde.loads_typed(("json", json.dumps(load).encode()))
assert isinstance(result, dict), "Unknown lc=2 type must stay as raw dict"
assert result.get("lc") == 2
def test_deserde_invalid_module() -> None:
serde = JsonPlusSerializer()
load = {
@@ -580,6 +633,14 @@ def test_msgpack_safe_types_no_warning(caplog: pytest.LogCaptureFixture) -> None
assert result is not None
@pytest.fixture(autouse=True)
def _reset_warned_types() -> None:
# Warning dedup state is process-global; reset per-test so each case sees
# a fresh slate and assertions about warning emission are stable.
_warned_unregistered_types.clear()
_warned_blocked_types.clear()
def test_msgpack_pydantic_warns_by_default(caplog: pytest.LogCaptureFixture) -> None:
"""Pydantic models not in allowlist should log warning but still deserialize."""
current = _lg_msgpack.STRICT_MSGPACK_ENABLED
@@ -595,6 +656,12 @@ def test_msgpack_pydantic_warns_by_default(caplog: pytest.LogCaptureFixture) ->
assert "unregistered type" in caplog.text.lower()
assert "allowed_msgpack_modules" in caplog.text
assert result == obj
# Second deserialization of the same type should NOT produce another warning
caplog.clear()
result2 = serde.loads_typed(dumped)
assert "unregistered type" not in caplog.text.lower()
assert result2 == obj
_lg_msgpack.STRICT_MSGPACK_ENABLED = current
@@ -639,7 +706,6 @@ def test_msgpack_allowlist_silences_warning(caplog: pytest.LogCaptureFixture) ->
def test_msgpack_none_blocks_unregistered(caplog: pytest.LogCaptureFixture) -> None:
"""allowed_msgpack_modules=None should block unregistered types."""
serde = JsonPlusSerializer(allowed_msgpack_modules=None)
obj = MyPydantic(foo="test", bar=42, inner=InnerPydantic(hello="world"))
@@ -657,7 +723,6 @@ def test_msgpack_allowlist_blocks_non_listed(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Allowlists should block unregistered types even if msgpack is enabled."""
serde = JsonPlusSerializer(
allowed_msgpack_modules=[("tests.test_jsonplus", "MyPydantic")]
)
+373 -3
View File
@@ -12,13 +12,25 @@ from langgraph.checkpoint.base import (
empty_checkpoint,
)
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
from langgraph.checkpoint.serde.jsonplus import (
JsonPlusSerializer,
_warned_blocked_types,
_warned_unregistered_types,
)
class MemoryPydantic(BaseModel):
foo: str
@pytest.fixture(autouse=True)
def _reset_warned_types() -> None:
# Warning dedup state is process-global; reset per-test so each case sees
# a fresh slate and assertions about warning emission are stable.
_warned_unregistered_types.clear()
_warned_blocked_types.clear()
class TestMemorySaver:
@pytest.fixture(autouse=True)
def setup(self) -> None:
@@ -196,8 +208,6 @@ class TestMemorySaver:
async def test_memory_saver() -> None:
from langgraph.checkpoint.memory import InMemorySaver
memory_saver = InMemorySaver()
assert isinstance(memory_saver, InMemorySaver)
@@ -308,3 +318,363 @@ def test_memory_saver_with_allowlist_proxy_isolated() -> None:
assert direct is not None
expected = obj.model_dump() if hasattr(obj, "model_dump") else obj.dict()
assert direct.checkpoint["channel_values"]["foo"] == expected
class TestInMemorySaverDeltaChannel:
def test_load_blobs_omits_delta_channel(self) -> None:
"""_load_blobs omits delta channels (stored as 'empty'); reconstruction deferred."""
saver = InMemorySaver()
thread_id, ns, channel = "t1", "", "messages"
v1 = "00000000000000000000000000000001.0000000000000000"
saver.blobs[(thread_id, ns, channel, v1)] = ("empty", b"")
result = saver._load_blobs(thread_id, ns, {channel: v1})
assert channel not in result
def test_get_channel_writes_collects_ancestor_writes_only(self) -> None:
"""`get_delta_channel_history` collects ancestor writes oldest→newest, and
excludes writes stored at the target checkpoint itself (those are
pending writes for the next step, applied separately by pregel).
When the walk reaches the root without finding any stored value for
the channel, the per-channel entry has no `seed` key (TypedDict
absence indicates "start empty").
"""
saver = InMemorySaver()
serde = JsonPlusSerializer()
thread_id, ns, channel = "t1", "", "messages"
cp1 = empty_checkpoint()
cp1["id"] = "cp1"
cp2 = empty_checkpoint()
cp2["id"] = "cp2"
saver.storage[thread_id][ns] = {
"cp1": (serde.dumps_typed(cp1), serde.dumps_typed({}), None),
"cp2": (serde.dumps_typed(cp2), serde.dumps_typed({}), "cp1"),
}
# Writes stored at cp1 produced the cp1 snapshot; part of history.
saver.writes[(thread_id, ns, "cp1")][("task1", 0)] = (
"task1",
channel,
serde.dumps_typed({"content": "hi"}),
"",
)
# Writes stored at cp2 are pending — they will produce cp3 when the
# step that loaded cp2 completes. They MUST NOT appear in the
# reconstructed snapshot value at cp2.
saver.writes[(thread_id, ns, "cp2")][("task2", 0)] = (
"task2",
channel,
serde.dumps_typed({"content": "pending"}),
"",
)
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": ns,
"checkpoint_id": "cp2",
}
}
result = saver.get_delta_channel_history(config=config, channels=[channel])[
channel
]
# Walk reached the root without finding a stored value → no `seed` key.
assert "seed" not in result
values = [v for _, _, v in result["writes"]]
assert values == [{"content": "hi"}]
def test_get_channel_writes_at_root_returns_empty(self) -> None:
"""Reconstructing the root checkpoint's state: no ancestors → []."""
saver = InMemorySaver()
serde = JsonPlusSerializer()
thread_id, ns, channel = "t1", "", "messages"
cp1 = empty_checkpoint()
cp1["id"] = "cp1"
saver.storage[thread_id][ns] = {
"cp1": (serde.dumps_typed(cp1), serde.dumps_typed({}), None),
}
saver.writes[(thread_id, ns, "cp1")][("task1", 0)] = (
"task1",
channel,
serde.dumps_typed({"content": "pending"}),
"",
)
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": ns,
"checkpoint_id": "cp1",
}
}
result = saver.get_delta_channel_history(config=config, channels=[channel])[
channel
]
# No ancestors → no seed found, no writes accumulated.
assert "seed" not in result
assert result["writes"] == []
class TestBaseFallbackGetChannelWrites:
"""Exercises the `BaseCheckpointSaver.get_delta_channel_history` default
implementation the path third-party savers inherit when they don't
override `get_delta_channel_history` themselves.
Regression guard for a bug where the fallback passed the caller's config
(with `checkpoint_id`) straight to `self.list()`, which most savers
collapse to a single row causing the fallback to return `[]`.
"""
def _build_saver_with_chain(self) -> tuple[InMemorySaver, str, str]:
"""Build an InMemorySaver with a 3-checkpoint chain and per-step writes
for a `messages` channel.
Returns `(saver, thread_id, namespace)`. The saver subclass deletes the
InMemorySaver override so the base class fallback is exercised.
"""
class _ThirdPartyStyleSaver(InMemorySaver):
get_delta_channel_history = (
InMemorySaver.__mro__[1].get_delta_channel_history # type: ignore[attr-defined]
)
aget_delta_channel_history = (
InMemorySaver.__mro__[1].aget_delta_channel_history # type: ignore[attr-defined]
)
saver = _ThirdPartyStyleSaver()
serde = JsonPlusSerializer()
thread_id, ns, channel = "t1", "", "messages"
cp0 = empty_checkpoint()
cp0["id"] = "00000000000000000000000000000001.0000000000000000"
cp1 = empty_checkpoint()
cp1["id"] = "00000000000000000000000000000002.0000000000000000"
cp2 = empty_checkpoint()
cp2["id"] = "00000000000000000000000000000003.0000000000000000"
saver.storage[thread_id][ns] = {
cp0["id"]: (serde.dumps_typed(cp0), serde.dumps_typed({}), None),
cp1["id"]: (serde.dumps_typed(cp1), serde.dumps_typed({}), cp0["id"]),
cp2["id"]: (serde.dumps_typed(cp2), serde.dumps_typed({}), cp1["id"]),
}
# Writes under cp0 produced cp1's state; writes under cp1 produced cp2's.
saver.writes[(thread_id, ns, cp0["id"])][("task1", 0)] = (
"task1",
channel,
serde.dumps_typed({"content": "first"}),
"",
)
saver.writes[(thread_id, ns, cp1["id"])][("task2", 0)] = (
"task2",
channel,
serde.dumps_typed({"content": "second"}),
"",
)
return saver, thread_id, ns
def test_fallback_returns_ancestor_writes_oldest_first(self) -> None:
saver, thread_id, ns = self._build_saver_with_chain()
target_id = "00000000000000000000000000000003.0000000000000000"
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": ns,
"checkpoint_id": target_id,
}
}
result = saver.get_delta_channel_history(config=config, channels=["messages"])[
"messages"
]
# Walk reached root without a stored value → `seed` key absent.
assert "seed" not in result
values = [v for _, _, v in result["writes"]]
assert values == [{"content": "first"}, {"content": "second"}]
async def test_async_fallback_returns_ancestor_writes_oldest_first(self) -> None:
saver, thread_id, ns = self._build_saver_with_chain()
target_id = "00000000000000000000000000000003.0000000000000000"
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": ns,
"checkpoint_id": target_id,
}
}
result = (
await saver.aget_delta_channel_history(config=config, channels=["messages"])
)["messages"]
assert "seed" not in result
values = [v for _, _, v in result["writes"]]
assert values == [{"content": "first"}, {"content": "second"}]
async def test_async_fallback_concurrent_tasks_do_not_interfere(self) -> None:
"""Regression: the re-entrancy guard must be task-local, not thread-local.
Two concurrent `aget_delta_channel_history` calls on the same event-loop
thread must each see their full reconstructed writes. A
`threading.local()` guard would let whichever task set it first
short-circuit the other to `writes=[]`.
"""
import asyncio
saver, thread_id, ns = self._build_saver_with_chain()
# Force the two tasks to interleave across the `set(True)` boundary:
# each `aget_tuple` yields control, so if the guard were thread-local
# the second task would observe `active=True` set by the first.
orig_aget_tuple = saver.aget_tuple
async def slow_aget_tuple(config: RunnableConfig) -> Any:
await asyncio.sleep(0)
return await orig_aget_tuple(config)
saver.aget_tuple = slow_aget_tuple # type: ignore[method-assign]
target_id = "00000000000000000000000000000003.0000000000000000"
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": ns,
"checkpoint_id": target_id,
}
}
results = await asyncio.gather(
saver.aget_delta_channel_history(config=config, channels=["messages"]),
saver.aget_delta_channel_history(config=config, channels=["messages"]),
)
expected_values = [{"content": "first"}, {"content": "second"}]
for result_map in results:
result = result_map["messages"]
assert "seed" not in result
values = [v for _, _, v in result["writes"]]
assert values == expected_values
class TestPreDeltaBlobTerminator:
"""Verify the pre-delta blob terminator: when the ancestor walk hits a
checkpoint whose blob for the channel is a real value, reconstruction
seeds from it and stops. This guards
* back-compat: a thread written by pre-delta code, then extended under
delta reconstruction must return the correct value without walking
past the last pre-delta ancestor;
* perf: without the terminator, every reconstruct-after-migration would
walk all the way to the thread root.
Under the new public API a found seed populates `seed` in the
`DeltaChannelHistory` TypedDict; absence of the `seed` key means the walk
reached root without finding a stored value.
"""
def _build_mixed_thread(self) -> tuple[InMemorySaver, str, str, str, str]:
"""Three-checkpoint chain: cp1 (pre-delta, blob=[A]), cp2 (delta,
write=B), cp3 (delta, write=C). Reconstructing at cp3 must yield
seed=[A] + writes=[B, C].
Returns `(saver, thread_id, ns, channel, cp3_id)`.
"""
saver = InMemorySaver()
serde = JsonPlusSerializer()
thread_id, ns, channel = "t1", "", "messages"
v1 = "00000000000000000000000000000001.0"
v2 = "00000000000000000000000000000002.0"
v3 = "00000000000000000000000000000003.0"
# Pre-delta: cp1 stored a real blob for the channel.
saver.blobs[(thread_id, ns, channel, v1)] = serde.dumps_typed(["A"])
# Delta-era: cp2 and cp3 store "empty"; real writes in checkpoint_writes.
saver.blobs[(thread_id, ns, channel, v2)] = ("empty", b"")
saver.blobs[(thread_id, ns, channel, v3)] = ("empty", b"")
cp1 = empty_checkpoint()
cp1["id"] = "cp1"
cp1["channel_versions"][channel] = v1
cp2 = empty_checkpoint()
cp2["id"] = "cp2"
cp2["channel_versions"][channel] = v2
cp3 = empty_checkpoint()
cp3["id"] = "cp3"
cp3["channel_versions"][channel] = v3
saver.storage[thread_id][ns] = {
"cp1": (serde.dumps_typed(cp1), serde.dumps_typed({}), None),
"cp2": (serde.dumps_typed(cp2), serde.dumps_typed({}), "cp1"),
"cp3": (serde.dumps_typed(cp3), serde.dumps_typed({}), "cp2"),
}
# Write under cp1 would be from the pre-delta era and MUST be ignored
# (the blob already captures it). We add one and assert it is not
# folded into the reconstructed result.
saver.writes[(thread_id, ns, "cp1")][("task0", 0)] = (
"task0",
channel,
serde.dumps_typed("PRE-DELTA-WRITE"),
"",
)
saver.writes[(thread_id, ns, "cp2")][("task2", 0)] = (
"task2",
channel,
serde.dumps_typed("B"),
"",
)
saver.writes[(thread_id, ns, "cp3")][("task3", 0)] = (
"task3",
channel,
serde.dumps_typed("PENDING-AT-TARGET"),
"",
)
return saver, thread_id, ns, channel, "cp3"
def test_seed_from_pre_delta_ancestor_blob(self) -> None:
saver, thread_id, ns, channel, target = self._build_mixed_thread()
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": ns,
"checkpoint_id": target,
}
}
result = saver.get_delta_channel_history(config=config, channels=[channel])[
channel
]
# Seed came from the pre-delta blob at cp1.
assert result["seed"] == ["A"]
# Delta-era writes from cp2 replay through the reducer on top of seed.
# cp3 is the target — its own write is pending for the NEXT step and
# must be excluded.
values = [v for _, _, v in result["writes"]]
assert values == ["B"]
def test_pre_delta_blob_terminates_walk_before_older_writes(self) -> None:
"""Writes stored at the pre-delta ancestor itself must not be replayed
(the blob subsumes them)."""
saver, thread_id, ns, channel, target = self._build_mixed_thread()
config: RunnableConfig = {
"configurable": {
"thread_id": thread_id,
"checkpoint_ns": ns,
"checkpoint_id": target,
}
}
result = saver.get_delta_channel_history(config=config, channels=[channel])[
channel
]
values = [v for _, _, v in result["writes"]]
# The pre-delta write under cp1 must not appear (the blob subsumes it).
assert "PRE-DELTA-WRITE" not in values
# And the pending write at the target is never folded in.
assert "PENDING-AT-TARGET" not in values
+213 -79
View File
@@ -2,7 +2,8 @@ version = 1
revision = 3
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.12'",
"python_full_version >= '3.15'",
"python_full_version >= '3.12' and python_full_version < '3.15'",
"python_full_version == '3.11.*'",
"python_full_version < '3.11'",
]
@@ -267,10 +268,11 @@ wheels = [
[[package]]
name = "langchain-core"
version = "1.2.23"
version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsonpatch" },
{ name = "langchain-protocol" },
{ name = "langsmith" },
{ name = "packaging" },
{ name = "pydantic" },
@@ -279,14 +281,26 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "uuid-utils" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1d/47/a5f21b651e9cbd7a26c3e5809336d10a0be94ef7bdf6bea47f2ad9fff1a8/langchain_core-1.2.23.tar.gz", hash = "sha256:fdec64f90cfea25317e88d9803c44684af1f4e30dec4e58320dd7393bb0f0785", size = 841684, upload-time = "2026-03-27T23:28:14.6Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/ae/8b74458fc3850ec3d150eb9f45e857db129dafa801fb5cf173dfc9f8bbf3/langchain_core-1.3.3.tar.gz", hash = "sha256:fa510a5db8efdc0c6ff41c0939fb5c00a0183c11f6b84233e892e3227ff69182", size = 915041, upload-time = "2026-05-05T19:02:36.612Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/5a/6ff2d76618e4cac531ea51d4ef44c6add36575a84c3f0f8877aee68c951a/langchain_core-1.2.23-py3-none-any.whl", hash = "sha256:70866dfc5275b7840ce272ff70f0ff216c8666ab25dc1b41964a4ef58c02a3ff", size = 506709, upload-time = "2026-03-27T23:28:13.372Z" },
{ url = "https://files.pythonhosted.org/packages/1f/01/4771b7ab2af1d1aba5b710bd8f13d9225c609425214b357590a17b01be77/langchain_core-1.3.3-py3-none-any.whl", hash = "sha256:18aae8506f37da7f74398492279a7d6efcee4f8e23c4c41c7af080eeb7ef7bd1", size = 543857, upload-time = "2026-05-05T19:02:34.52Z" },
]
[[package]]
name = "langchain-protocol"
version = "0.0.15"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4f/24/9777489d6fbbee64af0c8f96d4f840239c408cf694f3394672807dafc490/langchain_protocol-0.0.15.tar.gz", hash = "sha256:9ab2d11ee73944754f10e037e717098d3a6796f0e58afa9cadda6154e7655ade", size = 5862, upload-time = "2026-05-01T22:30:04.748Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/7a/9c97a7b9cbe4c5dc6a44cdb1545450c28f0c8ce89b9c1f0ee7fbad896263/langchain_protocol-0.0.15-py3-none-any.whl", hash = "sha256:461eb794358f83d5e42635a5797799ffec7b4702314e34edf73ac21e75d3ef79", size = 6982, upload-time = "2026-05-01T22:30:03.877Z" },
]
[[package]]
name = "langgraph-checkpoint"
version = "4.0.1"
version = "4.1.0"
source = { editable = "." }
dependencies = [
{ name = "langchain-core" },
@@ -369,7 +383,7 @@ test = [
[[package]]
name = "langsmith"
version = "0.6.4"
version = "0.7.31"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -379,11 +393,12 @@ dependencies = [
{ name = "requests" },
{ name = "requests-toolbelt" },
{ name = "uuid-utils" },
{ name = "xxhash" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e7/85/9c7933052a997da1b85bc5c774f3865e9b1da1c8d71541ea133178b13229/langsmith-0.6.4.tar.gz", hash = "sha256:36f7223a01c218079fbb17da5e536ebbaf5c1468c028abe070aa3ae59bc99ec8", size = 919964, upload-time = "2026-01-15T20:02:28.873Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e6/11/696019490992db5c87774dc20515529ef42a01e1d770fb754ed6d9b12fb0/langsmith-0.7.31.tar.gz", hash = "sha256:331ee4f7c26bb5be4022b9859b7d7b122cbf8c9d01d9f530114c1914b0349ffb", size = 1178480, upload-time = "2026-04-14T17:55:41.242Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/66/0f/09a6637a7ba777eb307b7c80852d9ee26438e2bdafbad6fcc849ff9d9192/langsmith-0.6.4-py3-none-any.whl", hash = "sha256:ac4835860160be371042c7adbba3cb267bcf8d96a5ea976c33a8a4acad6c5486", size = 283503, upload-time = "2026-01-15T20:02:26.662Z" },
{ url = "https://files.pythonhosted.org/packages/1d/a1/a013cf458c301cda86a213dd153ce0a01c93f1ab5833f951e6a44c9763ce/langsmith-0.7.31-py3-none-any.whl", hash = "sha256:0291d49203f6e80dda011af1afda61eb0595a4d697adb684590a8805e1d61fb6", size = 373276, upload-time = "2026-04-14T17:55:39.677Z" },
]
[[package]]
@@ -485,7 +500,7 @@ wheels = [
[[package]]
name = "mypy"
version = "1.20.0"
version = "1.20.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
@@ -494,51 +509,51 @@ dependencies = [
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/b0089fe7fef0a994ae5ee07029ced0526082c6cfaaa4c10d40a10e33b097/mypy-1.20.0.tar.gz", hash = "sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3", size = 3815028, upload-time = "2026-03-31T16:55:14.959Z" }
sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/a2/a965c8c3fcd4fa8b84ba0d46606181b0d0a1d50f274c67877f3e9ed4882c/mypy-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8", size = 14430138, upload-time = "2026-03-31T16:52:37.843Z" },
{ url = "https://files.pythonhosted.org/packages/53/6e/043477501deeb8eabbab7f1a2f6cac62cfb631806dc1d6862a04a7f5011b/mypy-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a", size = 13311282, upload-time = "2026-03-31T16:55:11.021Z" },
{ url = "https://files.pythonhosted.org/packages/65/aa/bd89b247b83128197a214f29f0632ff3c14f54d4cd70d144d157bd7d7d6e/mypy-1.20.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865", size = 13750889, upload-time = "2026-03-31T16:52:02.909Z" },
{ url = "https://files.pythonhosted.org/packages/fa/9d/2860be7355c45247ccc0be1501c91176318964c2a137bd4743f58ce6200e/mypy-1.20.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca", size = 14619788, upload-time = "2026-03-31T16:50:48.928Z" },
{ url = "https://files.pythonhosted.org/packages/75/7f/3ef3e360c91f3de120f205c8ce405e9caf9fc52ef14b65d37073e322c114/mypy-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018", size = 14918849, upload-time = "2026-03-31T16:51:10.478Z" },
{ url = "https://files.pythonhosted.org/packages/ae/72/af970dfe167ef788df7c5e6109d2ed0229f164432ce828bc9741a4250e64/mypy-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13", size = 10822007, upload-time = "2026-03-31T16:50:25.268Z" },
{ url = "https://files.pythonhosted.org/packages/93/94/ba9065c2ebe5421619aff684b793d953e438a8bfe31a320dd6d1e0706e81/mypy-1.20.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281", size = 9756158, upload-time = "2026-03-31T16:48:36.213Z" },
{ url = "https://files.pythonhosted.org/packages/6e/1c/74cb1d9993236910286865679d1c616b136b2eae468493aa939431eda410/mypy-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b", size = 14343972, upload-time = "2026-03-31T16:49:04.887Z" },
{ url = "https://files.pythonhosted.org/packages/d5/0d/01399515eca280386e308cf57901e68d3a52af18691941b773b3380c1df8/mypy-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367", size = 13225007, upload-time = "2026-03-31T16:50:08.151Z" },
{ url = "https://files.pythonhosted.org/packages/56/ac/b4ba5094fb2d7fe9d2037cd8d18bbe02bcf68fd22ab9ff013f55e57ba095/mypy-1.20.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62", size = 13663752, upload-time = "2026-03-31T16:49:26.064Z" },
{ url = "https://files.pythonhosted.org/packages/db/a7/460678d3cf7da252d2288dad0c602294b6ec22a91932ec368cc11e44bb6e/mypy-1.20.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0", size = 14532265, upload-time = "2026-03-31T16:53:55.077Z" },
{ url = "https://files.pythonhosted.org/packages/a3/3e/051cca8166cf0438ae3ea80e0e7c030d7a8ab98dffc93f80a1aa3f23c1a2/mypy-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f", size = 14768476, upload-time = "2026-03-31T16:50:34.587Z" },
{ url = "https://files.pythonhosted.org/packages/be/66/8e02ec184f852ed5c4abb805583305db475930854e09964b55e107cdcbc4/mypy-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e", size = 10818226, upload-time = "2026-03-31T16:53:15.624Z" },
{ url = "https://files.pythonhosted.org/packages/13/4b/383ad1924b28f41e4879a74151e7a5451123330d45652da359f9183bcd45/mypy-1.20.0-cp311-cp311-win_arm64.whl", hash = "sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442", size = 9750091, upload-time = "2026-03-31T16:54:12.162Z" },
{ url = "https://files.pythonhosted.org/packages/be/dd/3afa29b58c2e57c79116ed55d700721c3c3b15955e2b6251dd165d377c0e/mypy-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214", size = 14509525, upload-time = "2026-03-31T16:55:01.824Z" },
{ url = "https://files.pythonhosted.org/packages/54/eb/227b516ab8cad9f2a13c5e7a98d28cd6aa75e9c83e82776ae6c1c4c046c7/mypy-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e", size = 13326469, upload-time = "2026-03-31T16:51:41.23Z" },
{ url = "https://files.pythonhosted.org/packages/57/d4/1ddb799860c1b5ac6117ec307b965f65deeb47044395ff01ab793248a591/mypy-1.20.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651", size = 13705953, upload-time = "2026-03-31T16:48:55.69Z" },
{ url = "https://files.pythonhosted.org/packages/c5/b7/54a720f565a87b893182a2a393370289ae7149e4715859e10e1c05e49154/mypy-1.20.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5", size = 14710363, upload-time = "2026-03-31T16:53:26.948Z" },
{ url = "https://files.pythonhosted.org/packages/b2/2a/74810274848d061f8a8ea4ac23aaad43bd3d8c1882457999c2e568341c57/mypy-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78", size = 14947005, upload-time = "2026-03-31T16:50:17.591Z" },
{ url = "https://files.pythonhosted.org/packages/77/91/21b8ba75f958bcda75690951ce6fa6b7138b03471618959529d74b8544e2/mypy-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489", size = 10880616, upload-time = "2026-03-31T16:52:19.986Z" },
{ url = "https://files.pythonhosted.org/packages/8a/15/3d8198ef97c1ca03aea010cce4f1d4f3bc5d9849e8c0140111ca2ead9fdd/mypy-1.20.0-cp312-cp312-win_arm64.whl", hash = "sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33", size = 9813091, upload-time = "2026-03-31T16:53:44.385Z" },
{ url = "https://files.pythonhosted.org/packages/d6/a7/f64ea7bd592fa431cb597418b6dec4a47f7d0c36325fec7ac67bc8402b94/mypy-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134", size = 14485344, upload-time = "2026-03-31T16:49:16.78Z" },
{ url = "https://files.pythonhosted.org/packages/bb/72/8927d84cfc90c6abea6e96663576e2e417589347eb538749a464c4c218a0/mypy-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c", size = 13327400, upload-time = "2026-03-31T16:53:08.02Z" },
{ url = "https://files.pythonhosted.org/packages/ab/4a/11ab99f9afa41aa350178d24a7d2da17043228ea10f6456523f64b5a6cf6/mypy-1.20.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe", size = 13706384, upload-time = "2026-03-31T16:52:28.577Z" },
{ url = "https://files.pythonhosted.org/packages/42/79/694ca73979cfb3535ebfe78733844cd5aff2e63304f59bf90585110d975a/mypy-1.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f", size = 14700378, upload-time = "2026-03-31T16:48:45.527Z" },
{ url = "https://files.pythonhosted.org/packages/84/24/a022ccab3a46e3d2cdf2e0e260648633640eb396c7e75d5a42818a8d3971/mypy-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726", size = 14932170, upload-time = "2026-03-31T16:49:36.038Z" },
{ url = "https://files.pythonhosted.org/packages/d8/9b/549228d88f574d04117e736f55958bd4908f980f9f5700a07aeb85df005b/mypy-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69", size = 10888526, upload-time = "2026-03-31T16:50:59.827Z" },
{ url = "https://files.pythonhosted.org/packages/91/17/15095c0e54a8bc04d22d4ff06b2139d5f142c2e87520b4e39010c4862771/mypy-1.20.0-cp313-cp313-win_arm64.whl", hash = "sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e", size = 9816456, upload-time = "2026-03-31T16:49:59.537Z" },
{ url = "https://files.pythonhosted.org/packages/4e/0e/6ca4a84cbed9e62384bc0b2974c90395ece5ed672393e553996501625fc5/mypy-1.20.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948", size = 14483331, upload-time = "2026-03-31T16:52:57.999Z" },
{ url = "https://files.pythonhosted.org/packages/7d/c5/5fe9d8a729dd9605064691816243ae6c49fde0bd28f6e5e17f6a24203c43/mypy-1.20.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5", size = 13342047, upload-time = "2026-03-31T16:54:21.555Z" },
{ url = "https://files.pythonhosted.org/packages/4c/33/e18bcfa338ca4e6b2771c85d4c5203e627d0c69d9de5c1a2cf2ba13320ba/mypy-1.20.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188", size = 13719585, upload-time = "2026-03-31T16:51:53.89Z" },
{ url = "https://files.pythonhosted.org/packages/6b/8d/93491ff7b79419edc7eabf95cb3b3f7490e2e574b2855c7c7e7394ff933f/mypy-1.20.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83", size = 14685075, upload-time = "2026-03-31T16:54:04.464Z" },
{ url = "https://files.pythonhosted.org/packages/b5/9d/d924b38a4923f8d164bf2b4ec98bf13beaf6e10a5348b4b137eadae40a6e/mypy-1.20.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2", size = 14919141, upload-time = "2026-03-31T16:54:51.785Z" },
{ url = "https://files.pythonhosted.org/packages/59/98/1da9977016678c0b99d43afe52ed00bb3c1a0c4c995d3e6acca1a6ebb9b4/mypy-1.20.0-cp314-cp314-win_amd64.whl", hash = "sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732", size = 11050925, upload-time = "2026-03-31T16:51:30.758Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e3/ba0b7a3143e49a9c4f5967dde6ea4bf8e0b10ecbbcca69af84027160ee89/mypy-1.20.0-cp314-cp314-win_arm64.whl", hash = "sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef", size = 10001089, upload-time = "2026-03-31T16:49:43.632Z" },
{ url = "https://files.pythonhosted.org/packages/12/28/e617e67b3be9d213cda7277913269c874eb26472489f95d09d89765ce2d8/mypy-1.20.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1", size = 15534710, upload-time = "2026-03-31T16:52:12.506Z" },
{ url = "https://files.pythonhosted.org/packages/6e/0c/3b5f2d3e45dc7169b811adce8451679d9430399d03b168f9b0489f43adaa/mypy-1.20.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436", size = 14393013, upload-time = "2026-03-31T16:54:41.186Z" },
{ url = "https://files.pythonhosted.org/packages/a3/49/edc8b0aa145cc09c1c74f7ce2858eead9329931dcbbb26e2ad40906daa4e/mypy-1.20.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6", size = 15047240, upload-time = "2026-03-31T16:54:31.955Z" },
{ url = "https://files.pythonhosted.org/packages/42/37/a946bb416e37a57fa752b3100fd5ede0e28df94f92366d1716555d47c454/mypy-1.20.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526", size = 15858565, upload-time = "2026-03-31T16:53:36.997Z" },
{ url = "https://files.pythonhosted.org/packages/2f/99/7690b5b5b552db1bd4ff362e4c0eb3107b98d680835e65823fbe888c8b78/mypy-1.20.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787", size = 16087874, upload-time = "2026-03-31T16:52:48.313Z" },
{ url = "https://files.pythonhosted.org/packages/aa/76/53e893a498138066acd28192b77495c9357e5a58cc4be753182846b43315/mypy-1.20.0-cp314-cp314t-win_amd64.whl", hash = "sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb", size = 12572380, upload-time = "2026-03-31T16:49:52.454Z" },
{ url = "https://files.pythonhosted.org/packages/76/9c/6dbdae21f01b7aacddc2c0bbf3c5557aa547827fdf271770fe1e521e7093/mypy-1.20.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd", size = 10381174, upload-time = "2026-03-31T16:51:20.179Z" },
{ url = "https://files.pythonhosted.org/packages/21/66/4d734961ce167f0fd8380769b3b7c06dbdd6ff54c2190f3f2ecd22528158/mypy-1.20.0-py3-none-any.whl", hash = "sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e", size = 2636365, upload-time = "2026-03-31T16:51:44.911Z" },
{ url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" },
{ url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" },
{ url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" },
{ url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" },
{ url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" },
{ url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" },
{ url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" },
{ url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" },
{ url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" },
{ url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" },
{ url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" },
{ url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" },
{ url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" },
{ url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" },
{ url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" },
{ url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" },
{ url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" },
{ url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" },
{ url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" },
{ url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" },
{ url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" },
{ url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" },
{ url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" },
{ url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" },
{ url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" },
{ url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" },
{ url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" },
{ url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" },
{ url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" },
{ url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" },
{ url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" },
{ url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" },
{ url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" },
{ url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" },
{ url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" },
{ url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" },
{ url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" },
{ url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" },
{ url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" },
{ url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" },
{ url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" },
{ url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" },
]
[[package]]
@@ -620,7 +635,8 @@ name = "numpy"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.12'",
"python_full_version >= '3.15'",
"python_full_version >= '3.12' and python_full_version < '3.15'",
"python_full_version == '3.11.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" }
@@ -1117,7 +1133,7 @@ wheels = [
[[package]]
name = "pytest"
version = "9.0.2"
version = "9.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -1128,9 +1144,9 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
]
[[package]]
@@ -1298,27 +1314,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.15.8"
version = "0.15.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" }
sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" },
{ url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" },
{ url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" },
{ url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" },
{ url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" },
{ url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" },
{ url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" },
{ url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" },
{ url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" },
{ url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" },
{ url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" },
{ url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" },
{ url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" },
{ url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" },
{ url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" },
{ url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" },
{ url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" },
{ url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" },
{ url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" },
{ url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" },
{ url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" },
{ url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" },
{ url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" },
{ url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" },
{ url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" },
{ url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" },
{ url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" },
{ url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" },
{ url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" },
{ url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" },
{ url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" },
{ url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" },
{ url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" },
{ url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" },
]
[[package]]
@@ -1447,11 +1463,11 @@ wheels = [
[[package]]
name = "urllib3"
version = "2.6.3"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
]
[[package]]
@@ -1515,6 +1531,124 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
]
[[package]]
name = "xxhash"
version = "3.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" },
{ url = "https://files.pythonhosted.org/packages/a3/b1/93508d9460b292c74a09b83d16750c52a0ead89c51eea9951cb97a60d959/xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d", size = 30807, upload-time = "2025-10-02T14:33:52.964Z" },
{ url = "https://files.pythonhosted.org/packages/07/55/28c93a3662f2d200c70704efe74aab9640e824f8ce330d8d3943bf7c9b3c/xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8", size = 193786, upload-time = "2025-10-02T14:33:54.272Z" },
{ url = "https://files.pythonhosted.org/packages/c1/96/fec0be9bb4b8f5d9c57d76380a366f31a1781fb802f76fc7cda6c84893c7/xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058", size = 212830, upload-time = "2025-10-02T14:33:55.706Z" },
{ url = "https://files.pythonhosted.org/packages/c4/a0/c706845ba77b9611f81fd2e93fad9859346b026e8445e76f8c6fd057cc6d/xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2", size = 211606, upload-time = "2025-10-02T14:33:57.133Z" },
{ url = "https://files.pythonhosted.org/packages/67/1e/164126a2999e5045f04a69257eea946c0dc3e86541b400d4385d646b53d7/xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc", size = 444872, upload-time = "2025-10-02T14:33:58.446Z" },
{ url = "https://files.pythonhosted.org/packages/2d/4b/55ab404c56cd70a2cf5ecfe484838865d0fea5627365c6c8ca156bd09c8f/xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc", size = 193217, upload-time = "2025-10-02T14:33:59.724Z" },
{ url = "https://files.pythonhosted.org/packages/45/e6/52abf06bac316db33aa269091ae7311bd53cfc6f4b120ae77bac1b348091/xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07", size = 210139, upload-time = "2025-10-02T14:34:02.041Z" },
{ url = "https://files.pythonhosted.org/packages/34/37/db94d490b8691236d356bc249c08819cbcef9273a1a30acf1254ff9ce157/xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4", size = 197669, upload-time = "2025-10-02T14:34:03.664Z" },
{ url = "https://files.pythonhosted.org/packages/b7/36/c4f219ef4a17a4f7a64ed3569bc2b5a9c8311abdb22249ac96093625b1a4/xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06", size = 210018, upload-time = "2025-10-02T14:34:05.325Z" },
{ url = "https://files.pythonhosted.org/packages/fd/06/bfac889a374fc2fc439a69223d1750eed2e18a7db8514737ab630534fa08/xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4", size = 413058, upload-time = "2025-10-02T14:34:06.925Z" },
{ url = "https://files.pythonhosted.org/packages/c9/d1/555d8447e0dd32ad0930a249a522bb2e289f0d08b6b16204cfa42c1f5a0c/xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b", size = 190628, upload-time = "2025-10-02T14:34:08.669Z" },
{ url = "https://files.pythonhosted.org/packages/d1/15/8751330b5186cedc4ed4b597989882ea05e0408b53fa47bcb46a6125bfc6/xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b", size = 30577, upload-time = "2025-10-02T14:34:10.234Z" },
{ url = "https://files.pythonhosted.org/packages/bb/cc/53f87e8b5871a6eb2ff7e89c48c66093bda2be52315a8161ddc54ea550c4/xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb", size = 31487, upload-time = "2025-10-02T14:34:11.618Z" },
{ url = "https://files.pythonhosted.org/packages/9f/00/60f9ea3bb697667a14314d7269956f58bf56bb73864f8f8d52a3c2535e9a/xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d", size = 27863, upload-time = "2025-10-02T14:34:12.619Z" },
{ url = "https://files.pythonhosted.org/packages/17/d4/cc2f0400e9154df4b9964249da78ebd72f318e35ccc425e9f403c392f22a/xxhash-3.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b47bbd8cf2d72797f3c2772eaaac0ded3d3af26481a26d7d7d41dc2d3c46b04a", size = 32844, upload-time = "2025-10-02T14:34:14.037Z" },
{ url = "https://files.pythonhosted.org/packages/5e/ec/1cc11cd13e26ea8bc3cb4af4eaadd8d46d5014aebb67be3f71fb0b68802a/xxhash-3.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2b6821e94346f96db75abaa6e255706fb06ebd530899ed76d32cd99f20dc52fa", size = 30809, upload-time = "2025-10-02T14:34:15.484Z" },
{ url = "https://files.pythonhosted.org/packages/04/5f/19fe357ea348d98ca22f456f75a30ac0916b51c753e1f8b2e0e6fb884cce/xxhash-3.6.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d0a9751f71a1a65ce3584e9cae4467651c7e70c9d31017fa57574583a4540248", size = 194665, upload-time = "2025-10-02T14:34:16.541Z" },
{ url = "https://files.pythonhosted.org/packages/90/3b/d1f1a8f5442a5fd8beedae110c5af7604dc37349a8e16519c13c19a9a2de/xxhash-3.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b29ee68625ab37b04c0b40c3fafdf24d2f75ccd778333cfb698f65f6c463f62", size = 213550, upload-time = "2025-10-02T14:34:17.878Z" },
{ url = "https://files.pythonhosted.org/packages/c4/ef/3a9b05eb527457d5db13a135a2ae1a26c80fecd624d20f3e8dcc4cb170f3/xxhash-3.6.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6812c25fe0d6c36a46ccb002f40f27ac903bf18af9f6dd8f9669cb4d176ab18f", size = 212384, upload-time = "2025-10-02T14:34:19.182Z" },
{ url = "https://files.pythonhosted.org/packages/0f/18/ccc194ee698c6c623acbf0f8c2969811a8a4b6185af5e824cd27b9e4fd3e/xxhash-3.6.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4ccbff013972390b51a18ef1255ef5ac125c92dc9143b2d1909f59abc765540e", size = 445749, upload-time = "2025-10-02T14:34:20.659Z" },
{ url = "https://files.pythonhosted.org/packages/a5/86/cf2c0321dc3940a7aa73076f4fd677a0fb3e405cb297ead7d864fd90847e/xxhash-3.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:297b7fbf86c82c550e12e8fb71968b3f033d27b874276ba3624ea868c11165a8", size = 193880, upload-time = "2025-10-02T14:34:22.431Z" },
{ url = "https://files.pythonhosted.org/packages/82/fb/96213c8560e6f948a1ecc9a7613f8032b19ee45f747f4fca4eb31bb6d6ed/xxhash-3.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dea26ae1eb293db089798d3973a5fc928a18fdd97cc8801226fae705b02b14b0", size = 210912, upload-time = "2025-10-02T14:34:23.937Z" },
{ url = "https://files.pythonhosted.org/packages/40/aa/4395e669b0606a096d6788f40dbdf2b819d6773aa290c19e6e83cbfc312f/xxhash-3.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7a0b169aafb98f4284f73635a8e93f0735f9cbde17bd5ec332480484241aaa77", size = 198654, upload-time = "2025-10-02T14:34:25.644Z" },
{ url = "https://files.pythonhosted.org/packages/67/74/b044fcd6b3d89e9b1b665924d85d3f400636c23590226feb1eb09e1176ce/xxhash-3.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:08d45aef063a4531b785cd72de4887766d01dc8f362a515693df349fdb825e0c", size = 210867, upload-time = "2025-10-02T14:34:27.203Z" },
{ url = "https://files.pythonhosted.org/packages/bc/fd/3ce73bf753b08cb19daee1eb14aa0d7fe331f8da9c02dd95316ddfe5275e/xxhash-3.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:929142361a48ee07f09121fe9e96a84950e8d4df3bb298ca5d88061969f34d7b", size = 414012, upload-time = "2025-10-02T14:34:28.409Z" },
{ url = "https://files.pythonhosted.org/packages/ba/b3/5a4241309217c5c876f156b10778f3ab3af7ba7e3259e6d5f5c7d0129eb2/xxhash-3.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51312c768403d8540487dbbfb557454cfc55589bbde6424456951f7fcd4facb3", size = 191409, upload-time = "2025-10-02T14:34:29.696Z" },
{ url = "https://files.pythonhosted.org/packages/c0/01/99bfbc15fb9abb9a72b088c1d95219fc4782b7d01fc835bd5744d66dd0b8/xxhash-3.6.0-cp311-cp311-win32.whl", hash = "sha256:d1927a69feddc24c987b337ce81ac15c4720955b667fe9b588e02254b80446fd", size = 30574, upload-time = "2025-10-02T14:34:31.028Z" },
{ url = "https://files.pythonhosted.org/packages/65/79/9d24d7f53819fe301b231044ea362ce64e86c74f6e8c8e51320de248b3e5/xxhash-3.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:26734cdc2d4ffe449b41d186bbeac416f704a482ed835d375a5c0cb02bc63fef", size = 31481, upload-time = "2025-10-02T14:34:32.062Z" },
{ url = "https://files.pythonhosted.org/packages/30/4e/15cd0e3e8772071344eab2961ce83f6e485111fed8beb491a3f1ce100270/xxhash-3.6.0-cp311-cp311-win_arm64.whl", hash = "sha256:d72f67ef8bf36e05f5b6c65e8524f265bd61071471cd4cf1d36743ebeeeb06b7", size = 27861, upload-time = "2025-10-02T14:34:33.555Z" },
{ url = "https://files.pythonhosted.org/packages/9a/07/d9412f3d7d462347e4511181dea65e47e0d0e16e26fbee2ea86a2aefb657/xxhash-3.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01362c4331775398e7bb34e3ab403bc9ee9f7c497bc7dee6272114055277dd3c", size = 32744, upload-time = "2025-10-02T14:34:34.622Z" },
{ url = "https://files.pythonhosted.org/packages/79/35/0429ee11d035fc33abe32dca1b2b69e8c18d236547b9a9b72c1929189b9a/xxhash-3.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b2df81a23f8cb99656378e72501b2cb41b1827c0f5a86f87d6b06b69f9f204", size = 30816, upload-time = "2025-10-02T14:34:36.043Z" },
{ url = "https://files.pythonhosted.org/packages/b7/f2/57eb99aa0f7d98624c0932c5b9a170e1806406cdbcdb510546634a1359e0/xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490", size = 194035, upload-time = "2025-10-02T14:34:37.354Z" },
{ url = "https://files.pythonhosted.org/packages/4c/ed/6224ba353690d73af7a3f1c7cdb1fc1b002e38f783cb991ae338e1eb3d79/xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2", size = 212914, upload-time = "2025-10-02T14:34:38.6Z" },
{ url = "https://files.pythonhosted.org/packages/38/86/fb6b6130d8dd6b8942cc17ab4d90e223653a89aa32ad2776f8af7064ed13/xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa", size = 212163, upload-time = "2025-10-02T14:34:39.872Z" },
{ url = "https://files.pythonhosted.org/packages/ee/dc/e84875682b0593e884ad73b2d40767b5790d417bde603cceb6878901d647/xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0", size = 445411, upload-time = "2025-10-02T14:34:41.569Z" },
{ url = "https://files.pythonhosted.org/packages/11/4f/426f91b96701ec2f37bb2b8cec664eff4f658a11f3fa9d94f0a887ea6d2b/xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2", size = 193883, upload-time = "2025-10-02T14:34:43.249Z" },
{ url = "https://files.pythonhosted.org/packages/53/5a/ddbb83eee8e28b778eacfc5a85c969673e4023cdeedcfcef61f36731610b/xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9", size = 210392, upload-time = "2025-10-02T14:34:45.042Z" },
{ url = "https://files.pythonhosted.org/packages/1e/c2/ff69efd07c8c074ccdf0a4f36fcdd3d27363665bcdf4ba399abebe643465/xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e", size = 197898, upload-time = "2025-10-02T14:34:46.302Z" },
{ url = "https://files.pythonhosted.org/packages/58/ca/faa05ac19b3b622c7c9317ac3e23954187516298a091eb02c976d0d3dd45/xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374", size = 210655, upload-time = "2025-10-02T14:34:47.571Z" },
{ url = "https://files.pythonhosted.org/packages/d4/7a/06aa7482345480cc0cb597f5c875b11a82c3953f534394f620b0be2f700c/xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d", size = 414001, upload-time = "2025-10-02T14:34:49.273Z" },
{ url = "https://files.pythonhosted.org/packages/23/07/63ffb386cd47029aa2916b3d2f454e6cc5b9f5c5ada3790377d5430084e7/xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae", size = 191431, upload-time = "2025-10-02T14:34:50.798Z" },
{ url = "https://files.pythonhosted.org/packages/0f/93/14fde614cadb4ddf5e7cebf8918b7e8fac5ae7861c1875964f17e678205c/xxhash-3.6.0-cp312-cp312-win32.whl", hash = "sha256:50fc255f39428a27299c20e280d6193d8b63b8ef8028995323bf834a026b4fbb", size = 30617, upload-time = "2025-10-02T14:34:51.954Z" },
{ url = "https://files.pythonhosted.org/packages/13/5d/0d125536cbe7565a83d06e43783389ecae0c0f2ed037b48ede185de477c0/xxhash-3.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0f2ab8c715630565ab8991b536ecded9416d615538be8ecddce43ccf26cbc7c", size = 31534, upload-time = "2025-10-02T14:34:53.276Z" },
{ url = "https://files.pythonhosted.org/packages/54/85/6ec269b0952ec7e36ba019125982cf11d91256a778c7c3f98a4c5043d283/xxhash-3.6.0-cp312-cp312-win_arm64.whl", hash = "sha256:eae5c13f3bc455a3bbb68bdc513912dc7356de7e2280363ea235f71f54064829", size = 27876, upload-time = "2025-10-02T14:34:54.371Z" },
{ url = "https://files.pythonhosted.org/packages/33/76/35d05267ac82f53ae9b0e554da7c5e281ee61f3cad44c743f0fcd354f211/xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec", size = 32738, upload-time = "2025-10-02T14:34:55.839Z" },
{ url = "https://files.pythonhosted.org/packages/31/a8/3fbce1cd96534a95e35d5120637bf29b0d7f5d8fa2f6374e31b4156dd419/xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1", size = 30821, upload-time = "2025-10-02T14:34:57.219Z" },
{ url = "https://files.pythonhosted.org/packages/0c/ea/d387530ca7ecfa183cb358027f1833297c6ac6098223fd14f9782cd0015c/xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6", size = 194127, upload-time = "2025-10-02T14:34:59.21Z" },
{ url = "https://files.pythonhosted.org/packages/ba/0c/71435dcb99874b09a43b8d7c54071e600a7481e42b3e3ce1eb5226a5711a/xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263", size = 212975, upload-time = "2025-10-02T14:35:00.816Z" },
{ url = "https://files.pythonhosted.org/packages/84/7a/c2b3d071e4bb4a90b7057228a99b10d51744878f4a8a6dd643c8bd897620/xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546", size = 212241, upload-time = "2025-10-02T14:35:02.207Z" },
{ url = "https://files.pythonhosted.org/packages/81/5f/640b6eac0128e215f177df99eadcd0f1b7c42c274ab6a394a05059694c5a/xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89", size = 445471, upload-time = "2025-10-02T14:35:03.61Z" },
{ url = "https://files.pythonhosted.org/packages/5e/1e/3c3d3ef071b051cc3abbe3721ffb8365033a172613c04af2da89d5548a87/xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d", size = 193936, upload-time = "2025-10-02T14:35:05.013Z" },
{ url = "https://files.pythonhosted.org/packages/2c/bd/4a5f68381939219abfe1c22a9e3a5854a4f6f6f3c4983a87d255f21f2e5d/xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7", size = 210440, upload-time = "2025-10-02T14:35:06.239Z" },
{ url = "https://files.pythonhosted.org/packages/eb/37/b80fe3d5cfb9faff01a02121a0f4d565eb7237e9e5fc66e73017e74dcd36/xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db", size = 197990, upload-time = "2025-10-02T14:35:07.735Z" },
{ url = "https://files.pythonhosted.org/packages/d7/fd/2c0a00c97b9e18f72e1f240ad4e8f8a90fd9d408289ba9c7c495ed7dc05c/xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42", size = 210689, upload-time = "2025-10-02T14:35:09.438Z" },
{ url = "https://files.pythonhosted.org/packages/93/86/5dd8076a926b9a95db3206aba20d89a7fc14dd5aac16e5c4de4b56033140/xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11", size = 414068, upload-time = "2025-10-02T14:35:11.162Z" },
{ url = "https://files.pythonhosted.org/packages/af/3c/0bb129170ee8f3650f08e993baee550a09593462a5cddd8e44d0011102b1/xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd", size = 191495, upload-time = "2025-10-02T14:35:12.971Z" },
{ url = "https://files.pythonhosted.org/packages/e9/3a/6797e0114c21d1725e2577508e24006fd7ff1d8c0c502d3b52e45c1771d8/xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799", size = 30620, upload-time = "2025-10-02T14:35:14.129Z" },
{ url = "https://files.pythonhosted.org/packages/86/15/9bc32671e9a38b413a76d24722a2bf8784a132c043063a8f5152d390b0f9/xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392", size = 31542, upload-time = "2025-10-02T14:35:15.21Z" },
{ url = "https://files.pythonhosted.org/packages/39/c5/cc01e4f6188656e56112d6a8e0dfe298a16934b8c47a247236549a3f7695/xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6", size = 27880, upload-time = "2025-10-02T14:35:16.315Z" },
{ url = "https://files.pythonhosted.org/packages/f3/30/25e5321c8732759e930c555176d37e24ab84365482d257c3b16362235212/xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702", size = 32956, upload-time = "2025-10-02T14:35:17.413Z" },
{ url = "https://files.pythonhosted.org/packages/9f/3c/0573299560d7d9f8ab1838f1efc021a280b5ae5ae2e849034ef3dee18810/xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db", size = 31072, upload-time = "2025-10-02T14:35:18.844Z" },
{ url = "https://files.pythonhosted.org/packages/7a/1c/52d83a06e417cd9d4137722693424885cc9878249beb3a7c829e74bf7ce9/xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54", size = 196409, upload-time = "2025-10-02T14:35:20.31Z" },
{ url = "https://files.pythonhosted.org/packages/e3/8e/c6d158d12a79bbd0b878f8355432075fc82759e356ab5a111463422a239b/xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f", size = 215736, upload-time = "2025-10-02T14:35:21.616Z" },
{ url = "https://files.pythonhosted.org/packages/bc/68/c4c80614716345d55071a396cf03d06e34b5f4917a467faf43083c995155/xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5", size = 214833, upload-time = "2025-10-02T14:35:23.32Z" },
{ url = "https://files.pythonhosted.org/packages/7e/e9/ae27c8ffec8b953efa84c7c4a6c6802c263d587b9fc0d6e7cea64e08c3af/xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1", size = 448348, upload-time = "2025-10-02T14:35:25.111Z" },
{ url = "https://files.pythonhosted.org/packages/d7/6b/33e21afb1b5b3f46b74b6bd1913639066af218d704cc0941404ca717fc57/xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee", size = 196070, upload-time = "2025-10-02T14:35:26.586Z" },
{ url = "https://files.pythonhosted.org/packages/96/b6/fcabd337bc5fa624e7203aa0fa7d0c49eed22f72e93229431752bddc83d9/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd", size = 212907, upload-time = "2025-10-02T14:35:28.087Z" },
{ url = "https://files.pythonhosted.org/packages/4b/d3/9ee6160e644d660fcf176c5825e61411c7f62648728f69c79ba237250143/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729", size = 200839, upload-time = "2025-10-02T14:35:29.857Z" },
{ url = "https://files.pythonhosted.org/packages/0d/98/e8de5baa5109394baf5118f5e72ab21a86387c4f89b0e77ef3e2f6b0327b/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292", size = 213304, upload-time = "2025-10-02T14:35:31.222Z" },
{ url = "https://files.pythonhosted.org/packages/7b/1d/71056535dec5c3177eeb53e38e3d367dd1d16e024e63b1cee208d572a033/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf", size = 416930, upload-time = "2025-10-02T14:35:32.517Z" },
{ url = "https://files.pythonhosted.org/packages/dc/6c/5cbde9de2cd967c322e651c65c543700b19e7ae3e0aae8ece3469bf9683d/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033", size = 193787, upload-time = "2025-10-02T14:35:33.827Z" },
{ url = "https://files.pythonhosted.org/packages/19/fa/0172e350361d61febcea941b0cc541d6e6c8d65d153e85f850a7b256ff8a/xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec", size = 30916, upload-time = "2025-10-02T14:35:35.107Z" },
{ url = "https://files.pythonhosted.org/packages/ad/e6/e8cf858a2b19d6d45820f072eff1bea413910592ff17157cabc5f1227a16/xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8", size = 31799, upload-time = "2025-10-02T14:35:36.165Z" },
{ url = "https://files.pythonhosted.org/packages/56/15/064b197e855bfb7b343210e82490ae672f8bc7cdf3ddb02e92f64304ee8a/xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746", size = 28044, upload-time = "2025-10-02T14:35:37.195Z" },
{ url = "https://files.pythonhosted.org/packages/7e/5e/0138bc4484ea9b897864d59fce9be9086030825bc778b76cb5a33a906d37/xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e", size = 32754, upload-time = "2025-10-02T14:35:38.245Z" },
{ url = "https://files.pythonhosted.org/packages/18/d7/5dac2eb2ec75fd771957a13e5dda560efb2176d5203f39502a5fc571f899/xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405", size = 30846, upload-time = "2025-10-02T14:35:39.6Z" },
{ url = "https://files.pythonhosted.org/packages/fe/71/8bc5be2bb00deb5682e92e8da955ebe5fa982da13a69da5a40a4c8db12fb/xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3", size = 194343, upload-time = "2025-10-02T14:35:40.69Z" },
{ url = "https://files.pythonhosted.org/packages/e7/3b/52badfb2aecec2c377ddf1ae75f55db3ba2d321c5e164f14461c90837ef3/xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6", size = 213074, upload-time = "2025-10-02T14:35:42.29Z" },
{ url = "https://files.pythonhosted.org/packages/a2/2b/ae46b4e9b92e537fa30d03dbc19cdae57ed407e9c26d163895e968e3de85/xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063", size = 212388, upload-time = "2025-10-02T14:35:43.929Z" },
{ url = "https://files.pythonhosted.org/packages/f5/80/49f88d3afc724b4ac7fbd664c8452d6db51b49915be48c6982659e0e7942/xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7", size = 445614, upload-time = "2025-10-02T14:35:45.216Z" },
{ url = "https://files.pythonhosted.org/packages/ed/ba/603ce3961e339413543d8cd44f21f2c80e2a7c5cfe692a7b1f2cccf58f3c/xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b", size = 194024, upload-time = "2025-10-02T14:35:46.959Z" },
{ url = "https://files.pythonhosted.org/packages/78/d1/8e225ff7113bf81545cfdcd79eef124a7b7064a0bba53605ff39590b95c2/xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd", size = 210541, upload-time = "2025-10-02T14:35:48.301Z" },
{ url = "https://files.pythonhosted.org/packages/6f/58/0f89d149f0bad89def1a8dd38feb50ccdeb643d9797ec84707091d4cb494/xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0", size = 198305, upload-time = "2025-10-02T14:35:49.584Z" },
{ url = "https://files.pythonhosted.org/packages/11/38/5eab81580703c4df93feb5f32ff8fa7fe1e2c51c1f183ee4e48d4bb9d3d7/xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152", size = 210848, upload-time = "2025-10-02T14:35:50.877Z" },
{ url = "https://files.pythonhosted.org/packages/5e/6b/953dc4b05c3ce678abca756416e4c130d2382f877a9c30a20d08ee6a77c0/xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11", size = 414142, upload-time = "2025-10-02T14:35:52.15Z" },
{ url = "https://files.pythonhosted.org/packages/08/a9/238ec0d4e81a10eb5026d4a6972677cbc898ba6c8b9dbaec12ae001b1b35/xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5", size = 191547, upload-time = "2025-10-02T14:35:53.547Z" },
{ url = "https://files.pythonhosted.org/packages/f1/ee/3cf8589e06c2164ac77c3bf0aa127012801128f1feebf2a079272da5737c/xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f", size = 31214, upload-time = "2025-10-02T14:35:54.746Z" },
{ url = "https://files.pythonhosted.org/packages/02/5d/a19552fbc6ad4cb54ff953c3908bbc095f4a921bc569433d791f755186f1/xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad", size = 32290, upload-time = "2025-10-02T14:35:55.791Z" },
{ url = "https://files.pythonhosted.org/packages/b1/11/dafa0643bc30442c887b55baf8e73353a344ee89c1901b5a5c54a6c17d39/xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679", size = 28795, upload-time = "2025-10-02T14:35:57.162Z" },
{ url = "https://files.pythonhosted.org/packages/2c/db/0e99732ed7f64182aef4a6fb145e1a295558deec2a746265dcdec12d191e/xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4", size = 32955, upload-time = "2025-10-02T14:35:58.267Z" },
{ url = "https://files.pythonhosted.org/packages/55/f4/2a7c3c68e564a099becfa44bb3d398810cc0ff6749b0d3cb8ccb93f23c14/xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67", size = 31072, upload-time = "2025-10-02T14:35:59.382Z" },
{ url = "https://files.pythonhosted.org/packages/c6/d9/72a29cddc7250e8a5819dad5d466facb5dc4c802ce120645630149127e73/xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad", size = 196579, upload-time = "2025-10-02T14:36:00.838Z" },
{ url = "https://files.pythonhosted.org/packages/63/93/b21590e1e381040e2ca305a884d89e1c345b347404f7780f07f2cdd47ef4/xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b", size = 215854, upload-time = "2025-10-02T14:36:02.207Z" },
{ url = "https://files.pythonhosted.org/packages/ce/b8/edab8a7d4fa14e924b29be877d54155dcbd8b80be85ea00d2be3413a9ed4/xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b", size = 214965, upload-time = "2025-10-02T14:36:03.507Z" },
{ url = "https://files.pythonhosted.org/packages/27/67/dfa980ac7f0d509d54ea0d5a486d2bb4b80c3f1bb22b66e6a05d3efaf6c0/xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca", size = 448484, upload-time = "2025-10-02T14:36:04.828Z" },
{ url = "https://files.pythonhosted.org/packages/8c/63/8ffc2cc97e811c0ca5d00ab36604b3ea6f4254f20b7bc658ca825ce6c954/xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a", size = 196162, upload-time = "2025-10-02T14:36:06.182Z" },
{ url = "https://files.pythonhosted.org/packages/4b/77/07f0e7a3edd11a6097e990f6e5b815b6592459cb16dae990d967693e6ea9/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99", size = 213007, upload-time = "2025-10-02T14:36:07.733Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d8/bc5fa0d152837117eb0bef6f83f956c509332ce133c91c63ce07ee7c4873/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3", size = 200956, upload-time = "2025-10-02T14:36:09.106Z" },
{ url = "https://files.pythonhosted.org/packages/26/a5/d749334130de9411783873e9b98ecc46688dad5db64ca6e04b02acc8b473/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6", size = 213401, upload-time = "2025-10-02T14:36:10.585Z" },
{ url = "https://files.pythonhosted.org/packages/89/72/abed959c956a4bfc72b58c0384bb7940663c678127538634d896b1195c10/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93", size = 417083, upload-time = "2025-10-02T14:36:12.276Z" },
{ url = "https://files.pythonhosted.org/packages/0c/b3/62fd2b586283b7d7d665fb98e266decadf31f058f1cf6c478741f68af0cb/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518", size = 193913, upload-time = "2025-10-02T14:36:14.025Z" },
{ url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" },
{ url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" },
{ url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" },
{ url = "https://files.pythonhosted.org/packages/93/1e/8aec23647a34a249f62e2398c42955acd9b4c6ed5cf08cbea94dc46f78d2/xxhash-3.6.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f7b7e2ec26c1666ad5fc9dbfa426a6a3367ceaf79db5dd76264659d509d73b0", size = 30662, upload-time = "2025-10-02T14:37:01.743Z" },
{ url = "https://files.pythonhosted.org/packages/b8/0b/b14510b38ba91caf43006209db846a696ceea6a847a0c9ba0a5b1adc53d6/xxhash-3.6.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5dc1e14d14fa0f5789ec29a7062004b5933964bb9b02aae6622b8f530dc40296", size = 41056, upload-time = "2025-10-02T14:37:02.879Z" },
{ url = "https://files.pythonhosted.org/packages/50/55/15a7b8a56590e66ccd374bbfa3f9ffc45b810886c8c3b614e3f90bd2367c/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:881b47fc47e051b37d94d13e7455131054b56749b91b508b0907eb07900d1c13", size = 36251, upload-time = "2025-10-02T14:37:04.44Z" },
{ url = "https://files.pythonhosted.org/packages/62/b2/5ac99a041a29e58e95f907876b04f7067a0242cb85b5f39e726153981503/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6dc31591899f5e5666f04cc2e529e69b4072827085c1ef15294d91a004bc1bd", size = 32481, upload-time = "2025-10-02T14:37:05.869Z" },
{ url = "https://files.pythonhosted.org/packages/7b/d9/8d95e906764a386a3d3b596f3c68bb63687dfca806373509f51ce8eea81f/xxhash-3.6.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:15e0dac10eb9309508bfc41f7f9deaa7755c69e35af835db9cb10751adebc35d", size = 31565, upload-time = "2025-10-02T14:37:06.966Z" },
]
[[package]]
name = "zstandard"
version = "0.25.0"
-2
View File
@@ -1,3 +1 @@
.langgraph_api/
# Go cross-compiled binaries (built at release time, bundled into wheels)
langgraph_cli/bin/
-374
View File
@@ -1,374 +0,0 @@
# Go Migration Plan
## Goal
Move the full `langgraph` CLI implementation to Go while preserving existing
Python distribution and invocation flows.
Users should continue to be able to run:
- `langgraph ...`
- `uv run langgraph ...`
- `uvx langgraph ...`
During phase 1, the Go path is gated behind a feature flag. The Python package
remains the public entrypoint and launcher.
## Non-Goals
This phase does not include:
- JS migration
- `langsmith-cli` integration
- user-facing command renames
- intentional CLI behavior changes
- a long-lived dual implementation
## Source Of Truth
There will be one implementation of CLI behavior:
- shared Go implementation lives in the `langgraph` repo
- standalone Go `langgraph` binary uses that implementation
- Python `langgraph-cli` package is a thin launcher around that binary
- legacy Python implementation exists only temporarily as fallback during rollout
## Phase 1 Artifacts
Phase 1 ships these artifacts:
- shared Go package(s) in `langgraph`
- standalone `langgraph` Go binary
- Python wheel `langgraph-cli` that bundles the platform-specific Go binary
- Python launcher entrypoint that can route to legacy Python or Go
JS is explicitly out of scope for phase 1.
## User-Facing Command Scope
Phase 1 scope is the whole `langgraph` CLI, not just deploy.
Target command coverage:
- `langgraph deploy ...`
- `langgraph build ...`
- `langgraph up ...`
- `langgraph dockerfile ...`
- `langgraph dev ...`
- `langgraph new ...`
The goal is full parity with the current Python CLI command surface.
## Compatibility Contract
Behavior must not regress.
Required parity:
- exact JSON output where commands emit JSON
- exact or equivalent error semantics
- same exit codes
- same argument and flag behavior
- same generated artifacts for:
- Dockerfile output
- docker compose / inline compose output
- same API request semantics where mocked in tests
Human-readable output should be same or better, but not worse.
## Repo Ownership
Shared implementation lives in the current `langgraph` repo.
Reasons:
- current CLI spec and tests already live here
- rollout is initially only for `langgraph-cli`
- command compatibility should be driven by existing behavior in this repo
## Architecture
Use process boundaries, not language FFI.
Python should not call a Go shared library directly. Instead:
- Python launcher locates bundled `langgraph` Go binary
- Python launcher `exec`s or subprocesses into the Go binary
- Go handles all command execution
- for `dev`, Go subprocesses back into Python
This keeps the boundary simple and cross-platform.
## Go Package Structure
Recommended structure:
- `pkg/cli/config`
- parse and validate `langgraph.json`
- normalize config model
- `pkg/cli/docker`
- docker capability detection
- compose generation
- Dockerfile/build plan generation
- `pkg/cli/deploy`
- deployment flows
- host backend client
- polling, logs, revision logic
- `pkg/cli/dev`
- `dev` command orchestration
- Python subprocess handoff
- `pkg/cli/cmds`
- command runner functions with typed options/results
- no Cobra-specific code here
- `cmd/langgraph`
- standalone Go binary wrapping shared packages
Business logic should live in shared packages, not directly in CLI adapter code.
## Python Wrapper Model
The Python package remains installed as `langgraph-cli`, with entrypoint
`langgraph`.
During migration, the wrapper decides whether to route to legacy Python or Go.
Wrapper behavior:
1. inspect feature flags
2. resolve Go binary path
3. if Go path is active, `exec` into Go binary
4. otherwise fall back to legacy Python implementation
Long-term target:
- remove fallback
- Python wrapper always launches bundled Go binary
## Feature Flags
Temporary rollout env vars:
- `LANGGRAPH_USE_GO_CLI=1`
- route the Python wrapper to the Go binary instead of legacy Python
- `LANGGRAPH_GO_CLI_PATH=/path/to/langgraph`
- internal/dev/CI override for binary path resolution
- not intended as a long-term public interface
- `LANGGRAPH_CALLING_PYTHON=/path/to/python`
- set by the Python wrapper before invoking Go
- used by Go for `dev`
`LANGGRAPH_GO_CLI_PATH` is mainly for local development and CI and can be
removed later.
## `dev` Invocation Contract
`dev` is the main tricky area.
Design rule:
- Go owns CLI parsing and routing
- Python owns the actual in-process local dev server runtime
Flow for `uv run langgraph dev`:
1. `uv` selects the Python interpreter/environment
2. Python wrapper starts
3. Python wrapper sets `LANGGRAPH_CALLING_PYTHON=sys.executable`
4. Python wrapper launches Go binary
5. Go receives `dev`
6. Go shells out to that exact Python interpreter for the actual Python runtime behavior
This preserves the current selected Python environment.
Go Python resolution order for `dev`:
1. `LANGGRAPH_CALLING_PYTHON`
2. optional explicit override if added later
3. environment-derived interpreter / active venv
4. fallback detection
5. clear failure
The critical constraint is: if the user entered through Python, `dev` should
use that exact Python when possible.
## Why Not FFI
Do not use:
- cgo shared libs
- Python-Go FFI bindings
- embedded Python in Go
- RPC unless absolutely necessary
Reasons:
- packaging complexity
- cross-platform pain
- no advantage for a CLI architecture
- much worse release/debug story
Process-level boundaries are the right choice here.
## Packaging Constraints
The Go binary should be bundled inside Python wheels.
Preferred distribution model:
- build platform-specific `langgraph-cli` wheels
- each wheel includes the matching `langgraph` Go binary
- Python launcher resolves and executes the bundled binary
Do not rely on runtime download of the binary for normal operation.
Support matrix target:
- all OS/arch targets that are currently expected to be supported
- at minimum, align with the practical support matrix desired for the CLI,
using `orjson` support as a rough proxy if needed
If a platform is unsupported, fail clearly rather than silently falling back
forever.
## Release Constraints
Phase 1 versioning applies to:
- shared Go implementation
- standalone Go `langgraph` binary
- PyPI `langgraph-cli` wrapper
They should stay on one version line.
Constraint:
- bundled Go binary version must exactly match the Python wrapper version for
the migrated surface
The wrapper should detect obvious mismatch and fail clearly if it occurs.
## Migration Strategy
Use a big-bang hidden implementation change with gradual activation.
Phase 1 rollout:
1. implement full Go path behind `LANGGRAPH_USE_GO_CLI`
2. keep default behavior on legacy Python
3. run dual CI for legacy and Go-backed paths
4. dogfood with feature flag
5. flip default to Go
6. keep fallback briefly
7. remove fallback in about two weeks
This is a big internal rewrite with gradual external activation.
## CI Strategy
Dual CI is required during migration.
Run both variants:
- legacy Python implementation
- Python wrapper -> Go binary implementation
Required parity checks:
- help output
- exit code
- stdout
- stderr
- generated Dockerfile output
- generated compose output
- mocked deployment API request semantics
- validation errors / usage errors
Goal is not merely "both tests pass". Goal is "both implementations behave
identically enough to swap by default safely".
## Parity Test Philosophy
Use the current Python CLI tests as the behavioral spec.
Priority test areas:
- config validation
- compose/Dockerfile generation
- deployment flows
- error and prompt behavior
- command help / command surface
Where practical, add golden comparisons so regressions are obvious.
## Implementation Order Inside Phase 1
Even though rollout is one hidden phase, implementation should proceed in this
order:
1. wrapper contract and env contract
2. Go command scaffolding and package boundaries
3. config + docker/build/compose logic
4. deploy flows
5. remaining commands
6. `dev` subprocess orchestration
7. parity hardening in CI
This reduces risk because `dev` is the highest-uncertainty area.
## Command Ownership Constraint
All command behavior should live in Go once ported.
Do not allow:
- some flags parsed in Python and others in Go
- duplicated command logic across Python and Go
- separate behavior definitions for legacy and migrated commands
The wrapper should be thin only.
## Fallback Constraint
Fallback is temporary, not a product feature.
Policy:
- use feature flag during migration
- flip default after parity confidence
- remove legacy Python implementation roughly two weeks later
Do not normalize to permanent dual execution paths.
## Documentation Constraint
During migration, documentation should stay conservative:
- existing Python install flow remains primary
- feature flag is acceptable for internal/dogfood docs
- avoid broad external messaging about the Go implementation until default is flipped
## Open Issues To Track
These are not blockers, but they need explicit implementation decisions:
- exact bundled wheel layout for binaries
- exact list of supported OS/arch targets
- whether to expose a public `--python` override for `dev`
- whether some pretty output is allowed to improve while keeping parsed output stable
## Summary
Phase 1 plan:
- move the entire `langgraph` CLI implementation into shared Go code in the
`langgraph` repo
- ship a standalone `langgraph` Go binary
- keep `langgraph-cli` on PyPI as a thin launcher that bundles and executes
that binary
- preserve `uv run` / `uvx` behavior
- handle `dev` by passing the calling Python path through the wrapper and
having Go subprocess back into Python
- gate everything behind `LANGGRAPH_USE_GO_CLI`
- run dual CI until parity is proven
- flip default
- remove legacy fallback quickly
+3 -84
View File
@@ -1,21 +1,15 @@
.PHONY: test lint type format test-integration update-schema bump-version
.PHONY: test-go lint-go format-go
.PHONY: build-go build-go-all clean-go-bin
######################
# TESTING AND COVERAGE
######################
TEST?= "tests/unit_tests"
GO_FILES=$(shell find cmd internal -type f -name '*.go' 2>/dev/null)
test: test-go
test:
uv run pytest $(TEST)
test-integration:
uv run pytest tests/integration_tests
test-go:
[ ! -f go.mod ] || go test ./...
######################
# LINTING AND FORMATTING
######################
@@ -29,94 +23,19 @@ lint_package: PYTHON_FILES=langgraph_cli
lint_tests: PYTHON_FILES=tests
lint_tests: MYPY_CACHE=.mypy_cache_test
lint lint_diff lint_package lint_tests: lint-go
lint lint_diff lint_package lint_tests:
uv run ruff check .
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) || uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
lint-go:
[ -z "$(GO_FILES)" ] || test -z "$$(gofmt -l $(GO_FILES))"
type:
mkdir -p $(MYPY_CACHE) && uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
format format_diff: format-go
format format_diff:
uv run ruff format $(PYTHON_FILES)
uv run ruff check --select I --fix $(PYTHON_FILES)
format-go:
[ -z "$(GO_FILES)" ] || gofmt -w $(GO_FILES)
######################
# GO BINARY CROSS-COMPILATION
######################
GO_MODULE=github.com/langchain-ai/langgraph/libs/cli
GO_BINARY=cmd/langgraph/main.go
GO_VERSION_PKG=$(GO_MODULE)/internal/version
GO_BIN_DIR=langgraph_cli/bin
GO_VERSION=$(shell grep -m 1 '^__version__' langgraph_cli/__init__.py | cut -d '"' -f 2)
GO_COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
GO_DATE=$(shell date -u +%Y-%m-%dT%H:%M:%SZ)
GO_LDFLAGS=-s -w \
-X '$(GO_VERSION_PKG).Version=$(GO_VERSION)' \
-X '$(GO_VERSION_PKG).Commit=$(GO_COMMIT)' \
-X '$(GO_VERSION_PKG).Date=$(GO_DATE)'
# Platform matrix — covers every platform orjson ships for. Since the Go
# binary is statically linked (CGO_ENABLED=0), the same linux binary works
# on both glibc and musl. Musllinux wheels reuse the linux binary but are
# tagged differently so pip installs them on Alpine/musl systems.
GO_PLATFORMS = \
linux/amd64 \
linux/arm64 \
linux/arm \
linux/386 \
linux/ppc64le \
linux/s390x \
darwin/amd64 \
darwin/arm64 \
windows/amd64 \
windows/arm64 \
windows/386
# Build for the current platform (development).
build-go:
@mkdir -p $(GO_BIN_DIR)
go build -ldflags "$(GO_LDFLAGS)" -o $(GO_BIN_DIR)/langgraph $(GO_BINARY)
@echo "Built $(GO_BIN_DIR)/langgraph"
# Build for a single target: make build-go-target GOOS=linux GOARCH=amd64
build-go-target:
$(eval EXT=$(if $(filter windows,$(GOOS)),.exe,))
@mkdir -p $(GO_BIN_DIR)
GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 \
go build -ldflags "$(GO_LDFLAGS)" \
-o $(GO_BIN_DIR)/langgraph-$(GOOS)-$(GOARCH)$(EXT) $(GO_BINARY)
@echo "Built $(GO_BIN_DIR)/langgraph-$(GOOS)-$(GOARCH)$(EXT)"
# Build for all platforms.
build-go-all:
@mkdir -p $(GO_BIN_DIR)
@for platform in $(GO_PLATFORMS); do \
os=$${platform%/*}; arch=$${platform#*/}; \
ext=""; \
if [ "$$os" = "windows" ]; then ext=".exe"; fi; \
echo "Building $$os/$$arch..."; \
GOOS=$$os GOARCH=$$arch CGO_ENABLED=0 \
go build -ldflags "$(GO_LDFLAGS)" \
-o $(GO_BIN_DIR)/langgraph-$$os-$$arch$$ext $(GO_BINARY) || exit 1; \
done
@echo "All platforms built in $(GO_BIN_DIR)/"
clean-go-bin:
rm -rf $(GO_BIN_DIR)
######################
# SCHEMA AND VERSIONING
######################
update-schema:
uv run python generate_schema.py
-11
View File
@@ -1,11 +0,0 @@
package main
import (
"os"
"github.com/langchain-ai/langgraph/libs/cli/internal/root"
)
func main() {
os.Exit(root.Run(os.Args[1:], os.Stdout, os.Stderr))
}
@@ -5,5 +5,5 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langchain-openai==1.0.1"
"langchain-openai==1.1.14"
]
@@ -5,7 +5,7 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langchain-openai==1.0.0a2",
"langchain-openai==1.1.14",
"langchain-anthropic==1.0.0a5",
"langgraph==1.1.5"
]
@@ -5,7 +5,7 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langchain-openai==1.0.0a2",
"langchain-openai==1.1.14",
"langgraph==1.1.2",
"langchain_community>=0.3.0",
]
-3
View File
@@ -1,3 +0,0 @@
module github.com/langchain-ai/langgraph/libs/cli
go 1.23.0
-59
View File
@@ -1,59 +0,0 @@
"""Hatch build hook that bundles the platform-specific Go binary into the wheel.
Usage:
1. Cross-compile: make build-go-target GOOS=linux GOARCH=amd64
2. Set LANGGRAPH_GO_BINARY to the built binary path
3. Build wheel: uv build --wheel
The hook copies the binary into langgraph_cli/bin/ so the entrypoint can find it.
If LANGGRAPH_GO_BINARY is not set, the wheel is built without a binary (pure Python
fallback fine for development and the legacy code path).
"""
from __future__ import annotations
import os
import shutil
import stat
from pathlib import Path
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
class GoBinaryBuildHook(BuildHookInterface):
PLUGIN_NAME = "go-binary"
def initialize(self, version: str, build_data: dict) -> None:
bin_dir = Path("langgraph_cli/bin")
if bin_dir.exists():
shutil.rmtree(bin_dir)
binary_path = os.environ.get("LANGGRAPH_GO_BINARY")
if not binary_path:
return
source = Path(binary_path)
if not source.is_file():
msg = f"LANGGRAPH_GO_BINARY points to missing file: {source}"
raise FileNotFoundError(msg)
bin_dir.mkdir(parents=True, exist_ok=True)
# Determine output name (langgraph or langgraph.exe)
dest_name = "langgraph.exe" if source.suffix == ".exe" else "langgraph"
dest = bin_dir / dest_name
shutil.copy2(str(source), str(dest))
# Ensure executable permission
dest.chmod(dest.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
# Tell hatch to include the binary in the wheel
build_data["shared_data"] = {}
build_data["force_include"] = {
str(dest): f"langgraph_cli/bin/{dest_name}",
}
# Set the platform tag so pip installs the right wheel
platform_tag = os.environ.get("LANGGRAPH_WHEEL_PLAT")
if platform_tag:
build_data["tag"] = f"py3-none-{platform_tag}"
-698
View File
@@ -1,698 +0,0 @@
// Package config provides validation for langgraph.json configuration files.
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
const (
MinNodeVersion = "20"
DefaultNodeVersion = "20"
MinPythonVersion = "3.11"
DefaultPythonVersion = "3.11"
DefaultImageDistro = "debian"
)
var validDistros = []string{"debian", "wolfi", "bookworm"}
var knownConfigKeys = map[string]bool{
"python_version": true,
"node_version": true,
"api_version": true,
"base_image": true,
"image_distro": true,
"pip_config_file": true,
"pip_installer": true,
"source": true,
"dependencies": true,
"dockerfile_lines": true,
"graphs": true,
"env": true,
"store": true,
"auth": true,
"encryption": true,
"http": true,
"webhooks": true,
"checkpointer": true,
"ui": true,
"ui_config": true,
"keep_pkg_tools": true,
"_INTERNAL_docker_tag": true,
"project_root": true,
"package": true,
}
var nodeExtensions = map[string]bool{
".ts": true, ".mts": true, ".cts": true,
".js": true, ".mjs": true, ".cjs": true,
}
// isNodeGraph checks whether a graph spec refers to a Node.js file.
func isNodeGraph(spec any) bool {
var filePath string
switch v := spec.(type) {
case string:
filePath = strings.SplitN(v, ":", 2)[0]
case map[string]any:
if p, _ := v["path"].(string); p != "" {
filePath = strings.SplitN(p, ":", 2)[0]
}
}
return nodeExtensions[filepath.Ext(filePath)]
}
// getSourceKind extracts source.kind from a raw config.
func getSourceKind(raw map[string]any) string {
source, ok := raw["source"]
if !ok {
return ""
}
m, ok := source.(map[string]any)
if !ok {
return ""
}
kind, _ := m["kind"].(string)
return kind
}
// getString returns the string value for key, or "" if missing/wrong type.
func getString(raw map[string]any, key string) string {
v, _ := raw[key].(string)
return v
}
// parseVersion parses "3.11" or "0.8.1" into integer parts.
func parseVersion(s string) ([]int, error) {
s = strings.SplitN(s, "-", 2)[0]
parts := strings.Split(s, ".")
result := make([]int, len(parts))
for i, p := range parts {
n, err := strconv.Atoi(p)
if err != nil {
return nil, fmt.Errorf("invalid version part: %s", p)
}
result[i] = n
}
return result, nil
}
// versionLessThan returns true if a < b (component-wise).
func versionLessThan(a, b []int) bool {
for i := 0; i < len(a) && i < len(b); i++ {
if a[i] < b[i] {
return true
}
if a[i] > b[i] {
return false
}
}
return len(a) < len(b)
}
// ValidateConfig validates a raw config map and returns a normalised copy.
// Errors match the Python CLI's click.UsageError messages exactly.
func ValidateConfig(raw map[string]any) (map[string]any, error) {
// --- detect graph types ---
graphs, _ := raw["graphs"].(map[string]any)
hasNode, hasPython := false, false
for _, spec := range graphs {
if isNodeGraph(spec) {
hasNode = true
} else {
hasPython = true
}
}
// --- version defaults ---
nodeVersion := getString(raw, "node_version")
pythonVersion := getString(raw, "python_version")
if hasNode && nodeVersion == "" {
nodeVersion = DefaultNodeVersion
}
if hasPython && pythonVersion == "" {
pythonVersion = DefaultPythonVersion
}
imageDistro := getString(raw, "image_distro")
if imageDistro == "" {
imageDistro = DefaultImageDistro
}
// --- mutual exclusion: _INTERNAL_docker_tag vs api_version ---
_, hasInternalTag := raw["_INTERNAL_docker_tag"]
_, hasAPIVersion := raw["api_version"]
if hasInternalTag && hasAPIVersion {
return nil, fmt.Errorf("Cannot specify both _INTERNAL_docker_tag and api_version.")
}
// --- api_version format ---
if apiVersion := getString(raw, "api_version"); apiVersion != "" {
base := strings.SplitN(apiVersion, "-", 2)[0]
parts := strings.Split(base, ".")
if len(parts) > 3 {
return nil, fmt.Errorf("Version must be major or major.minor or major.minor.patch.")
}
for _, p := range parts {
if _, err := strconv.Atoi(p); err != nil {
return nil, fmt.Errorf(
"Invalid version format: %s.\n\n"+
"Pin to a minor version, e.g.:\n"+
" \"api_version\": \"0.8\"", apiVersion)
}
}
}
// --- build result config with defaults ---
config := map[string]any{
"node_version": nodeVersion,
"python_version": pythonVersion,
"pip_config_file": raw["pip_config_file"],
"pip_installer": "auto",
"source": raw["source"],
"base_image": raw["base_image"],
"image_distro": imageDistro,
"dependencies": raw["dependencies"],
"dockerfile_lines": raw["dockerfile_lines"],
"graphs": raw["graphs"],
"env": raw["env"],
"store": raw["store"],
"auth": raw["auth"],
"encryption": raw["encryption"],
"http": raw["http"],
"webhooks": raw["webhooks"],
"checkpointer": raw["checkpointer"],
"ui": raw["ui"],
"ui_config": raw["ui_config"],
"keep_pkg_tools": raw["keep_pkg_tools"],
}
if raw["pip_installer"] != nil {
config["pip_installer"] = raw["pip_installer"]
}
if hasInternalTag {
config["_INTERNAL_docker_tag"] = raw["_INTERNAL_docker_tag"]
}
if hasAPIVersion {
config["api_version"] = raw["api_version"]
}
// Apply list defaults.
if config["dependencies"] == nil {
config["dependencies"] = []any{}
}
if config["dockerfile_lines"] == nil {
config["dockerfile_lines"] = []any{}
}
if config["graphs"] == nil {
config["graphs"] = map[string]any{}
}
if config["env"] == nil {
config["env"] = map[string]any{}
}
// --- node_version validation ---
if nodeVersion != "" {
if strings.Contains(nodeVersion, ".") {
return nil, fmt.Errorf("Node.js version must be major version only")
}
major, err := strconv.Atoi(nodeVersion)
if err != nil {
return nil, fmt.Errorf(
"Invalid Node.js version format: %s. Use major version only (e.g., '20').",
nodeVersion)
}
minMajor, _ := strconv.Atoi(MinNodeVersion)
if major < minMajor {
return nil, fmt.Errorf(
"Node.js version %s is not supported. "+
"Minimum required version is %s.\n\n"+
"Set node_version to %s or higher:\n"+
" \"node_version\": \"%s\"",
nodeVersion, MinNodeVersion, MinNodeVersion, MinNodeVersion)
}
}
// --- pip_installer validation ---
if pi, ok := raw["pip_installer"].(string); ok {
switch pi {
case "auto", "pip", "uv":
// valid
default:
return nil, fmt.Errorf(
"Invalid pip_installer: '%s'. "+
"Consider using uv-based source management instead:\n\n"+
" \"source\": {\"kind\": \"uv\", \"root\": \"..\"}",
pi)
}
}
// --- source validation ---
sourceKind := getSourceKind(raw)
if source := raw["source"]; source != nil {
if _, ok := source.(map[string]any); !ok {
return nil, fmt.Errorf(
"`source` must be an object, e.g.:\n" +
" \"source\": {\"kind\": \"uv\", \"root\": \"..\"}")
}
if sourceKind != "uv" {
return nil, fmt.Errorf(
"Invalid source.kind. The only supported value is 'uv':\n" +
" \"source\": {\"kind\": \"uv\", \"root\": \"..\"}")
}
}
// --- python_version validation ---
if pythonVersion != "" {
base := strings.SplitN(pythonVersion, "-", 2)[0]
dotParts := strings.Split(base, ".")
allDigits := true
for _, p := range dotParts {
if _, err := strconv.Atoi(p); err != nil {
allDigits = false
break
}
}
if len(dotParts) != 2 || !allDigits {
fix := MinPythonVersion
if len(dotParts) >= 2 {
fix = dotParts[0] + "." + dotParts[1]
}
return nil, fmt.Errorf(
"Invalid Python version format: %s. "+
"Use 'major.minor' format — patch version cannot be specified.\n\n"+
" \"python_version\": \"%s\"",
pythonVersion, fix)
}
pyParsed, _ := parseVersion(pythonVersion)
minParsed, _ := parseVersion(MinPythonVersion)
if versionLessThan(pyParsed, minParsed) {
return nil, fmt.Errorf(
"Python version %s is not supported. "+
"Minimum required version is %s.\n\n"+
" \"python_version\": \"%s\"",
pythonVersion, MinPythonVersion, MinPythonVersion)
}
if strings.Contains(pythonVersion, "bullseye") {
return nil, fmt.Errorf(
"Bullseye images were deprecated in version 0.4.13. " +
"Please use 'bookworm' or 'debian' instead.")
}
// dependencies required when not uv
deps, _ := config["dependencies"].([]any)
if sourceKind != "uv" && len(deps) == 0 {
return nil, fmt.Errorf(
"No dependencies found in config. " +
"Consider using uv-based source management:\n\n" +
" \"source\": {\"kind\": \"uv\", \"root\": \"..\"}")
}
}
// --- graphs required ---
graphMap, _ := config["graphs"].(map[string]any)
if len(graphMap) == 0 {
return nil, fmt.Errorf(
"No graphs found in config. Add at least one graph, e.g.:\n" +
" \"graphs\": {\n" +
" \"agent\": \"./my_agent/graph.py:graph\"\n" +
" }")
}
// --- image_distro validation ---
if imageDistro == "bullseye" {
return nil, fmt.Errorf(
"Bullseye images were deprecated in version 0.4.13. " +
"Please use 'bookworm' or 'debian' instead.")
}
validDistro := false
for _, d := range validDistros {
if imageDistro == d {
validDistro = true
break
}
}
if !validDistro {
quoted := make([]string, len(validDistros))
for i, d := range validDistros {
quoted[i] = fmt.Sprintf("'%s'", d)
}
return nil, fmt.Errorf(
"Invalid image_distro: '%s'. "+
"Must be one of: %s.\n\n"+
" \"image_distro\": \"wolfi\" (recommended)",
imageDistro, strings.Join(quoted, ", "))
}
// --- uv source mode validation ---
if sourceKind == "uv" {
var errs []string
if pythonVersion == "" {
errs = append(errs, "source.kind 'uv' requires `python_version` — it is a Python-only deployment mode. Node.js-only graphs are not supported.")
}
deps, _ := raw["dependencies"].([]any)
if deps != nil && len(deps) > 0 {
errs = append(errs, "Remove `dependencies` from your config. With `source.kind = \"uv\"`, all dependencies are read from your pyproject.toml and uv.lock instead.")
}
// Also check if dependencies key exists even if empty array.
if deps == nil {
if rawDeps, exists := raw["dependencies"]; exists && rawDeps != nil {
// dependencies key present but not an array — still flag it
if depsArr, ok := rawDeps.([]any); ok && len(depsArr) > 0 {
errs = append(errs, "Remove `dependencies` from your config. With `source.kind = \"uv\"`, all dependencies are read from your pyproject.toml and uv.lock instead.")
}
}
}
sourceMap, _ := raw["source"].(map[string]any)
if root, exists := sourceMap["root"]; exists {
rootStr, ok := root.(string)
if !ok {
errs = append(errs, fmt.Sprintf("`source.root` must be a string, got %T.", root))
} else if rootStr == "" {
errs = append(errs, "`source.root` must be a non-empty string. Use `\".\"`.")
}
}
if pkg, exists := sourceMap["package"]; exists {
if pkg != nil {
pkgStr, ok := pkg.(string)
if !ok {
errs = append(errs, "`source.package` must be a non-empty string.")
} else if pkgStr == "" {
errs = append(errs, "`source.package` must be a non-empty string.")
}
}
}
if len(errs) > 0 {
formatted := ""
for i, e := range errs {
formatted += fmt.Sprintf("\n %d. %s", i+1, e)
}
return nil, fmt.Errorf(
"source.kind 'uv' requires a different config shape than dependency-based installs:%s",
formatted)
}
}
// --- legacy project_root / package ---
_, hasProjectRoot := raw["project_root"]
_, hasPackage := raw["package"]
if hasProjectRoot || hasPackage {
return nil, fmt.Errorf(
"Top-level `project_root` and `package` are no longer supported. " +
"Use `source.root` and `source.package` instead.")
}
// --- auth path validation ---
if auth, ok := raw["auth"].(map[string]any); ok {
if authPath, _ := auth["path"].(string); authPath != "" {
if !strings.Contains(authPath, ":") {
return nil, fmt.Errorf(
"Invalid auth.path format: '%s'. "+
"Must be in format './path/to/file.py:attribute_name'",
authPath)
}
}
}
// --- encryption path validation ---
if enc, ok := raw["encryption"].(map[string]any); ok {
if encPath, _ := enc["path"].(string); encPath != "" {
if !strings.Contains(encPath, ":") {
return nil, fmt.Errorf(
"Invalid encryption.path format: '%s'. "+
"Must be in format './path/to/file.py:attribute_name'",
encPath)
}
}
}
// --- http.app path validation ---
if httpConf, ok := raw["http"].(map[string]any); ok {
if app, _ := httpConf["app"].(string); app != "" {
if !strings.Contains(app, ":") {
return nil, fmt.Errorf(
"Invalid http.app format: '%s'. "+
"Must be in format './path/to/file.py:attribute_name'",
app)
}
}
}
// --- keep_pkg_tools validation ---
if kpt := raw["keep_pkg_tools"]; kpt != nil {
validBuildTools := map[string]bool{"pip": true, "setuptools": true, "wheel": true}
switch v := kpt.(type) {
case bool:
// ok
case []any:
for _, item := range v {
tool, ok := item.(string)
if !ok || !validBuildTools[tool] {
return nil, fmt.Errorf(
"Invalid keep_pkg_tools: '%v'. "+
"Must be one of 'pip', 'setuptools', 'wheel'.",
item)
}
}
default:
return nil, fmt.Errorf(
"Invalid keep_pkg_tools: '%v'. "+
"Must be bool or list[str] (with values 'pip', 'setuptools', and/or 'wheel').",
kpt)
}
}
return config, nil
}
// ValidateConfigFile loads a config file, validates it, and returns the result.
func ValidateConfigFile(configPath string) (map[string]any, error) {
raw, err := LoadRawConfigFile(configPath)
if err != nil {
return nil, err
}
return validateConfigFile(configPath, raw)
}
// LoadRawConfigFile loads a config file and requires the top-level JSON value
// to be an object.
func LoadRawConfigFile(configPath string) (map[string]any, error) {
data, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("could not read config file: %w", err)
}
var rawAny any
if err := json.Unmarshal(data, &rawAny); err != nil {
return nil, fmt.Errorf("Invalid JSON in %s: %s", configPath, err.Error())
}
raw, ok := rawAny.(map[string]any)
if !ok {
return nil, fmt.Errorf(
"Invalid config in %s: top-level JSON value must be an object.",
configPath,
)
}
return raw, nil
}
func validateConfigFile(configPath string, raw map[string]any) (map[string]any, error) {
validated, err := ValidateConfig(raw)
if err != nil {
return nil, err
}
// Check package.json node version if node_version is set.
if nv, _ := validated["node_version"].(string); nv != "" {
dir := filepath.Dir(configPath)
pkgJSONPath := filepath.Join(dir, "package.json")
if info, statErr := os.Stat(pkgJSONPath); statErr == nil && !info.IsDir() {
if pkgErr := validatePackageJSON(pkgJSONPath); pkgErr != nil {
return nil, pkgErr
}
}
}
return validated, nil
}
func validatePackageJSON(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return nil
}
var pkg map[string]any
if err := json.Unmarshal(data, &pkg); err != nil {
return fmt.Errorf(
"Invalid package.json found in langgraph config directory %s: file is not valid JSON",
path,
)
}
enginesRaw, ok := pkg["engines"]
if !ok {
return nil
}
engines, ok := enginesRaw.(map[string]any)
if !ok {
return nil
}
for k := range engines {
if k != "node" {
keys := make([]string, 0, len(engines))
for ek := range engines {
keys = append(keys, ek)
}
return fmt.Errorf(
"Only 'node' engine is supported in package.json engines. Got engines: %v",
keys)
}
}
if nodeVer, ok := engines["node"].(string); ok && nodeVer != "" {
if strings.Contains(nodeVer, ".") {
return fmt.Errorf(
"Node.js version in package.json engines must be >= %s "+
"(major version only), got '%s'. "+
"Minor/patch versions (like '20.x.y') are not supported to "+
"prevent deployment issues when new Node.js versions are released.",
MinNodeVersion, nodeVer)
}
major, err := strconv.Atoi(nodeVer)
if err == nil {
minMajor, _ := strconv.Atoi(MinNodeVersion)
if major < minMajor {
return fmt.Errorf(
"Node.js version in package.json engines must be >= %s "+
"(major version only), got '%s'. "+
"Minor/patch versions (like '20.x.y') are not supported to "+
"prevent deployment issues when new Node.js versions are released.",
MinNodeVersion, nodeVer)
}
}
}
return nil
}
// GetUnknownKeys returns warnings for unrecognised top-level keys.
func GetUnknownKeys(raw map[string]any) []string {
var unknown []string
for k := range raw {
if !knownConfigKeys[k] {
unknown = append(unknown, k)
}
}
sortStrings(unknown)
var warnings []string
knownList := make([]string, 0, len(knownConfigKeys))
for k := range knownConfigKeys {
knownList = append(knownList, k)
}
for _, key := range unknown {
if close := closestMatch(key, knownList); close != "" {
warnings = append(warnings, fmt.Sprintf("Unknown key '%s' — did you mean '%s'?", key, close))
} else {
warnings = append(warnings, fmt.Sprintf("Unknown key '%s' is not a recognized config field.", key))
}
}
return warnings
}
// closestMatch finds the best match for word among candidates using edit distance.
// Returns "" if no match is close enough (ratio >= 0.6).
func closestMatch(word string, candidates []string) string {
best := ""
bestRatio := 0.6 // minimum threshold
for _, c := range candidates {
ratio := similarity(word, c)
if ratio > bestRatio {
bestRatio = ratio
best = c
}
}
return best
}
// similarity returns a ratio in [0,1] based on Levenshtein distance.
func similarity(a, b string) float64 {
maxLen := len(a)
if len(b) > maxLen {
maxLen = len(b)
}
if maxLen == 0 {
return 1.0
}
dist := editDistance(a, b)
return 1.0 - float64(dist)/float64(maxLen)
}
// editDistance computes Levenshtein distance between two strings.
func editDistance(a, b string) int {
la, lb := len(a), len(b)
if la == 0 {
return lb
}
if lb == 0 {
return la
}
prev := make([]int, lb+1)
curr := make([]int, lb+1)
for j := 0; j <= lb; j++ {
prev[j] = j
}
for i := 1; i <= la; i++ {
curr[0] = i
for j := 1; j <= lb; j++ {
cost := 1
if a[i-1] == b[j-1] {
cost = 0
}
ins := curr[j-1] + 1
del := prev[j] + 1
sub := prev[j-1] + cost
curr[j] = min3(ins, del, sub)
}
prev, curr = curr, prev
}
return prev[lb]
}
func min3(a, b, c int) int {
if a < b {
if a < c {
return a
}
return c
}
if b < c {
return b
}
return c
}
// sortStrings sorts a slice of strings in place (simple insertion sort, fine for small n).
func sortStrings(s []string) {
for i := 1; i < len(s); i++ {
for j := i; j > 0 && s[j] < s[j-1]; j-- {
s[j], s[j-1] = s[j-1], s[j]
}
}
}
-568
View File
@@ -1,568 +0,0 @@
package config
import (
"strings"
"testing"
)
// baseConfig returns a minimal valid config map. Tests should copy and modify it.
func baseConfig() map[string]any {
return map[string]any{
"dependencies": []any{"langchain"},
"graphs": map[string]any{"agent": "./agent.py:graph"},
}
}
// copyMap returns a shallow copy of m.
func copyMap(m map[string]any) map[string]any {
out := make(map[string]any, len(m))
for k, v := range m {
out[k] = v
}
return out
}
// mustSucceed is a test helper that fails if err is non-nil.
func mustSucceed(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("expected success but got error: %v", err)
}
}
// mustFail is a test helper that fails if err is nil.
func mustFail(t *testing.T, err error) {
t.Helper()
if err == nil {
t.Fatal("expected error but got nil")
}
}
// mustContain checks that err is non-nil and its message contains substr.
func mustContain(t *testing.T, err error, substr string) {
t.Helper()
if err == nil {
t.Fatalf("expected error containing %q but got nil", substr)
}
if !strings.Contains(err.Error(), substr) {
t.Fatalf("expected error to contain %q, got: %s", substr, err.Error())
}
}
func TestValidateConfigValid(t *testing.T) {
t.Run("minimal config", func(t *testing.T) {
raw := baseConfig()
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if pv, _ := result["python_version"].(string); pv != "3.11" {
t.Fatalf("expected python_version '3.11', got %q", pv)
}
if id, _ := result["image_distro"].(string); id != "debian" {
t.Fatalf("expected image_distro 'debian', got %q", id)
}
})
t.Run("full config with all optional fields", func(t *testing.T) {
raw := map[string]any{
"python_version": "3.12",
"image_distro": "wolfi",
"pip_installer": "uv",
"dependencies": []any{"langchain", "langgraph"},
"graphs": map[string]any{"agent": "./agent.py:graph"},
"env": map[string]any{"FOO": "bar"},
"dockerfile_lines": []any{"RUN apt-get update"},
"auth": map[string]any{"path": "./auth.py:handler"},
"encryption": map[string]any{"path": "./enc.py:enc"},
"http": map[string]any{"app": "./app.py:app"},
"keep_pkg_tools": true,
"api_version": "0.8",
}
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
}
func TestValidateConfigPythonVersion(t *testing.T) {
validVersions := []string{"3.11", "3.12", "3.13"}
for _, v := range validVersions {
t.Run("valid "+v, func(t *testing.T) {
raw := copyMap(baseConfig())
raw["python_version"] = v
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
}
t.Run("valid 3.12-slim suffix stripped", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["python_version"] = "3.12-slim"
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
tooOld := []string{"3.10", "3.9"}
for _, v := range tooOld {
t.Run("too old "+v, func(t *testing.T) {
raw := copyMap(baseConfig())
raw["python_version"] = v
_, err := ValidateConfig(raw)
mustContain(t, err, "Minimum required version")
})
}
badFormat := []struct {
version string
}{
{"3.11.0"},
{"3"},
{"abc.def"},
}
for _, tc := range badFormat {
t.Run("bad format "+tc.version, func(t *testing.T) {
raw := copyMap(baseConfig())
raw["python_version"] = tc.version
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid Python version format")
})
}
}
func TestValidateConfigNodeVersion(t *testing.T) {
// Need a node graph to trigger node_version validation
nodeBase := func() map[string]any {
return map[string]any{
"dependencies": []any{"langchain"},
"graphs": map[string]any{"agent": "./agent.py:graph"},
}
}
t.Run("valid 20", func(t *testing.T) {
raw := nodeBase()
raw["node_version"] = "20"
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("valid 22", func(t *testing.T) {
raw := nodeBase()
raw["node_version"] = "22"
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("too old 18", func(t *testing.T) {
raw := nodeBase()
raw["node_version"] = "18"
_, err := ValidateConfig(raw)
mustContain(t, err, "Minimum required version is 20")
})
t.Run("minor version 20.1", func(t *testing.T) {
raw := nodeBase()
raw["node_version"] = "20.1"
_, err := ValidateConfig(raw)
mustContain(t, err, "major version only")
})
}
func TestValidateConfigGraphs(t *testing.T) {
t.Run("empty graphs", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["graphs"] = map[string]any{}
_, err := ValidateConfig(raw)
mustContain(t, err, "No graphs found")
})
t.Run("missing graphs key", func(t *testing.T) {
raw := map[string]any{
"dependencies": []any{"langchain"},
}
_, err := ValidateConfig(raw)
mustContain(t, err, "No graphs found")
})
}
func TestValidateConfigImageDistro(t *testing.T) {
validDistroTests := []string{"debian", "wolfi", "bookworm"}
for _, d := range validDistroTests {
t.Run("valid "+d, func(t *testing.T) {
raw := copyMap(baseConfig())
raw["image_distro"] = d
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
}
t.Run("bullseye deprecated", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["image_distro"] = "bullseye"
_, err := ValidateConfig(raw)
mustContain(t, err, "deprecated")
})
t.Run("invalid ubuntu", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["image_distro"] = "ubuntu"
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid image_distro")
})
t.Run("invalid alpine", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["image_distro"] = "alpine"
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid image_distro")
})
t.Run("default is debian", func(t *testing.T) {
raw := baseConfig()
// no image_distro key
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if id, _ := result["image_distro"].(string); id != "debian" {
t.Fatalf("expected default image_distro 'debian', got %q", id)
}
})
}
func TestValidateConfigPipInstaller(t *testing.T) {
valid := []string{"auto", "pip", "uv"}
for _, pi := range valid {
t.Run("valid "+pi, func(t *testing.T) {
raw := copyMap(baseConfig())
raw["pip_installer"] = pi
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
}
invalid := []string{"conda", "uv_lock"}
for _, pi := range invalid {
t.Run("invalid "+pi, func(t *testing.T) {
raw := copyMap(baseConfig())
raw["pip_installer"] = pi
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid pip_installer")
})
}
}
func TestValidateConfigSource(t *testing.T) {
t.Run("valid uv source with root", func(t *testing.T) {
raw := map[string]any{
"python_version": "3.12",
"graphs": map[string]any{"agent": "./agent.py:graph"},
"source": map[string]any{"kind": "uv", "root": "../.."},
}
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("invalid source kind poetry", func(t *testing.T) {
raw := map[string]any{
"python_version": "3.12",
"graphs": map[string]any{"agent": "./agent.py:graph"},
"source": map[string]any{"kind": "poetry"},
}
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid source.kind")
})
t.Run("source as string not object", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["source"] = "not-an-object"
_, err := ValidateConfig(raw)
mustContain(t, err, "`source` must be an object")
})
t.Run("uv source with dependencies", func(t *testing.T) {
raw := map[string]any{
"python_version": "3.12",
"graphs": map[string]any{"agent": "./agent.py:graph"},
"source": map[string]any{"kind": "uv", "root": ".."},
"dependencies": []any{"langchain"},
}
_, err := ValidateConfig(raw)
mustContain(t, err, "Remove `dependencies`")
})
t.Run("uv source with root as number", func(t *testing.T) {
raw := map[string]any{
"python_version": "3.12",
"graphs": map[string]any{"agent": "./agent.py:graph"},
"source": map[string]any{"kind": "uv", "root": 123},
}
_, err := ValidateConfig(raw)
mustContain(t, err, "source.root` must be a string")
})
t.Run("uv source with package as number", func(t *testing.T) {
raw := map[string]any{
"python_version": "3.12",
"graphs": map[string]any{"agent": "./agent.py:graph"},
"source": map[string]any{"kind": "uv", "root": "..", "package": 123},
}
_, err := ValidateConfig(raw)
mustContain(t, err, "source.package` must be a non-empty string")
})
}
func TestValidateConfigAPIVersion(t *testing.T) {
t.Run("valid 0.8", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["api_version"] = "0.8"
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("valid 0.8.1", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["api_version"] = "0.8.1"
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("invalid abc", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["api_version"] = "abc"
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid version format")
})
t.Run("invalid 1.2.3.4 too many parts", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["api_version"] = "1.2.3.4"
_, err := ValidateConfig(raw)
mustContain(t, err, "major or major.minor")
})
}
func TestValidateConfigMutualExclusion(t *testing.T) {
t.Run("both _INTERNAL_docker_tag and api_version", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["_INTERNAL_docker_tag"] = "some-tag"
raw["api_version"] = "0.8"
_, err := ValidateConfig(raw)
mustContain(t, err, "Cannot specify both")
})
}
func TestValidateConfigAuthPath(t *testing.T) {
t.Run("valid auth path with colon", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["auth"] = map[string]any{"path": "./auth.py:handler"}
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("invalid auth path without colon", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["auth"] = map[string]any{"path": "../../examples/my_app.py"}
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid auth.path format")
})
}
func TestValidateConfigEncryptionPath(t *testing.T) {
t.Run("valid encryption path with colon", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["encryption"] = map[string]any{"path": "./enc.py:enc"}
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("invalid encryption path without colon", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["encryption"] = map[string]any{"path": "./enc.py"}
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid encryption.path format")
})
}
func TestValidateConfigHTTPApp(t *testing.T) {
t.Run("valid http app with colon", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["http"] = map[string]any{"app": "./app.py:app"}
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("invalid http app without colon", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["http"] = map[string]any{"app": "./app.py"}
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid http.app format")
})
}
func TestValidateConfigKeepPkgTools(t *testing.T) {
t.Run("bool true", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["keep_pkg_tools"] = true
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("valid list", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["keep_pkg_tools"] = []any{"pip", "wheel"}
_, err := ValidateConfig(raw)
mustSucceed(t, err)
})
t.Run("invalid list item", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["keep_pkg_tools"] = []any{"invalid"}
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid keep_pkg_tools")
})
t.Run("invalid string type", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["keep_pkg_tools"] = "string"
_, err := ValidateConfig(raw)
mustContain(t, err, "Invalid keep_pkg_tools")
})
}
func TestValidateConfigLegacyKeys(t *testing.T) {
t.Run("project_root legacy", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["project_root"] = ".."
_, err := ValidateConfig(raw)
mustContain(t, err, "no longer supported")
})
t.Run("package legacy", func(t *testing.T) {
raw := copyMap(baseConfig())
raw["package"] = "foo"
_, err := ValidateConfig(raw)
mustContain(t, err, "no longer supported")
})
}
func TestValidateConfigNodeGraphDetection(t *testing.T) {
t.Run("ts extension auto-sets node_version", func(t *testing.T) {
raw := map[string]any{
"dependencies": []any{"langchain"},
"graphs": map[string]any{"agent": "./agent.py:graph", "bot": "./bot.ts:bot"},
}
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if nv, _ := result["node_version"].(string); nv != "20" {
t.Fatalf("expected node_version '20', got %q", nv)
}
})
t.Run("js extension auto-sets node_version", func(t *testing.T) {
raw := map[string]any{
"dependencies": []any{"langchain"},
"graphs": map[string]any{"agent": "./agent.py:graph", "bot": "./bot.js:bot"},
}
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if nv, _ := result["node_version"].(string); nv != "20" {
t.Fatalf("expected node_version '20', got %q", nv)
}
})
t.Run("py extension does not set node_version", func(t *testing.T) {
raw := baseConfig()
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if nv, _ := result["node_version"].(string); nv != "" {
t.Fatalf("expected node_version '', got %q", nv)
}
})
}
func TestGetUnknownKeys(t *testing.T) {
t.Run("typo suggests correction", func(t *testing.T) {
raw := map[string]any{
"grpahs": map[string]any{"agent": "./agent.py:graph"},
"dependencies": []any{"langchain"},
}
warnings := GetUnknownKeys(raw)
found := false
for _, w := range warnings {
if strings.Contains(w, "did you mean 'graphs'") {
found = true
break
}
}
if !found {
t.Fatalf("expected warning suggesting 'graphs', got: %v", warnings)
}
})
t.Run("totally unknown key", func(t *testing.T) {
raw := map[string]any{
"totally_unknown": "value",
"graphs": map[string]any{"agent": "./agent.py:graph"},
"dependencies": []any{"langchain"},
}
warnings := GetUnknownKeys(raw)
found := false
for _, w := range warnings {
if strings.Contains(w, "not a recognized config field") {
found = true
break
}
}
if !found {
t.Fatalf("expected 'not a recognized config field' warning, got: %v", warnings)
}
})
t.Run("only known keys gives no warnings", func(t *testing.T) {
raw := baseConfig()
warnings := GetUnknownKeys(raw)
if len(warnings) != 0 {
t.Fatalf("expected no warnings, got: %v", warnings)
}
})
}
func TestValidateConfigMultiplatform(t *testing.T) {
t.Run("only JS graphs", func(t *testing.T) {
raw := map[string]any{
"graphs": map[string]any{"bot": "./bot.ts:bot"},
}
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if nv, _ := result["node_version"].(string); nv != "20" {
t.Fatalf("expected node_version '20', got %q", nv)
}
if pv, _ := result["python_version"].(string); pv != "" {
t.Fatalf("expected python_version '', got %q", pv)
}
})
t.Run("only Python graphs", func(t *testing.T) {
raw := baseConfig()
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if pv, _ := result["python_version"].(string); pv != "3.11" {
t.Fatalf("expected python_version '3.11', got %q", pv)
}
if nv, _ := result["node_version"].(string); nv != "" {
t.Fatalf("expected node_version '', got %q", nv)
}
})
t.Run("mixed graphs", func(t *testing.T) {
raw := map[string]any{
"dependencies": []any{"langchain"},
"graphs": map[string]any{"agent": "./agent.py:graph", "bot": "./bot.ts:bot"},
}
result, err := ValidateConfig(raw)
mustSucceed(t, err)
if pv, _ := result["python_version"].(string); pv != "3.11" {
t.Fatalf("expected python_version '3.11', got %q", pv)
}
if nv, _ := result["node_version"].(string); nv != "20" {
t.Fatalf("expected node_version '20', got %q", nv)
}
})
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-804
View File
@@ -1,804 +0,0 @@
package config
import (
"os"
"path/filepath"
"strings"
"testing"
)
// ---------------------------------------------------------------------------
// Helper: write a uv-lock workspace fixture
// ---------------------------------------------------------------------------
type workspaceOpts struct {
// Relative directory (within project_root) that holds langgraph.json.
// Default: "deploy/agent"
configRelativeDir string
// Extra TOML appended to the root pyproject.toml (e.g. [tool.uv.sources]).
rootSources string
// Extra TOML appended to the agent pyproject.toml (e.g. [tool.uv.sources]).
agentSources string
// Extra TOML appended to the shared lib pyproject.toml (e.g. [tool.uv] section).
sharedUvConfig string
// Custom agent dependencies list. When nil the default is used.
agentDependencies []string
// Additional files to create: relative-path (from project_root) -> content.
extraFiles map[string]string
}
// writeUvLockWorkspace creates the standard multi-package uv workspace used
// by the Python test_config_to_docker_uv_lock* test suite and returns
// (projectRoot, configPath).
//
// Layout:
//
// workspace/
// pyproject.toml [project] name="workspace-root" + [tool.uv.workspace]
// uv.lock
// apps/agent/ package "agent"
// pyproject.toml
// src/agent/graph.py
// libs/shared/ package "shared"
// pyproject.toml
// src/shared/auth.py
// libs/extra/ package "extra" (not a dep of agent)
// pyproject.toml
// src/extra/graph.py
// deploy/agent/ config directory (configRelativeDir)
// langgraph.json
func writeUvLockWorkspace(t *testing.T, opts workspaceOpts) (string, string) {
t.Helper()
base := t.TempDir()
projectRoot := filepath.Join(base, "workspace")
configRelDir := opts.configRelativeDir
if configRelDir == "" {
configRelDir = "deploy/agent"
}
configDir := filepath.Join(projectRoot, configRelDir)
sharedDir := filepath.Join(projectRoot, "libs", "shared")
extraDir := filepath.Join(projectRoot, "libs", "extra")
deployDir := filepath.Join(projectRoot, "deploy", "agent")
for _, d := range []string{configDir, sharedDir, extraDir, deployDir} {
if err := os.MkdirAll(d, 0o755); err != nil {
t.Fatalf("MkdirAll(%q): %v", d, err)
}
}
// -- root pyproject.toml -------------------------------------------------
rootSources := opts.rootSources
writeFile(t, filepath.Join(projectRoot, "pyproject.toml"),
"[project]\n"+
"name = \"workspace-root\"\n"+
"version = \"0.1.0\"\n"+
"\n"+
"[tool.uv.workspace]\n"+
"members = [\"apps/*\", \"libs/*\"]\n"+
"\n"+
rootSources+"\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
// -- uv.lock -------------------------------------------------------------
writeFile(t, filepath.Join(projectRoot, "uv.lock"), "# uv lock file\n")
// -- agent pyproject.toml ------------------------------------------------
agentDir := filepath.Join(projectRoot, "apps", "agent")
if err := os.MkdirAll(agentDir, 0o755); err != nil {
t.Fatalf("MkdirAll: %v", err)
}
agentDeps := opts.agentDependencies
if agentDeps == nil {
agentDeps = []string{"shared", "httpx>=0.28"}
}
depsList := "[\"" + strings.Join(agentDeps, "\", \"") + "\"]"
agentSources := opts.agentSources
writeFile(t, filepath.Join(agentDir, "pyproject.toml"),
"[project]\n"+
"name = \"agent\"\n"+
"version = \"0.1.0\"\n"+
"dependencies = "+depsList+"\n"+
"\n"+
agentSources+"\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
// -- shared pyproject.toml -----------------------------------------------
sharedUvConfig := opts.sharedUvConfig
writeFile(t, filepath.Join(sharedDir, "pyproject.toml"),
"[project]\n"+
"name = \"shared\"\n"+
"version = \"0.1.0\"\n"+
"dependencies = [\"anyio>=4\"]\n"+
"\n"+
sharedUvConfig+"\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
// -- extra pyproject.toml ------------------------------------------------
writeFile(t, filepath.Join(extraDir, "pyproject.toml"),
"[project]\n"+
"name = \"extra\"\n"+
"version = \"0.1.0\"\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
// -- source files --------------------------------------------------------
writeFile(t, filepath.Join(agentDir, "src", "agent", "graph.py"), "")
writeFile(t, filepath.Join(sharedDir, "src", "shared", "auth.py"), "")
writeFile(t, filepath.Join(extraDir, "src", "extra", "graph.py"), "")
// -- config file ---------------------------------------------------------
configPath := filepath.Join(deployDir, "langgraph.json")
writeFile(t, configPath, "{}\n")
// If configRelativeDir is non-default, also place langgraph.json there.
if configRelDir != "deploy/agent" {
altConfigPath := filepath.Join(configDir, "langgraph.json")
writeFile(t, altConfigPath, "{}\n")
configPath = altConfigPath
}
// -- extra files ---------------------------------------------------------
for relPath, content := range opts.extraFiles {
writeFile(t, filepath.Join(projectRoot, relPath), content)
}
return projectRoot, configPath
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
func TestUvLockBasic(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentSources: "[tool.uv.sources]\nshared = { workspace = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"auth": map[string]any{"path": "../../libs/shared/src/shared/auth.py:create_auth"},
})
docker, contexts, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
// Core commands.
assertContains(t, docker, "uv pip install --system")
assertContains(t, docker,
"uv export --package 'agent' --frozen --no-hashes --no-emit-project --no-emit-workspace")
// Copy project metadata for uv export.
assertContains(t, docker,
"COPY --from=uv-workspace-root pyproject.toml /tmp/uv_export/project/pyproject.toml")
assertContains(t, docker,
"COPY --from=uv-workspace-root uv.lock /tmp/uv_export/project/uv.lock")
// Additional build contexts.
if _, ok := contexts["uv-workspace-root"]; !ok {
t.Fatal("expected 'uv-workspace-root' in additional contexts")
}
// Workspace packages copied.
assertContains(t, docker,
"COPY --from=uv-workspace-root apps/agent /deps/workspace/apps/agent")
assertContains(t, docker,
"COPY --from=uv-workspace-root libs/shared /deps/workspace/libs/shared")
// Unrelated member NOT copied.
assertNotContains(t, docker,
"libs/extra /deps/workspace/libs/extra")
// Install order: shared before agent.
assertContains(t, docker, "WORKDIR /deps/workspace/libs/shared")
assertContains(t, docker, "WORKDIR /deps/workspace/apps/agent")
sharedInstall := "uv pip install --system --no-cache-dir -c /api/constraints.txt --no-deps -e ."
assertContains(t, docker, sharedInstall)
// Ordering: shared COPY < shared WORKDIR < agent COPY < agent WORKDIR.
sharedCopy := "COPY --from=uv-workspace-root libs/shared /deps/workspace/libs/shared"
sharedWD := "WORKDIR /deps/workspace/libs/shared"
agentCopy := "COPY --from=uv-workspace-root apps/agent /deps/workspace/apps/agent"
agentWD := "WORKDIR /deps/workspace/apps/agent"
if strings.Index(docker, sharedCopy) >= strings.Index(docker, sharedWD) {
t.Error("shared COPY should appear before shared WORKDIR")
}
if strings.Index(docker, sharedWD) >= strings.Index(docker, agentCopy) {
t.Error("shared WORKDIR should appear before agent COPY")
}
if strings.Index(docker, sharedWD) >= strings.Index(docker, agentWD) {
t.Error("shared WORKDIR should appear before agent WORKDIR")
}
// No legacy dep-loop patterns.
assertNotContains(t, docker, "for dep in /deps/*")
assertNotContains(t, docker, "# -- Installing workspace packages --")
assertContains(t, docker, "WORKDIR /tmp/uv_export/project")
assertNotContains(t, docker, "RUN cd ")
// Rewritten paths in env vars (Go JSON uses compact format without spaces).
assertContains(t, docker,
`"/deps/workspace/libs/shared/src/shared/auth.py:create_auth"`)
assertContains(t, docker,
`"/deps/workspace/apps/agent/src/agent/graph.py:graph"`)
// Cleanup.
assertContains(t, docker, "rm /usr/bin/uv /usr/bin/uvx")
}
func TestUvLockHonorsRootWorkspaceSources(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
rootSources: "[tool.uv.sources]\nshared = { workspace = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"auth": map[string]any{"path": "../../libs/shared/src/shared/auth.py:create_auth"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker,
"COPY --from=uv-workspace-root libs/shared /deps/workspace/libs/shared")
assertContains(t, docker,
`"/deps/workspace/libs/shared/src/shared/auth.py:create_auth"`)
}
func TestUvLockIgnoresUnrelatedWorkspacePackageSources(t *testing.T) {
projectRoot, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentSources: "[tool.uv.sources]\nshared = { workspace = true }",
})
// Add badlib with [tool.uv.sources] pointing outside (an unrelated member).
badlibDir := filepath.Join(projectRoot, "libs", "badlib")
writeFile(t, filepath.Join(badlibDir, "pyproject.toml"),
"[project]\n"+
"name = \"badlib\"\n"+
"version = \"0.1.0\"\n"+
"\n"+
"[tool.uv.sources]\n"+
"outside = { path = \"../outside\" }\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"auth": map[string]any{"path": "../../libs/shared/src/shared/auth.py:create_auth"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker,
"COPY --from=uv-workspace-root libs/shared /deps/workspace/libs/shared")
assertNotContains(t, docker,
"COPY --from=uv-workspace-root libs/badlib /deps/workspace/libs/badlib")
}
func TestUvLockIgnoresUnrelatedRootSources(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
rootSources: "[tool.uv.sources]\nshared = { workspace = true }\nunused = { path = \"libs/extra\", editable = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"auth": map[string]any{"path": "../../libs/shared/src/shared/auth.py:create_auth"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker,
"COPY --from=uv-workspace-root libs/shared /deps/workspace/libs/shared")
assertNotContains(t, docker,
"COPY --from=uv-workspace-root libs/extra /deps/workspace/libs/extra")
}
func TestUvLockValidatesRootPathSourcesRelativeToProjectRoot(t *testing.T) {
projectRoot, configPath := writeUvLockWorkspace(t, workspaceOpts{
rootSources: "[tool.uv.sources]\nshared = { path = \"../outside\", editable = true }",
})
// Create the outside directory the source points to.
outsideDir := filepath.Join(filepath.Dir(projectRoot), "outside")
writeFile(t, filepath.Join(outsideDir, "pyproject.toml"),
"[project]\n"+
"name = \"shared\"\n"+
"version = \"0.1.0\"\n")
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for path outside project_root")
}
assertContains(t, err.Error(), "outside project_root")
}
func TestUvLockRequiresExplicitWorkspaceSources(t *testing.T) {
// No agent_sources or root_sources => shared is NOT a workspace dep.
_, configPath := writeUvLockWorkspace(t, workspaceOpts{})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"auth": map[string]any{"path": "../../libs/shared/src/shared/auth.py:create_auth"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error because shared is not an explicit workspace source")
}
assertContains(t, err.Error(), "not inside the target package 'agent'")
}
func TestUvLockAcceptsPathWorkspaceSources(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentSources: "[tool.uv.sources]\nshared = { path = \"../../libs/shared\", editable = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker,
"COPY --from=uv-workspace-root libs/shared /deps/workspace/libs/shared")
}
func TestUvLockRejectsPackageFalseWorkspaceDependency(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentSources: "[tool.uv.sources]\nshared = { workspace = true }",
sharedUvConfig: "[tool.uv]\npackage = false",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for package = false workspace dependency")
}
assertContains(t, err.Error(), "tool.uv.package = false")
}
func TestUvLockAcceptsRootPathWorkspaceSources(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
rootSources: "[tool.uv.sources]\nshared = { path = \"libs/shared\", editable = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"auth": map[string]any{"path": "../../libs/shared/src/shared/auth.py:create_auth"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker,
"COPY --from=uv-workspace-root libs/shared /deps/workspace/libs/shared")
assertContains(t, docker,
`"/deps/workspace/libs/shared/src/shared/auth.py:create_auth"`)
}
func TestUvLockRejectsMismatchedPathWorkspaceSources(t *testing.T) {
// agent sources point to libs/extra with the name "shared" -> mismatch.
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentSources: "[tool.uv.sources]\nshared = { path = \"../../libs/extra\", editable = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for mismatched path workspace sources")
}
assertContains(t, err.Error(), "dependency name and the workspace package name must match")
}
func TestUvLockDetectsJsPmFromTargetPackageRoot(t *testing.T) {
projectRoot, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentSources: "[tool.uv.sources]\nshared = { workspace = true }",
})
agentDir := filepath.Join(projectRoot, "apps", "agent")
writeFile(t, filepath.Join(agentDir, "package.json"), "{\"packageManager\":\"pnpm@9.0.0\"}\n")
writeFile(t, filepath.Join(agentDir, "pnpm-lock.yaml"), "lockfileVersion: 9.0\n")
writeFile(t, filepath.Join(agentDir, "ui.tsx"), "export const ui = null;\n")
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"ui": map[string]any{"agent": "../../apps/agent/ui.tsx"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker, "WORKDIR /deps/workspace/apps/agent")
assertContains(t, docker,
"RUN pnpm i --frozen-lockfile && tsx /api/langgraph_api/js/build.mts")
assertContains(t, docker, `/deps/workspace/apps/agent/ui.tsx`)
}
func TestUvLockUsesWorkdirForJsInstallWithSpecialChars(t *testing.T) {
projectRoot, _ := writeUvLockWorkspace(t, workspaceOpts{
configRelativeDir: "apps/agent;echo pwned",
})
// The custom configRelativeDir creates its own langgraph.json.
// We need to place the agent pyproject.toml at this directory.
agentDir := filepath.Join(projectRoot, "apps", "agent;echo pwned")
writeFile(t, filepath.Join(agentDir, "package.json"), "{\"packageManager\":\"pnpm@9.0.0\"}\n")
writeFile(t, filepath.Join(agentDir, "pnpm-lock.yaml"), "lockfileVersion: 9.0\n")
writeFile(t, filepath.Join(agentDir, "ui.tsx"), "export const ui = null;\n")
writeFile(t, filepath.Join(agentDir, "pyproject.toml"),
"[project]\n"+
"name = \"agent\"\n"+
"version = \"0.1.0\"\n"+
"dependencies = [\"shared\", \"httpx>=0.28\"]\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
writeFile(t, filepath.Join(agentDir, "src", "agent", "graph.py"), "")
// Remove the default apps/agent so there's no duplicate package name.
if err := os.RemoveAll(filepath.Join(projectRoot, "apps", "agent")); err != nil {
t.Fatal(err)
}
// Update workspace members to include the special-char directory.
writeFile(t, filepath.Join(projectRoot, "pyproject.toml"),
"[project]\n"+
"name = \"workspace-root\"\n"+
"version = \"0.1.0\"\n"+
"\n"+
"[tool.uv.workspace]\n"+
"members = [\"apps/*\", \"libs/*\"]\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
configPath := filepath.Join(agentDir, "langgraph.json")
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "./src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
"ui": map[string]any{"agent": "./ui.tsx"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker, "WORKDIR /deps/workspace/apps/agent;echo pwned")
assertContains(t, docker,
"RUN pnpm i --frozen-lockfile && tsx /api/langgraph_api/js/build.mts")
assertNotContains(t, docker, "RUN cd /deps/workspace/apps/agent;echo pwned")
}
func TestUvLockSupportsSingleUvProjectRoot(t *testing.T) {
base := t.TempDir()
projectRoot := filepath.Join(base, "single")
if err := os.MkdirAll(projectRoot, 0o755); err != nil {
t.Fatal(err)
}
writeFile(t, filepath.Join(projectRoot, "uv.lock"), "# uv lock file\n")
writeFile(t, filepath.Join(projectRoot, "pyproject.toml"),
"[project]\n"+
"name = \"single-app\"\n"+
"version = \"0.1.0\"\n"+
"dependencies = [\"httpx>=0.28\"]\n"+
"\n"+
"[build-system]\n"+
"requires = [\"setuptools>=61\"]\n"+
"build-backend = \"setuptools.build_meta\"\n")
configPath := filepath.Join(projectRoot, "langgraph.json")
writeFile(t, configPath, "{}\n")
writeFile(t, filepath.Join(projectRoot, "src", "agent.py"), "graph = object()\n")
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{"agent": "./src/agent.py:graph"},
"source": map[string]any{"kind": "uv"},
})
docker, contexts, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
assertContains(t, docker,
"uv export --package 'single-app' --frozen --no-hashes --no-emit-project --no-emit-workspace")
assertContains(t, docker, `"/deps/workspace/src/agent.py:graph"`)
if len(contexts) != 0 {
t.Fatalf("expected no additional contexts for single-project root, got %d: %v",
len(contexts), contexts)
}
}
func TestUvLockRejectsInvalidSourcePackageType(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
// Override the validated source.package to an integer.
cfg["source"].(map[string]any)["package"] = 123
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for non-string source.package")
}
assertContains(t, err.Error(), "`source.package` must be a non-empty string")
}
func TestUvLockRejectsPathsOutsideTargetClosure(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentSources: "[tool.uv.sources]\nshared = { workspace = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
// Graph path points into libs/extra which is NOT a dependency of agent.
"agent": "../../libs/extra/src/extra/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for graph path outside target closure")
}
assertContains(t, err.Error(), "not inside the target package 'agent'")
}
func TestUvLockRejectsUnrelatedMemberWhenRootInClosure(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentDependencies: []string{"workspace-root", "shared", "httpx>=0.28"},
rootSources: "[tool.uv.sources]\nshared = { workspace = true }\nworkspace-root = { workspace = true }",
agentSources: "[tool.uv.sources]\nshared = { workspace = true }\nworkspace-root = { workspace = true }",
})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
// extra is a workspace member but NOT a dependency of agent.
"agent": "../../libs/extra/src/extra/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for unrelated member when root is in closure")
}
assertContains(t, err.Error(), "not inside the target package 'agent'")
}
func TestUvLockRootPackageCopySkipsUnrelatedMembers(t *testing.T) {
projectRoot, configPath := writeUvLockWorkspace(t, workspaceOpts{
agentDependencies: []string{"workspace-root", "shared", "httpx>=0.28"},
rootSources: "[tool.uv.sources]\nshared = { workspace = true }\nworkspace-root = { workspace = true }",
agentSources: "[tool.uv.sources]\nshared = { workspace = true }\nworkspace-root = { workspace = true }",
})
// Add files in the workspace root package itself.
writeFile(t, filepath.Join(projectRoot, "src", "workspace_root", "__init__.py"), "__all__ = []\n")
writeFile(t, filepath.Join(projectRoot, "README.md"), "workspace root package\n")
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
docker, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err != nil {
t.Fatalf("ConfigToDocker: %v", err)
}
// Should NOT do a blanket COPY of the whole workspace root.
assertNotContains(t, docker, "COPY --from=uv-workspace-root . /deps/workspace")
// Should copy specific entries.
assertContains(t, docker, "COPY --from=uv-workspace-root src /deps/workspace/src")
assertContains(t, docker, "COPY --from=uv-workspace-root README.md /deps/workspace/README.md")
// Should NOT copy unrelated member dirs.
assertNotContains(t, docker,
"COPY --from=uv-workspace-root libs/extra /deps/workspace/libs/extra")
// Should install workspace root from /deps/workspace.
assertContains(t, docker, "WORKDIR /deps/workspace")
assertContains(t, docker,
"uv pip install --system --no-cache-dir -c /api/constraints.txt --no-deps -e .")
}
func TestUvLockMissingLockfile(t *testing.T) {
projectRoot, configPath := writeUvLockWorkspace(t, workspaceOpts{})
// Remove uv.lock.
if err := os.Remove(filepath.Join(projectRoot, "uv.lock")); err != nil {
t.Fatal(err)
}
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{"agent": "../../apps/agent/src/agent/graph.py:graph"},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for missing uv.lock")
}
assertContains(t, err.Error(), "No uv.lock found")
}
func TestUvLockMissingPyproject(t *testing.T) {
projectRoot, configPath := writeUvLockWorkspace(t, workspaceOpts{})
// Remove root pyproject.toml.
if err := os.Remove(filepath.Join(projectRoot, "pyproject.toml")); err != nil {
t.Fatal(err)
}
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{"agent": "../../apps/agent/src/agent/graph.py:graph"},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.47",
})
if err == nil {
t.Fatal("expected error for missing pyproject.toml")
}
assertContains(t, err.Error(), "No pyproject.toml found")
}
func TestUvLockOldImage(t *testing.T) {
_, configPath := writeUvLockWorkspace(t, workspaceOpts{})
cfg := mustValidate(t, map[string]any{
"python_version": "3.11",
"graphs": map[string]any{"agent": "../../apps/agent/src/agent/graph.py:graph"},
"source": map[string]any{"kind": "uv", "root": "../..", "package": "agent"},
})
_, _, err := ConfigToDocker(configPath, cfg, DockerOpts{
BaseImage: "langchain/langgraph-api:0.2.46",
})
if err == nil {
t.Fatal("expected error for old image without uv support")
}
assertContains(t, err.Error(), "requires a base image with uv support")
}
-272
View File
@@ -1,272 +0,0 @@
// Package deploy provides an HTTP client for the LangGraph host backend
// deployment service.
package deploy
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
// Secret represents a name/value pair sent as a deployment secret.
type Secret struct {
Name string `json:"name"`
Value string `json:"value"`
}
// HostBackendClient is a minimal JSON HTTP client for the host backend
// deployment service.
type HostBackendClient struct {
BaseURL string
APIKey string
TenantID string
client *http.Client
}
// retryTransport wraps an http.RoundTripper and retries failed requests.
type retryTransport struct {
base http.RoundTripper
retries int
}
func (t *retryTransport) RoundTrip(req *http.Request) (*http.Response, error) {
var resp *http.Response
var err error
// We need to buffer the body so we can replay it on retries.
var bodyBytes []byte
if req.Body != nil {
bodyBytes, err = io.ReadAll(req.Body)
if err != nil {
return nil, err
}
req.Body.Close()
}
for attempt := 0; attempt <= t.retries; attempt++ {
if bodyBytes != nil {
req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
}
resp, err = t.base.RoundTrip(req)
if err == nil {
return resp, nil
}
// Only retry on transport-level errors; do not retry on HTTP error
// status codes (the caller handles those).
}
return resp, err
}
// NewClient creates a new HostBackendClient. The baseURL is stripped of any
// trailing slash. The underlying http.Client uses a 30-second timeout and
// retries transport-level failures up to 3 times.
func NewClient(baseURL, apiKey string) *HostBackendClient {
return &HostBackendClient{
BaseURL: strings.TrimRight(baseURL, "/"),
APIKey: apiKey,
TenantID: "",
client: &http.Client{
Timeout: 30 * time.Second,
Transport: &retryTransport{
base: http.DefaultTransport,
retries: 3,
},
},
}
}
// request executes an HTTP request against the host backend and returns the
// parsed JSON response. It attaches required headers and handles errors.
func (c *HostBackendClient) request(method, path string, payload map[string]any, params map[string]string) (map[string]any, error) {
fullURL := c.BaseURL + path
// Append query parameters.
if len(params) > 0 {
q := url.Values{}
for k, v := range params {
q.Set(k, v)
}
fullURL += "?" + q.Encode()
}
var body io.Reader
if payload != nil {
data, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("marshalling request payload: %w", err)
}
body = bytes.NewReader(data)
}
req, err := http.NewRequest(method, fullURL, body)
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
req.Header.Set("X-Api-Key", c.APIKey)
req.Header.Set("Accept", "application/json")
if payload != nil {
req.Header.Set("Content-Type", "application/json")
}
if c.TenantID != "" {
req.Header.Set("X-Tenant-ID", c.TenantID)
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("%s %s: %w", method, path, err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading response from %s: %w", path, err)
}
if resp.StatusCode >= 400 {
detail := string(respBody)
if detail == "" {
detail = fmt.Sprintf("%d", resp.StatusCode)
}
return nil, fmt.Errorf("%s %s failed with status %d: %s", method, path, resp.StatusCode, detail)
}
if len(respBody) == 0 {
return nil, nil
}
var result map[string]any
if err := json.Unmarshal(respBody, &result); err != nil {
return nil, fmt.Errorf("failed to decode response from %s: %w", path, err)
}
return result, nil
}
// requestNoBody is a convenience wrapper for requests that return no parsed body.
func (c *HostBackendClient) requestNoBody(method, path string, payload map[string]any, params map[string]string) error {
_, err := c.request(method, path, payload, params)
return err
}
// CreateDeployment creates a new deployment.
func (c *HostBackendClient) CreateDeployment(name, deploymentType, source string, configPath string, secrets []Secret) (map[string]any, error) {
sourceRevisionConfig := map[string]any{}
if source == "internal_source" && configPath != "" {
sourceRevisionConfig["langgraph_config_path"] = configPath
}
payload := map[string]any{
"name": name,
"source": source,
"source_config": map[string]any{"deployment_type": deploymentType},
"source_revision_config": sourceRevisionConfig,
}
if secrets != nil {
payload["secrets"] = secrets
}
return c.request("POST", "/v2/deployments", payload, nil)
}
// ListDeployments lists deployments, optionally filtering by name.
func (c *HostBackendClient) ListDeployments(nameContains string) (map[string]any, error) {
params := map[string]string{"name_contains": nameContains}
return c.request("GET", "/v2/deployments", nil, params)
}
// GetDeployment retrieves a single deployment by ID.
func (c *HostBackendClient) GetDeployment(deploymentID string) (map[string]any, error) {
return c.request("GET", fmt.Sprintf("/v2/deployments/%s", deploymentID), nil, nil)
}
// DeleteDeployment deletes a deployment by ID.
func (c *HostBackendClient) DeleteDeployment(deploymentID string) error {
return c.requestNoBody("DELETE", fmt.Sprintf("/v2/deployments/%s", deploymentID), nil, nil)
}
// RequestPushToken requests a push token for a deployment.
func (c *HostBackendClient) RequestPushToken(deploymentID string) (map[string]any, error) {
return c.request("POST", fmt.Sprintf("/v2/deployments/%s/push-token", deploymentID), nil, nil)
}
// RequestUploadURL gets a signed URL for uploading the source tarball.
func (c *HostBackendClient) RequestUploadURL(deploymentID string) (map[string]any, error) {
return c.request("POST", fmt.Sprintf("/v2/deployments/%s/upload-url", deploymentID), nil, nil)
}
// UpdateDeployment triggers a new revision using a pre-pushed Docker image.
func (c *HostBackendClient) UpdateDeployment(deploymentID, imageURI string, secrets []Secret) (map[string]any, error) {
payload := map[string]any{
"revision_source": "internal_docker",
"source_revision_config": map[string]any{"image_uri": imageURI},
}
if secrets != nil {
payload["secrets"] = secrets
}
return c.request("PATCH", fmt.Sprintf("/v2/deployments/%s", deploymentID), payload, nil)
}
// UpdateDeploymentInternalSource triggers a remote-build revision using an
// uploaded source tarball.
func (c *HostBackendClient) UpdateDeploymentInternalSource(
deploymentID, sourceTarballPath, configPath string,
secrets []Secret,
installCommand, buildCommand string,
) (map[string]any, error) {
payload := map[string]any{
"revision_source": "internal_source",
"source_revision_config": map[string]any{
"source_tarball_path": sourceTarballPath,
"langgraph_config_path": configPath,
},
}
sourceConfig := map[string]any{}
if installCommand != "" {
sourceConfig["install_command"] = installCommand
}
if buildCommand != "" {
sourceConfig["build_command"] = buildCommand
}
if len(sourceConfig) > 0 {
payload["source_config"] = sourceConfig
}
if secrets != nil {
payload["secrets"] = secrets
}
return c.request("PATCH", fmt.Sprintf("/v2/deployments/%s", deploymentID), payload, nil)
}
// ListRevisions lists revisions for a deployment.
func (c *HostBackendClient) ListRevisions(deploymentID string, limit int) (map[string]any, error) {
return c.request("GET", fmt.Sprintf("/v2/deployments/%s/revisions", deploymentID), nil, map[string]string{
"limit": fmt.Sprintf("%d", limit),
})
}
// GetRevision retrieves a single revision.
func (c *HostBackendClient) GetRevision(deploymentID, revisionID string) (map[string]any, error) {
return c.request("GET", fmt.Sprintf("/v2/deployments/%s/revisions/%s", deploymentID, revisionID), nil, nil)
}
// GetBuildLogs retrieves build logs for a revision.
func (c *HostBackendClient) GetBuildLogs(projectID, revisionID string, payload map[string]any) (map[string]any, error) {
return c.request("POST", fmt.Sprintf("/v1/projects/%s/revisions/%s/build_logs", projectID, revisionID), payload, nil)
}
// GetDeployLogs retrieves deploy logs. If revisionID is non-empty, it is
// included in the path to scope the logs.
func (c *HostBackendClient) GetDeployLogs(projectID string, payload map[string]any, revisionID string) (map[string]any, error) {
var path string
if revisionID != "" {
path = fmt.Sprintf("/v1/projects/%s/revisions/%s/deploy_logs", projectID, revisionID)
} else {
path = fmt.Sprintf("/v1/projects/%s/deploy_logs", projectID)
}
return c.request("POST", path, payload, nil)
}
-306
View File
@@ -1,306 +0,0 @@
package deploy
import (
"bufio"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)
// APIKeyEnvNames lists the environment variable names checked (in order) when
// resolving a LangSmith / LangGraph API key.
var APIKeyEnvNames = []string{
"LANGGRAPH_HOST_API_KEY",
"LANGSMITH_API_KEY",
"LANGCHAIN_API_KEY",
}
// DefaultHostURL is the default host backend URL.
const DefaultHostURL = "https://api.host.langchain.com"
// reservedEnvVars contains environment variable names that must not be sent as
// deployment secrets. The set mirrors the Python CLI's RESERVED_ENV_VARS.
var reservedEnvVars = map[string]bool{
// LANGCHAIN_RESERVED_ENV_VARS from host-backend
"LANGCHAIN_TRACING_V2": true,
"LANGSMITH_TRACING_V2": true,
"LANGCHAIN_ENDPOINT": true,
"LANGCHAIN_PROJECT": true,
"LANGSMITH_PROJECT": true,
"LANGSMITH_LANGGRAPH_GIT_REPO": true,
"LANGGRAPH_GIT_REPO_PATH": true,
"LANGCHAIN_API_KEY": true,
"LANGSMITH_CONTROL_PLANE_API_KEY": true,
"POSTGRES_URI": true,
"POSTGRES_PASSWORD": true,
"DATABASE_URI": true,
"LANGSMITH_LANGGRAPH_GIT_REF": true,
"LANGSMITH_LANGGRAPH_GIT_REF_SHA": true,
"LANGGRAPH_AUTH_TYPE": true,
"LANGSMITH_AUTH_ENDPOINT": true,
"LANGSMITH_TENANT_ID": true,
"LANGSMITH_AUTH_VERIFY_TENANT_ID": true,
"LANGSMITH_HOST_PROJECT_ID": true,
"LANGSMITH_HOST_PROJECT_NAME": true,
"LANGSMITH_HOST_REVISION_ID": true,
"LOG_JSON": true,
"LOG_DICT_TRACEBACKS": true,
"REDIS_URI": true,
"LANGCHAIN_CALLBACKS_BACKGROUND": true,
"DD_TRACE_PSYCOPG_ENABLED": true,
"DD_TRACE_REDIS_ENABLED": true,
"LANGSMITH_DEPLOYMENT_NAME": true,
"LANGGRAPH_CLOUD_LICENSE_KEY": true,
// ALLOWED_SELF_HOSTED_ENV_VARS (rejected for non-self-hosted)
"LANGSMITH_API_KEY": true,
"LANGSMITH_ENDPOINT": true,
"POSTGRES_URI_CUSTOM": true,
"REDIS_URI_CUSTOM": true,
"PATH": true,
"PORT": true,
"MOUNT_PREFIX": true,
"LSD_ENV": true,
"LSD_DD_API_KEY": true,
"LSD_DD_ENDPOINT": true,
"LSD_DEPLOYMENT_TYPE": true,
}
var (
invalidImageNameChars = regexp.MustCompile(`[^a-z0-9._-]+`)
validImageTag = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`)
)
// NormalizeImageName sanitizes a deployment/directory name into a valid Docker
// repository name. Invalid characters are replaced with hyphens and the result
// is lowercased. Returns "app" if the result would be empty.
func NormalizeImageName(name string) string {
if name == "" {
return "app"
}
slug := invalidImageNameChars.ReplaceAllString(strings.ToLower(name), "-")
slug = strings.TrimLeft(slug, "-.")
slug = strings.TrimRight(slug, "-.")
if slug == "" {
return "app"
}
return slug
}
// NormalizeImageTag validates and returns a Docker image tag. Tags may only
// contain [A-Za-z0-9_.-]. Defaults to "latest" when empty.
func NormalizeImageTag(tag string) (string, error) {
if tag == "" {
return "latest", nil
}
if !validImageTag.MatchString(tag) {
return "", fmt.Errorf("image tag may only contain characters A-Z, a-z, 0-9, '_', '-', '.'")
}
return tag, nil
}
// ResolveAPIKey resolves an API key by checking (in order): the explicit flag
// value, the provided envVars map, and the process environment. Returns an
// empty string if no key is found (the caller should prompt the user).
func ResolveAPIKey(flagValue string, envVars map[string]string) string {
if flagValue != "" {
return flagValue
}
for _, keyName := range APIKeyEnvNames {
if envVars != nil {
if v, ok := envVars[keyName]; ok && v != "" {
return v
}
}
if v := os.Getenv(keyName); v != "" {
return v
}
}
return ""
}
// ParseEnvFromConfig resolves environment variables from the langgraph.json
// config. If the "env" field is a dict (map), those values are used directly.
// If it is a string, it is treated as a path to a .env file (resolved relative
// to the config file's directory). Otherwise, a .env file in the config
// directory is attempted as a fallback.
func ParseEnvFromConfig(configJSON map[string]any, configPath string) map[string]string {
envField, ok := configJSON["env"]
if !ok {
// Fallback: try .env in config dir.
return parseDotEnvFile(filepath.Join(filepath.Dir(configPath), ".env"))
}
// If env is a dict (map[string]any), convert to map[string]string.
if envMap, ok := envField.(map[string]any); ok && len(envMap) > 0 {
result := make(map[string]string, len(envMap))
for k, v := range envMap {
result[k] = fmt.Sprintf("%v", v)
}
return result
}
// If env is a string path, parse that .env file.
if envStr, ok := envField.(string); ok && envStr != "" {
envPath := filepath.Join(filepath.Dir(configPath), envStr)
absPath, err := filepath.Abs(envPath)
if err != nil {
return map[string]string{}
}
if _, err := os.Stat(absPath); os.IsNotExist(err) {
return map[string]string{}
}
return parseDotEnvFile(absPath)
}
// Fallback: try .env in config dir.
return parseDotEnvFile(filepath.Join(filepath.Dir(configPath), ".env"))
}
// parseDotEnvFile reads a .env file and returns its key-value pairs. Lines
// starting with # are treated as comments. Empty values are skipped.
func parseDotEnvFile(path string) map[string]string {
f, err := os.Open(path)
if err != nil {
return map[string]string{}
}
defer f.Close()
result := map[string]string{}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
idx := strings.IndexByte(line, '=')
if idx < 0 {
continue
}
key := strings.TrimSpace(line[:idx])
value := strings.TrimSpace(line[idx+1:])
// Strip surrounding quotes if present.
if len(value) >= 2 {
if (value[0] == '"' && value[len(value)-1] == '"') ||
(value[0] == '\'' && value[len(value)-1] == '\'') {
value = value[1 : len(value)-1]
}
}
if key != "" && value != "" {
result[key] = value
}
}
return result
}
// FindDeploymentIDByName lists deployments matching the given name and returns
// the ID of the first exact match. Returns an empty string (and no error) if
// no exact match is found.
func FindDeploymentIDByName(client *HostBackendClient, name string) (string, error) {
if name == "" {
return "", nil
}
existing, err := client.ListDeployments(name)
if err != nil {
return "", err
}
resources, ok := existing["resources"]
if !ok {
return "", nil
}
resourceList, ok := resources.([]any)
if !ok {
return "", nil
}
for _, item := range resourceList {
dep, ok := item.(map[string]any)
if !ok {
continue
}
depName, _ := dep["name"].(string)
if depName == name {
if id, ok := dep["id"]; ok {
return fmt.Sprintf("%v", id), nil
}
}
}
return "", nil
}
// ValidateDeploymentSelector ensures at least one of deploymentID or name is
// provided.
func ValidateDeploymentSelector(deploymentID, name string) error {
if deploymentID != "" {
return nil
}
if name == "" {
return fmt.Errorf("either --deployment-id or --name is required")
}
return nil
}
// ResolvedReservedEnvVars returns the set of reserved environment variable
// names that must not be sent as deployment secrets.
func ResolvedReservedEnvVars() map[string]bool {
// Return a copy to prevent callers from mutating the package-level map.
result := make(map[string]bool, len(reservedEnvVars))
for k, v := range reservedEnvVars {
result[k] = v
}
return result
}
// TerminalStatuses are deployment statuses that indicate completion.
var TerminalStatuses = map[string]bool{
"DEPLOYED": true,
"CREATE_FAILED": true,
"BUILD_FAILED": true,
"DEPLOY_FAILED": true,
"SKIPPED": true,
}
// PollDeploymentStatus polls a deployment until it reaches a terminal status or times out.
func PollDeploymentStatus(client *HostBackendClient, deploymentID string, timeoutSeconds, pollIntervalSeconds int, onStatus func(string)) (string, error) {
deadline := time.Now().Add(time.Duration(timeoutSeconds) * time.Second)
interval := time.Duration(pollIntervalSeconds) * time.Second
for time.Now().Before(deadline) {
resp, err := client.ListRevisions(deploymentID, 1)
if err != nil {
return "", err
}
revisions, _ := resp["revisions"].([]any)
if len(revisions) == 0 {
time.Sleep(interval)
continue
}
rev, _ := revisions[0].(map[string]any)
status, _ := rev["status"].(string)
if onStatus != nil {
onStatus(status)
}
if TerminalStatuses[status] {
return status, nil
}
time.Sleep(interval)
}
return "", fmt.Errorf("deployment timed out after %d seconds", timeoutSeconds)
}
// SecretsFromEnv converts an env var map into a Secret slice, filtering out
// reserved variable names and empty values.
func SecretsFromEnv(envVars map[string]string) []Secret {
var secrets []Secret
for name, value := range envVars {
if reservedEnvVars[name] {
continue
}
if value == "" {
continue
}
secrets = append(secrets, Secret{Name: name, Value: value})
}
return secrets
}
-154
View File
@@ -1,154 +0,0 @@
package deploy
import (
"os"
"sort"
"testing"
)
func TestNormalizeImageName(t *testing.T) {
tests := []struct {
input string
want string
}{
{"MyApp", "myapp"},
{"", "app"},
{"!!!", "app"},
{"my-app", "my-app"},
{"My Cool App", "my-cool-app"},
{"..leading-dots", "leading-dots"},
{"trailing-dots..", "trailing-dots"},
{"hello_world.v2", "hello_world.v2"},
}
for _, tc := range tests {
t.Run(tc.input, func(t *testing.T) {
got := NormalizeImageName(tc.input)
if got != tc.want {
t.Errorf("NormalizeImageName(%q) = %q, want %q", tc.input, got, tc.want)
}
})
}
}
func TestNormalizeImageTag(t *testing.T) {
tests := []struct {
input string
want string
wantErr bool
}{
{"v1.2.3", "v1.2.3", false},
{"", "latest", false},
{"has space", "", true},
{"valid_tag-1.0", "valid_tag-1.0", false},
{"tag/slash", "", true},
}
for _, tc := range tests {
t.Run(tc.input, func(t *testing.T) {
got, err := NormalizeImageTag(tc.input)
if tc.wantErr {
if err == nil {
t.Errorf("NormalizeImageTag(%q) expected error, got nil", tc.input)
}
return
}
if err != nil {
t.Errorf("NormalizeImageTag(%q) unexpected error: %v", tc.input, err)
return
}
if got != tc.want {
t.Errorf("NormalizeImageTag(%q) = %q, want %q", tc.input, got, tc.want)
}
})
}
}
func TestValidateDeploymentSelector(t *testing.T) {
tests := []struct {
name string
deploymentID string
depName string
wantErr bool
}{
{"both empty", "", "", true},
{"id set", "abc-123", "", false},
{"name set", "", "my-deploy", false},
{"both set", "abc-123", "my-deploy", false},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := ValidateDeploymentSelector(tc.deploymentID, tc.depName)
if tc.wantErr && err == nil {
t.Error("expected error, got nil")
}
if !tc.wantErr && err != nil {
t.Errorf("unexpected error: %v", err)
}
})
}
}
func TestSecretsFromEnv(t *testing.T) {
envVars := map[string]string{
"MY_SECRET": "value1",
"ANOTHER_SECRET": "value2",
"LANGCHAIN_API_KEY": "should-be-filtered",
"POSTGRES_URI": "should-be-filtered",
"EMPTY_VAR": "",
}
secrets := SecretsFromEnv(envVars)
// Sort for deterministic comparison
sort.Slice(secrets, func(i, j int) bool {
return secrets[i].Name < secrets[j].Name
})
if len(secrets) != 2 {
t.Fatalf("expected 2 secrets, got %d: %+v", len(secrets), secrets)
}
if secrets[0].Name != "ANOTHER_SECRET" || secrets[0].Value != "value2" {
t.Errorf("unexpected secret[0]: %+v", secrets[0])
}
if secrets[1].Name != "MY_SECRET" || secrets[1].Value != "value1" {
t.Errorf("unexpected secret[1]: %+v", secrets[1])
}
}
func TestResolveAPIKey(t *testing.T) {
// Flag value takes precedence
got := ResolveAPIKey("flag-key", map[string]string{"LANGSMITH_API_KEY": "env-key"})
if got != "flag-key" {
t.Errorf("expected flag-key, got %q", got)
}
// envVars map is checked next
got = ResolveAPIKey("", map[string]string{"LANGSMITH_API_KEY": "env-key"})
if got != "env-key" {
t.Errorf("expected env-key, got %q", got)
}
// os.Getenv fallback
os.Setenv("LANGSMITH_API_KEY", "os-env-key")
defer os.Unsetenv("LANGSMITH_API_KEY")
got = ResolveAPIKey("", nil)
if got != "os-env-key" {
t.Errorf("expected os-env-key, got %q", got)
}
// Flag still takes precedence over os env
got = ResolveAPIKey("flag-value", nil)
if got != "flag-value" {
t.Errorf("expected flag-value, got %q", got)
}
// Empty everything returns empty
os.Unsetenv("LANGSMITH_API_KEY")
os.Unsetenv("LANGGRAPH_HOST_API_KEY")
os.Unsetenv("LANGCHAIN_API_KEY")
got = ResolveAPIKey("", nil)
if got != "" {
t.Errorf("expected empty string, got %q", got)
}
}
-529
View File
@@ -1,529 +0,0 @@
// Package docker provides Docker compose generation, capability detection,
// and image building for the LangGraph CLI.
package docker
import (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"time"
"github.com/langchain-ai/langgraph/libs/cli/internal/config"
)
// DefaultPostgresURI is the default connection string used when no custom
// Postgres URI is provided.
const DefaultPostgresURI = "postgres://postgres:postgres@langgraph-postgres:5432/postgres?sslmode=disable"
// Version represents a semantic version with major, minor, and patch components.
type Version struct {
Major, Minor, Patch int
}
// GreaterOrEqual returns true if v >= other.
func (v Version) GreaterOrEqual(other Version) bool {
if v.Major != other.Major {
return v.Major > other.Major
}
if v.Minor != other.Minor {
return v.Minor > other.Minor
}
return v.Patch >= other.Patch
}
// DockerCapabilities describes the Docker environment available on the host.
type DockerCapabilities struct {
VersionDocker Version
VersionCompose Version
HealthcheckStartInterval bool
ComposeType string // "plugin" or "standalone"
}
// ComposeOpts configures the generated docker-compose YAML.
type ComposeOpts struct {
Port int
DebuggerPort int // 0 means no debugger
DebuggerBaseURL string // optional base URL for the debugger
PostgresURI string // empty means use DefaultPostgresURI
Image string // pre-built image name
BaseImage string
APIVersion string
EngineRuntimeMode string // "combined_queue_worker" or "distributed"
}
// BuildImageOpts configures docker image building.
type BuildImageOpts struct {
ConfigPath string
ConfigJSON map[string]any
BaseImage string
APIVersion string
Pull bool
Tag string
Passthrough []string
InstallCommand string
BuildCommand string
DockerCommand []string // default: ["docker", "build"]
ExtraFlags []string
Verbose bool
}
// OrderedMap preserves insertion order for map keys.
type OrderedMap struct {
Keys []string
Values map[string]any
}
// NewOrderedMap creates an empty OrderedMap.
func NewOrderedMap() *OrderedMap {
return &OrderedMap{
Values: make(map[string]any),
}
}
// Set adds or updates a key-value pair, preserving insertion order.
func (om *OrderedMap) Set(key string, value any) {
if _, exists := om.Values[key]; !exists {
om.Keys = append(om.Keys, key)
}
om.Values[key] = value
}
// Get retrieves the value for a key.
func (om *OrderedMap) Get(key string) (any, bool) {
v, ok := om.Values[key]
return v, ok
}
// ---------------------------------------------------------------------------
// ParseVersion
// ---------------------------------------------------------------------------
// ParseVersion parses a version string like "1.2.3", "v1.2.3-alpha+build",
// "1.2", or "1" into a Version.
func ParseVersion(version string) Version {
parts := strings.SplitN(version, ".", 3)
major := "0"
minor := "0"
patch := "0"
switch len(parts) {
case 1:
major = parts[0]
case 2:
major = parts[0]
minor = parts[1]
default:
major = parts[0]
minor = parts[1]
patch = parts[2]
}
// Strip "v" prefix from major
major = strings.TrimPrefix(major, "v")
// Strip "-" and "+" suffixes from patch
if idx := strings.IndexAny(patch, "-+"); idx >= 0 {
patch = patch[:idx]
}
majorInt, _ := strconv.Atoi(major)
minorInt, _ := strconv.Atoi(minor)
patchInt, _ := strconv.Atoi(patch)
return Version{Major: majorInt, Minor: minorInt, Patch: patchInt}
}
// ---------------------------------------------------------------------------
// CanBuildLocally
// ---------------------------------------------------------------------------
// CanBuildLocally checks whether local deployment builds can run on this machine.
// It returns (ok, errorMessage). If ok is true, errorMessage is empty.
func CanBuildLocally() (bool, string) {
if _, err := exec.LookPath("docker"); err != nil {
return false, "Docker is required but not installed.\n" +
"Install Docker Desktop: https://docs.docker.com/get-docker/"
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "docker", "info")
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return false, "Docker is installed but not running.\nStart Docker and try again."
}
if runtime.GOARCH != "amd64" {
ctx2, cancel2 := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel2()
buildx := exec.CommandContext(ctx2, "docker", "buildx", "version")
buildx.Stdout = nil
buildx.Stderr = nil
if err := buildx.Run(); err != nil {
arch := runtime.GOARCH
// Try to match Python's platform.machine() naming for the error message
if arch == "arm64" {
arch = "aarch64"
}
return false, "Docker Buildx is required but not installed.\n" +
"Your machine architecture (" + arch + ") requires Buildx to cross-compile images for linux/amd64.\n" +
"Install Buildx: https://docs.docker.com/build/install-buildx/"
}
}
return true, ""
}
// ---------------------------------------------------------------------------
// CheckCapabilities
// ---------------------------------------------------------------------------
// CheckCapabilities detects the Docker and Docker Compose versions available
// on the host and returns a DockerCapabilities describing them.
func CheckCapabilities() (*DockerCapabilities, error) {
if _, err := exec.LookPath("docker"); err != nil {
return nil, fmt.Errorf("Docker not installed")
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
out, err := exec.CommandContext(ctx, "docker", "info", "-f", "{{json .}}").Output()
if err != nil {
return nil, fmt.Errorf("Docker not installed or not running")
}
var info map[string]any
if err := json.Unmarshal(out, &info); err != nil {
return nil, fmt.Errorf("Docker not installed or not running")
}
serverVersion, _ := info["ServerVersion"].(string)
if serverVersion == "" {
return nil, fmt.Errorf("Docker not running")
}
// Try to find compose plugin
var composeVersionStr string
composeType := "plugin"
found := false
if clientInfo, ok := info["ClientInfo"].(map[string]any); ok {
if plugins, ok := clientInfo["Plugins"].([]any); ok {
for _, p := range plugins {
pm, ok := p.(map[string]any)
if !ok {
continue
}
name, _ := pm["Name"].(string)
if name == "compose" {
composeVersionStr, _ = pm["Version"].(string)
found = true
break
}
}
}
}
if !found {
// Fall back to standalone docker-compose
if _, err := exec.LookPath("docker-compose"); err != nil {
return nil, fmt.Errorf("Docker Compose not installed")
}
ctx2, cancel2 := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel2()
out2, err := exec.CommandContext(ctx2, "docker-compose", "--version", "--short").Output()
if err != nil {
return nil, fmt.Errorf("Docker Compose not installed")
}
composeVersionStr = strings.TrimSpace(string(out2))
composeType = "standalone"
}
dockerVersion := ParseVersion(serverVersion)
composeVersion := ParseVersion(composeVersionStr)
return &DockerCapabilities{
VersionDocker: dockerVersion,
VersionCompose: composeVersion,
HealthcheckStartInterval: dockerVersion.GreaterOrEqual(Version{25, 0, 0}),
ComposeType: composeType,
}, nil
}
// ---------------------------------------------------------------------------
// DebuggerCompose
// ---------------------------------------------------------------------------
// DebuggerCompose returns a service config map for the langgraph-debugger
// container, or nil if port is 0 (no debugger requested).
func DebuggerCompose(port int, baseURL string) *OrderedMap {
if port == 0 {
return nil
}
dependsOn := NewOrderedMap()
postgresCondition := NewOrderedMap()
postgresCondition.Set("condition", "service_healthy")
dependsOn.Set("langgraph-postgres", postgresCondition)
service := NewOrderedMap()
service.Set("image", "langchain/langgraph-debugger")
service.Set("restart", "on-failure")
service.Set("depends_on", dependsOn)
service.Set("ports", []any{fmt.Sprintf(`"%d:3968"`, port)})
if baseURL != "" {
env := NewOrderedMap()
env.Set("VITE_STUDIO_LOCAL_GRAPH_URL", baseURL)
service.Set("environment", env)
}
result := NewOrderedMap()
result.Set("langgraph-debugger", service)
return result
}
// ---------------------------------------------------------------------------
// DictToYAML
// ---------------------------------------------------------------------------
// DictToYAML converts an OrderedMap to a YAML string. For top-level keys
// (indent < 2) it adds a blank line between entries (except the first).
func DictToYAML(d *OrderedMap, indent int) string {
var b strings.Builder
for idx, key := range d.Keys {
// Add blank line between top-level entries (except first)
if idx >= 1 && indent < 2 {
b.WriteString("\n")
}
space := strings.Repeat(" ", indent)
value := d.Values[key]
switch v := value.(type) {
case *OrderedMap:
b.WriteString(fmt.Sprintf("%s%s:\n", space, key))
b.WriteString(DictToYAML(v, indent+1))
case []any:
b.WriteString(fmt.Sprintf("%s%s:\n", space, key))
for _, item := range v {
b.WriteString(fmt.Sprintf("%s - %v\n", space, item))
}
default:
b.WriteString(fmt.Sprintf("%s%s: %v\n", space, key, value))
}
}
return b.String()
}
// ---------------------------------------------------------------------------
// ComposeAsDict
// ---------------------------------------------------------------------------
// ComposeAsDict builds the docker-compose configuration as an OrderedMap.
func ComposeAsDict(caps *DockerCapabilities, opts ComposeOpts) *OrderedMap {
postgresURI := opts.PostgresURI
includeDB := false
if postgresURI == "" {
includeDB = true
postgresURI = DefaultPostgresURI
}
services := NewOrderedMap()
// --- Redis service ---
redisHealthcheck := NewOrderedMap()
redisHealthcheck.Set("test", "redis-cli ping")
redisHealthcheck.Set("interval", "5s")
redisHealthcheck.Set("timeout", "1s")
redisHealthcheck.Set("retries", 5)
redisService := NewOrderedMap()
redisService.Set("image", "redis:6")
redisService.Set("healthcheck", redisHealthcheck)
services.Set("langgraph-redis", redisService)
// --- Postgres service (if no custom URI) ---
if includeDB {
pgEnv := NewOrderedMap()
pgEnv.Set("POSTGRES_DB", "postgres")
pgEnv.Set("POSTGRES_USER", "postgres")
pgEnv.Set("POSTGRES_PASSWORD", "postgres")
pgHealthcheck := NewOrderedMap()
pgHealthcheck.Set("test", "pg_isready -U postgres")
pgHealthcheck.Set("start_period", "10s")
pgHealthcheck.Set("timeout", "1s")
pgHealthcheck.Set("retries", 5)
if caps.HealthcheckStartInterval {
pgHealthcheck.Set("interval", "60s")
pgHealthcheck.Set("start_interval", "1s")
} else {
pgHealthcheck.Set("interval", "5s")
}
pgService := NewOrderedMap()
pgService.Set("image", "pgvector/pgvector:pg16")
pgService.Set("ports", []any{`"5433:5432"`})
pgService.Set("environment", pgEnv)
pgService.Set("command", []any{"postgres", "-c", "shared_preload_libraries=vector"})
pgService.Set("volumes", []any{"langgraph-data:/var/lib/postgresql/data"})
pgService.Set("healthcheck", pgHealthcheck)
services.Set("langgraph-postgres", pgService)
}
// --- Debugger service (optional) ---
if opts.DebuggerPort != 0 {
debuggerMap := DebuggerCompose(opts.DebuggerPort, opts.DebuggerBaseURL)
if debuggerMap != nil {
debuggerService, _ := debuggerMap.Get("langgraph-debugger")
services.Set("langgraph-debugger", debuggerService)
}
}
// --- langgraph-api service ---
apiEnv := NewOrderedMap()
apiEnv.Set("REDIS_URI", "redis://langgraph-redis:6379")
apiEnv.Set("POSTGRES_URI", postgresURI)
if opts.EngineRuntimeMode == "distributed" {
apiEnv.Set("N_JOBS_PER_WORKER", `"0"`)
}
apiDependsOn := NewOrderedMap()
redisCondition := NewOrderedMap()
redisCondition.Set("condition", "service_healthy")
apiDependsOn.Set("langgraph-redis", redisCondition)
if includeDB {
pgCondition := NewOrderedMap()
pgCondition.Set("condition", "service_healthy")
apiDependsOn.Set("langgraph-postgres", pgCondition)
}
apiService := NewOrderedMap()
apiService.Set("ports", []any{fmt.Sprintf(`"%d:8000"`, opts.Port)})
apiService.Set("depends_on", apiDependsOn)
apiService.Set("environment", apiEnv)
if opts.Image != "" {
apiService.Set("image", opts.Image)
}
if caps.HealthcheckStartInterval {
apiHealthcheck := NewOrderedMap()
apiHealthcheck.Set("test", "python /api/healthcheck.py")
apiHealthcheck.Set("interval", "60s")
apiHealthcheck.Set("start_interval", "1s")
apiHealthcheck.Set("start_period", "10s")
apiService.Set("healthcheck", apiHealthcheck)
}
services.Set("langgraph-api", apiService)
// --- Build final compose dict ---
composeDict := NewOrderedMap()
if includeDB {
volumes := NewOrderedMap()
volumeDriver := NewOrderedMap()
volumeDriver.Set("driver", "local")
volumes.Set("langgraph-data", volumeDriver)
composeDict.Set("volumes", volumes)
}
composeDict.Set("services", services)
return composeDict
}
// ---------------------------------------------------------------------------
// Compose
// ---------------------------------------------------------------------------
// Compose generates a docker-compose YAML string from the given capabilities
// and options.
func Compose(caps *DockerCapabilities, opts ComposeOpts) string {
d := ComposeAsDict(caps, opts)
return DictToYAML(d, 0)
}
// ---------------------------------------------------------------------------
// BuildDockerImage
// ---------------------------------------------------------------------------
// BuildDockerImage builds a Docker image from a LangGraph configuration.
// It shells out to docker build (or a custom docker command) with the
// generated Dockerfile piped via stdin.
func BuildDockerImage(opts BuildImageOpts) error {
dockerCmd := opts.DockerCommand
if len(dockerCmd) == 0 {
dockerCmd = []string{"docker", "build"}
}
// Pull the base image first if requested.
if opts.Pull {
pullCmd := exec.Command("docker", "pull", opts.Tag)
pullCmd.Stdout = os.Stdout
pullCmd.Stderr = os.Stderr
if err := pullCmd.Run(); err != nil {
return fmt.Errorf("failed to pull image %s: %w", opts.Tag, err)
}
}
// Build the docker build arguments.
args := []string{
"-f", "-", // read Dockerfile from stdin
"-t", opts.Tag,
}
// Determine build context.
buildContext := "."
if opts.ConfigPath != "" {
// Use the parent directory of the config file by default.
idx := strings.LastIndex(opts.ConfigPath, "/")
if idx >= 0 {
buildContext = opts.ConfigPath[:idx]
}
}
// Generate the Dockerfile using the real config.ConfigToDocker.
dockerfile, additionalContexts, err := config.ConfigToDocker(opts.ConfigPath, opts.ConfigJSON, config.DockerOpts{
BaseImage: opts.BaseImage,
APIVersion: opts.APIVersion,
InstallCommand: opts.InstallCommand,
BuildCommand: opts.BuildCommand,
})
if err != nil {
return fmt.Errorf("generating Dockerfile: %w", err)
}
// Add additional build contexts (for dependencies outside the main context).
for name, path := range additionalContexts {
args = append(args, "--build-context", fmt.Sprintf("%s=%s", name, path))
}
// Assemble the full command.
fullArgs := append(dockerCmd[1:], args...)
fullArgs = append(fullArgs, opts.ExtraFlags...)
fullArgs = append(fullArgs, opts.Passthrough...)
fullArgs = append(fullArgs, buildContext)
cmd := exec.Command(dockerCmd[0], fullArgs...)
cmd.Stdin = strings.NewReader(dockerfile)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
-290
View File
@@ -1,290 +0,0 @@
package docker
import (
"fmt"
"strings"
"testing"
)
func cleanEmptyLines(s string) string {
lines := strings.Split(s, "\n")
var result []string
for _, line := range lines {
if strings.TrimSpace(line) != "" {
result = append(result, line)
}
}
return strings.Join(result, "\n")
}
var defaultCaps = &DockerCapabilities{
VersionDocker: Version{Major: 26, Minor: 1, Patch: 1},
VersionCompose: Version{Major: 2, Minor: 27, Patch: 0},
HealthcheckStartInterval: false,
}
func TestComposeCustomDBNoDebugger(t *testing.T) {
port := 8123
actual := Compose(defaultCaps, ComposeOpts{
Port: port,
PostgresURI: "custom_postgres_uri",
})
expected := fmt.Sprintf(`services:
langgraph-redis:
image: redis:6
healthcheck:
test: redis-cli ping
interval: 5s
timeout: 1s
retries: 5
langgraph-api:
ports:
- "%d:8000"
depends_on:
langgraph-redis:
condition: service_healthy
environment:
REDIS_URI: redis://langgraph-redis:6379
POSTGRES_URI: custom_postgres_uri`, port)
if cleanEmptyLines(actual) != expected {
t.Errorf("mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, cleanEmptyLines(actual))
}
}
func TestComposeCustomDBWithHealthcheck(t *testing.T) {
port := 8123
capsHC := &DockerCapabilities{
VersionDocker: Version{Major: 26, Minor: 1, Patch: 1},
VersionCompose: Version{Major: 2, Minor: 27, Patch: 0},
HealthcheckStartInterval: true,
}
actual := Compose(capsHC, ComposeOpts{
Port: port,
PostgresURI: "custom_postgres_uri",
})
expected := fmt.Sprintf(`services:
langgraph-redis:
image: redis:6
healthcheck:
test: redis-cli ping
interval: 5s
timeout: 1s
retries: 5
langgraph-api:
ports:
- "%d:8000"
depends_on:
langgraph-redis:
condition: service_healthy
environment:
REDIS_URI: redis://langgraph-redis:6379
POSTGRES_URI: custom_postgres_uri
healthcheck:
test: python /api/healthcheck.py
interval: 60s
start_interval: 1s
start_period: 10s`, port)
if cleanEmptyLines(actual) != expected {
t.Errorf("mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, cleanEmptyLines(actual))
}
}
func TestComposeDefaultDB(t *testing.T) {
port := 8123
actual := Compose(defaultCaps, ComposeOpts{Port: port})
expected := fmt.Sprintf(`volumes:
langgraph-data:
driver: local
services:
langgraph-redis:
image: redis:6
healthcheck:
test: redis-cli ping
interval: 5s
timeout: 1s
retries: 5
langgraph-postgres:
image: pgvector/pgvector:pg16
ports:
- "5433:5432"
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
command:
- postgres
- -c
- shared_preload_libraries=vector
volumes:
- langgraph-data:/var/lib/postgresql/data
healthcheck:
test: pg_isready -U postgres
start_period: 10s
timeout: 1s
retries: 5
interval: 5s
langgraph-api:
ports:
- "%d:8000"
depends_on:
langgraph-redis:
condition: service_healthy
langgraph-postgres:
condition: service_healthy
environment:
REDIS_URI: redis://langgraph-redis:6379
POSTGRES_URI: %s`, port, DefaultPostgresURI)
if cleanEmptyLines(actual) != expected {
t.Errorf("mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, cleanEmptyLines(actual))
}
}
func TestComposeDistributedMode(t *testing.T) {
port := 8123
actual := Compose(defaultCaps, ComposeOpts{
Port: port,
PostgresURI: "custom_postgres_uri",
EngineRuntimeMode: "distributed",
})
expected := fmt.Sprintf(`services:
langgraph-redis:
image: redis:6
healthcheck:
test: redis-cli ping
interval: 5s
timeout: 1s
retries: 5
langgraph-api:
ports:
- "%d:8000"
depends_on:
langgraph-redis:
condition: service_healthy
environment:
REDIS_URI: redis://langgraph-redis:6379
POSTGRES_URI: custom_postgres_uri
N_JOBS_PER_WORKER: "0"`, port)
if cleanEmptyLines(actual) != expected {
t.Errorf("mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, cleanEmptyLines(actual))
}
}
func TestComposeCombinedModeNoNJobs(t *testing.T) {
actual := Compose(defaultCaps, ComposeOpts{
Port: 8123,
EngineRuntimeMode: "combined_queue_worker",
})
if strings.Contains(actual, "N_JOBS_PER_WORKER") {
t.Error("combined mode should not contain N_JOBS_PER_WORKER")
}
}
func TestComposeDebuggerDefaultDB(t *testing.T) {
port := 8123
debuggerPort := 8001
actual := Compose(defaultCaps, ComposeOpts{
Port: port,
DebuggerPort: debuggerPort,
})
expected := fmt.Sprintf(`volumes:
langgraph-data:
driver: local
services:
langgraph-redis:
image: redis:6
healthcheck:
test: redis-cli ping
interval: 5s
timeout: 1s
retries: 5
langgraph-postgres:
image: pgvector/pgvector:pg16
ports:
- "5433:5432"
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
command:
- postgres
- -c
- shared_preload_libraries=vector
volumes:
- langgraph-data:/var/lib/postgresql/data
healthcheck:
test: pg_isready -U postgres
start_period: 10s
timeout: 1s
retries: 5
interval: 5s
langgraph-debugger:
image: langchain/langgraph-debugger
restart: on-failure
depends_on:
langgraph-postgres:
condition: service_healthy
ports:
- "%d:3968"
langgraph-api:
ports:
- "%d:8000"
depends_on:
langgraph-redis:
condition: service_healthy
langgraph-postgres:
condition: service_healthy
environment:
REDIS_URI: redis://langgraph-redis:6379
POSTGRES_URI: %s`, debuggerPort, port, DefaultPostgresURI)
if cleanEmptyLines(actual) != expected {
t.Errorf("mismatch.\nExpected:\n%s\n\nGot:\n%s", expected, cleanEmptyLines(actual))
}
}
func TestParseVersion(t *testing.T) {
tests := []struct {
input string
expected Version
}{
{"1.2.3", Version{1, 2, 3}},
{"v1.2.3", Version{1, 2, 3}},
{"1.2.3-alpha", Version{1, 2, 3}},
{"1.2.3+1", Version{1, 2, 3}},
{"1.2.3-alpha+build", Version{1, 2, 3}},
{"1.2", Version{1, 2, 0}},
{"1", Version{1, 0, 0}},
{"v28.1.1+1", Version{28, 1, 1}},
{"2.0.0-beta.1+exp.sha.5114f85", Version{2, 0, 0}},
{"v3.4.5-rc1+build.123", Version{3, 4, 5}},
}
for _, tc := range tests {
result := ParseVersion(tc.input)
if result != tc.expected {
t.Errorf("ParseVersion(%q) = %v, want %v", tc.input, result, tc.expected)
}
}
}
func TestVersionGreaterOrEqual(t *testing.T) {
tests := []struct {
v, other Version
want bool
}{
{Version{25, 0, 0}, Version{25, 0, 0}, true},
{Version{26, 1, 1}, Version{25, 0, 0}, true},
{Version{24, 9, 9}, Version{25, 0, 0}, false},
}
for _, tc := range tests {
got := tc.v.GreaterOrEqual(tc.other)
if got != tc.want {
t.Errorf("%v.GreaterOrEqual(%v) = %v, want %v", tc.v, tc.other, got, tc.want)
}
}
}
-123
View File
@@ -1,123 +0,0 @@
// Package lgexec provides subprocess execution helpers for the LangGraph CLI.
//
// The package name is lgexec (rather than exec) to avoid shadowing the
// standard library os/exec package.
package lgexec
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"os/exec"
"strings"
)
// RunOpts configures how a subprocess is executed.
type RunOpts struct {
Stdin string // input to pass via stdin
Verbose bool // pipe stdout/stderr to os.Stdout/os.Stderr
Dir string // working directory
Env []string // environment variables (KEY=VALUE)
}
// Run executes the named program with the given arguments.
//
// When Verbose is true stdout and stderr are forwarded to the process's
// os.Stdout / os.Stderr. Otherwise output is silently discarded.
// A non-zero exit code is returned as an *ExitError.
func Run(name string, args []string, opts RunOpts) error {
cmd := exec.Command(name, args...)
if opts.Dir != "" {
cmd.Dir = opts.Dir
}
if len(opts.Env) > 0 {
cmd.Env = append(os.Environ(), opts.Env...)
}
if opts.Stdin != "" {
cmd.Stdin = strings.NewReader(opts.Stdin)
}
if opts.Verbose {
if opts.Stdin != "" {
cmdStr := fmt.Sprintf("+ %s %s", name, strings.Join(args, " "))
fmt.Printf("%s <\n%s\n", cmdStr, strings.Join(
nonEmptyLines(opts.Stdin), "\n"))
} else {
fmt.Printf("+ %s %s\n", name, strings.Join(args, " "))
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else {
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
}
return cmd.Run()
}
// RunCollect executes the named program and collects stdout and stderr.
// Both are returned as strings. A non-zero exit code results in a non-nil error.
func RunCollect(name string, args []string) (stdout, stderr string, err error) {
cmd := exec.Command(name, args...)
var outBuf, errBuf bytes.Buffer
cmd.Stdout = &outBuf
cmd.Stderr = &errBuf
err = cmd.Run()
return outBuf.String(), errBuf.String(), err
}
// RunWithCallback executes the named program and invokes onStdout for each
// line of stdout output. If onStdout returns true the callback is no longer
// called and remaining stdout is forwarded directly to os.Stdout (matching
// the Python CLI's monitor_stream behaviour).
// Stderr is always forwarded to os.Stderr.
func RunWithCallback(name string, args []string, onStdout func(string) bool) error {
cmd := exec.Command(name, args...)
cmd.Stderr = os.Stderr
pipe, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("cannot create stdout pipe: %w", err)
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("cannot start command: %w", err)
}
scanner := bufio.NewScanner(pipe)
stopped := false
for scanner.Scan() {
line := scanner.Text()
if stopped {
// After callback signalled stop, forward remaining output.
fmt.Fprintln(os.Stdout, line)
continue
}
if onStdout(line) {
stopped = true
}
}
if scanErr := scanner.Err(); scanErr != nil {
// Drain but ignore read errors on stdout — the exit code matters.
_ = scanErr
}
return cmd.Wait()
}
// nonEmptyLines splits s on newlines and returns lines that are not empty.
func nonEmptyLines(s string) []string {
var out []string
for _, line := range strings.Split(s, "\n") {
if line != "" {
out = append(out, line)
}
}
return out
}
File diff suppressed because it is too large Load Diff
-287
View File
@@ -1,287 +0,0 @@
package root
import (
"bytes"
"io"
"os"
"path/filepath"
"strings"
"testing"
)
func TestRunHelp(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run(nil, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if stderr.Len() != 0 {
t.Fatalf("expected no stderr output, got %q", stderr.String())
}
if !strings.Contains(stdout.String(), "validate") {
t.Fatalf("expected help text to contain 'validate', got %q", stdout.String())
}
}
func TestRunVersion(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"version"}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if stderr.Len() != 0 {
t.Fatalf("expected no stderr output, got %q", stderr.String())
}
if !strings.Contains(stdout.String(), "langgraph") {
t.Fatalf("unexpected stdout: %q", stdout.String())
}
}
func TestRunUnknownCommand(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"nonexistent-cmd"}, &stdout, &stderr)
if exitCode != 1 {
t.Fatalf("expected exit code 1, got %d", exitCode)
}
if !strings.Contains(stderr.String(), "is not a langgraph command") {
t.Fatalf("unexpected stderr: %q", stderr.String())
}
}
func writeTempConfig(t *testing.T, content string) string {
t.Helper()
dir := t.TempDir()
path := filepath.Join(dir, "langgraph.json")
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
t.Fatalf("failed to write temp config: %v", err)
}
return path
}
func TestRunValidateWithValidConfig(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
path := writeTempConfig(t, `{"dependencies": ["langchain"], "graphs": {"agent": "./agent.py:graph"}}`)
exitCode := Run([]string{"validate", "-c", path}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d; stderr: %q", exitCode, stderr.String())
}
if !strings.Contains(stdout.String(), "is valid") {
t.Fatalf("expected stdout to contain 'is valid', got %q", stdout.String())
}
if !strings.Contains(stdout.String(), "1 graph") {
t.Fatalf("expected stdout to contain '1 graph', got %q", stdout.String())
}
}
func TestRunValidateWithInvalidConfig(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
path := writeTempConfig(t, `{"graphs": {}}`)
exitCode := Run([]string{"validate", "-c", path}, &stdout, &stderr)
if exitCode != 1 {
t.Fatalf("expected exit code 1, got %d", exitCode)
}
if !strings.Contains(stderr.String(), "No graphs found") {
t.Fatalf("expected stderr to contain 'No graphs found', got %q", stderr.String())
}
}
func TestRunValidateWithInvalidJSON(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
path := writeTempConfig(t, `{invalid json`)
exitCode := Run([]string{"validate", "-c", path}, &stdout, &stderr)
if exitCode != 1 {
t.Fatalf("expected exit code 1, got %d", exitCode)
}
if !strings.Contains(stderr.String(), "Invalid JSON") {
t.Fatalf("expected stderr to contain 'Invalid JSON', got %q", stderr.String())
}
}
func TestRunValidateWithNonObjectJSON(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
path := writeTempConfig(t, `[]`)
exitCode := Run([]string{"validate", "-c", path}, &stdout, &stderr)
if exitCode != 1 {
t.Fatalf("expected exit code 1, got %d", exitCode)
}
if !strings.Contains(stderr.String(), "top-level JSON value must be an object") {
t.Fatalf("expected stderr to mention object-shaped config, got %q", stderr.String())
}
}
func TestRunValidateDefaultConfigMissing(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
// Use a path that definitely does not exist.
nonexistent := filepath.Join(t.TempDir(), "langgraph.json")
exitCode := Run([]string{"validate", "-c", nonexistent}, &stdout, &stderr)
if exitCode != 1 {
t.Fatalf("expected exit code 1, got %d", exitCode)
}
if !strings.Contains(stderr.String(), "does not exist") {
t.Fatalf("expected stderr to contain 'does not exist', got %q", stderr.String())
}
}
func TestRunValidateWithUnknownKeys(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
path := writeTempConfig(t, `{"dependencies": ["langchain"], "graphs": {"agent": "./agent.py:graph"}, "grpahs": {}}`)
exitCode := Run([]string{"validate", "-c", path}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d; stderr: %q", exitCode, stderr.String())
}
out := stdout.String()
if !strings.Contains(strings.ToLower(out), "warning") {
t.Fatalf("expected stdout to contain 'warning', got %q", out)
}
if !strings.Contains(strings.ToLower(out), "did you mean") {
t.Fatalf("expected stdout to contain 'did you mean', got %q", out)
}
}
func TestRunValidateHelp(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"validate", "--help"}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "Validate the LangGraph configuration file") {
t.Fatalf("expected stdout to contain validate help text, got %q", stdout.String())
}
}
func TestRunValidateMultipleGraphs(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
path := writeTempConfig(t, `{"dependencies": ["langchain"], "graphs": {"agent": "./a.py:g", "bot": "./b.py:g"}}`)
exitCode := Run([]string{"validate", "-c", path}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d; stderr: %q", exitCode, stderr.String())
}
if !strings.Contains(stdout.String(), "2 graphs found") {
t.Fatalf("expected stdout to contain '2 graphs found', got %q", stdout.String())
}
}
func TestRunValidateWithWarningsAndErrors(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
path := writeTempConfig(t, `{"graphs": {}, "grpahs": {}}`)
exitCode := Run([]string{"validate", "-c", path}, &stdout, &stderr)
if exitCode != 1 {
t.Fatalf("expected exit code 1, got %d", exitCode)
}
errOut := stderr.String()
if !strings.Contains(errOut, "No graphs found") {
t.Fatalf("expected stderr to contain 'No graphs found', got %q", errOut)
}
if !strings.Contains(strings.ToLower(errOut), "warning") {
t.Fatalf("expected stderr to contain 'warning', got %q", errOut)
}
}
func TestRunValidateWithInvalidPackageJSON(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
dir := t.TempDir()
configPath := filepath.Join(dir, "langgraph.json")
packagePath := filepath.Join(dir, "package.json")
if err := os.WriteFile(
configPath,
[]byte(`{"node_version":"20","graphs":{"agent":"./agent.js:graph"}}`),
0644,
); err != nil {
t.Fatalf("failed to write config: %v", err)
}
if err := os.WriteFile(packagePath, []byte(`{invalid json`), 0644); err != nil {
t.Fatalf("failed to write package.json: %v", err)
}
exitCode := Run([]string{"validate", "-c", configPath}, &stdout, &stderr)
if exitCode != 1 {
t.Fatalf("expected exit code 1, got %d", exitCode)
}
if !strings.Contains(stderr.String(), "Invalid package.json found") {
t.Fatalf("expected stderr to mention invalid package.json, got %q", stderr.String())
}
}
func TestRunDeployDelegatesToPythonCLI(t *testing.T) {
t.Setenv("LANGGRAPH_CALLING_PYTHON", "/custom/python")
originalRunPythonSubprocess := runPythonSubprocess
t.Cleanup(func() {
runPythonSubprocess = originalRunPythonSubprocess
})
var gotPython string
var gotArgs []string
runPythonSubprocess = func(
pythonExe string,
args []string,
stdout io.Writer,
stderr io.Writer,
) error {
gotPython = pythonExe
gotArgs = append([]string(nil), args...)
return nil
}
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"deploy", "--remote", "--install-command", "make deps"}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d; stderr: %q", exitCode, stderr.String())
}
if gotPython != "/custom/python" {
t.Fatalf("expected delegated python to be /custom/python, got %q", gotPython)
}
expected := []string{"-m", "langgraph_cli.cli", "deploy", "--remote", "--install-command", "make deps"}
if strings.Join(gotArgs, "\x00") != strings.Join(expected, "\x00") {
t.Fatalf("unexpected delegated args: got %q want %q", gotArgs, expected)
}
}
-258
View File
@@ -1,258 +0,0 @@
// Package templates provides template definitions and project scaffolding
// for the LangGraph CLI `new` command.
package templates
import (
"archive/zip"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
)
// Template describes a project template with language-specific download URLs.
type Template struct {
Name string
Description string
Languages map[string]string // lang -> download URL
}
// Templates is the ordered list of available project templates.
var Templates = []Template{
{
Name: "Deep Agent",
Description: "An opinionated deployment template for a Deep Agent.",
Languages: map[string]string{
"python": "https://github.com/langchain-ai/deep-agent-template/archive/refs/heads/main.zip",
"js": "https://github.com/langchain-ai/deep-agent-template-js/archive/refs/heads/main.zip",
},
},
{
Name: "Agent",
Description: "A simple agent that can be flexibly extended to many tools.",
Languages: map[string]string{
"python": "https://github.com/langchain-ai/simple-agent-template/archive/refs/heads/main.zip",
},
},
{
Name: "New LangGraph Project",
Description: "A simple, minimal chatbot with memory.",
Languages: map[string]string{
"python": "https://github.com/langchain-ai/new-langgraph-project/archive/refs/heads/main.zip",
"js": "https://github.com/langchain-ai/new-langgraphjs-project/archive/refs/heads/main.zip",
},
},
}
// templateIDEntry maps a template ID to its download URL, template name, and language.
type templateIDEntry struct {
URL string
Name string
Language string
}
// templateIDMap is built once at init time from the Templates slice.
var templateIDMap map[string]templateIDEntry
func init() {
templateIDMap = make(map[string]templateIDEntry)
for _, t := range Templates {
for lang, url := range t.Languages {
if lang != "python" && lang != "js" {
continue
}
id := toTemplateID(t.Name, lang)
templateIDMap[id] = templateIDEntry{
URL: url,
Name: t.Name,
Language: lang,
}
}
}
}
// toTemplateID converts a template name and language into a slug like "deep-agent-python".
func toTemplateID(name, lang string) string {
return strings.ToLower(strings.ReplaceAll(name, " ", "-")) + "-" + lang
}
// ListTemplateIDs returns a sorted list of all available template IDs.
func ListTemplateIDs() []string {
ids := make([]string, 0, len(templateIDMap))
for id := range templateIDMap {
ids = append(ids, id)
}
sort.Strings(ids)
return ids
}
// TemplateHelp returns a formatted help string listing available templates.
func TemplateHelp() string {
var b strings.Builder
b.WriteString("The name of the template to use. Available options:\n")
for _, id := range ListTemplateIDs() {
entry := templateIDMap[id]
// Find the description from the Templates slice.
var desc string
for _, t := range Templates {
if t.Name == entry.Name {
desc = t.Description
break
}
}
fmt.Fprintf(&b, " %s: %s\n", id, desc)
}
return b.String()
}
// CreateNew creates a new LangGraph project at path using the given templateID.
//
// If templateID is empty an error listing available templates is returned (the
// Go CLI is non-interactive, so we cannot prompt). If path is empty an error
// is returned.
func CreateNew(path, templateID string) error {
if path == "" {
return fmt.Errorf("path is required: specify the directory for the new project")
}
// Resolve to absolute path.
absPath, err := filepath.Abs(path)
if err != nil {
return fmt.Errorf("cannot resolve path: %w", err)
}
path = absPath
// Check if path exists and is not empty.
entries, err := os.ReadDir(path)
if err == nil && len(entries) > 0 {
return fmt.Errorf(
"the specified directory already exists and is not empty: %s. "+
"Aborting to prevent overwriting files", path)
}
if templateID == "" {
return fmt.Errorf(
"template is required. Use one of the following template IDs:\n%s",
TemplateHelp())
}
entry, ok := templateIDMap[templateID]
if !ok {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("template %q not found.\n", templateID))
sb.WriteString("Please select from the available options:\n")
for _, id := range ListTemplateIDs() {
e := templateIDMap[id]
var desc string
for _, t := range Templates {
if t.Name == e.Name {
desc = t.Description
break
}
}
fmt.Fprintf(&sb, " - %s: %s\n", id, desc)
}
return fmt.Errorf("%s", sb.String())
}
if err := DownloadAndExtract(entry.URL, path); err != nil {
return fmt.Errorf("failed to download template: %w", err)
}
return nil
}
// DownloadAndExtract downloads a ZIP archive from url and extracts it to
// destPath, stripping the top-level wrapper directory that GitHub includes
// in repository archives.
func DownloadAndExtract(url, destPath string) error {
// Ensure destination directory exists.
if err := os.MkdirAll(destPath, 0o755); err != nil {
return fmt.Errorf("cannot create destination directory: %w", err)
}
// Download to a temporary file.
tmpFile, err := os.CreateTemp("", "langgraph-template-*.zip")
if err != nil {
return fmt.Errorf("cannot create temp file: %w", err)
}
tmpPath := tmpFile.Name()
defer os.Remove(tmpPath)
resp, err := http.Get(url) //nolint:gosec
if err != nil {
tmpFile.Close()
return fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
tmpFile.Close()
return fmt.Errorf("HTTP %d: failed to download %s", resp.StatusCode, url)
}
if _, err := io.Copy(tmpFile, resp.Body); err != nil {
tmpFile.Close()
return fmt.Errorf("failed to write ZIP data: %w", err)
}
tmpFile.Close()
// Open the ZIP archive.
zr, err := zip.OpenReader(tmpPath)
if err != nil {
return fmt.Errorf("failed to open ZIP archive: %w", err)
}
defer zr.Close()
for _, f := range zr.File {
// Strip the first path component (GitHub's wrapper directory).
parts := strings.SplitN(f.Name, "/", 2)
if len(parts) < 2 || parts[1] == "" {
continue // skip the wrapper directory entry itself
}
relPath := parts[1]
outPath := filepath.Join(destPath, relPath)
// Ensure the output path is within destPath (zip-slip protection).
if !strings.HasPrefix(filepath.Clean(outPath), filepath.Clean(destPath)+string(os.PathSeparator)) {
continue
}
if f.FileInfo().IsDir() {
if err := os.MkdirAll(outPath, f.Mode()); err != nil {
return fmt.Errorf("cannot create directory %s: %w", outPath, err)
}
continue
}
// Create parent directories.
if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil {
return fmt.Errorf("cannot create parent directory: %w", err)
}
outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, f.Mode())
if err != nil {
return fmt.Errorf("cannot create file %s: %w", outPath, err)
}
rc, err := f.Open()
if err != nil {
outFile.Close()
return fmt.Errorf("cannot read ZIP entry %s: %w", f.Name, err)
}
if _, err := io.Copy(outFile, rc); err != nil {
rc.Close()
outFile.Close()
return fmt.Errorf("failed writing %s: %w", outPath, err)
}
rc.Close()
outFile.Close()
}
return nil
}
-7
View File
@@ -1,7 +0,0 @@
package version
var (
Version = "dev"
Commit = "unknown"
Date = "unknown"
)
+8 -8
View File
@@ -21,25 +21,25 @@
"test:all": "yarn test && yarn test:int && yarn lint:langgraph"
},
"dependencies": {
"@langchain/core": "^1.1.38",
"@langchain/langgraph": "^1.2.6"
"@langchain/core": "^1.1.42",
"@langchain/langgraph": "^1.2.9"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "^10.0.1",
"@tsconfig/recommended": "^1.0.13",
"@types/jest": "^30.0.0",
"@typescript-eslint/eslint-plugin": "^8.58.0",
"@typescript-eslint/parser": "^8.58.0",
"dotenv": "^17.3.1",
"eslint": "^10.1.0",
"@typescript-eslint/eslint-plugin": "^8.59.1",
"@typescript-eslint/parser": "^8.59.1",
"dotenv": "^17.4.2",
"eslint": "^10.2.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-no-instanceof": "^1.0.1",
"eslint-plugin-prettier": "^5.5.5",
"jest": "^30.3.0",
"prettier": "^3.8.1",
"ts-jest": "^29.4.6",
"prettier": "^3.8.3",
"ts-jest": "^29.4.9",
"typescript": "^5.9.3"
}
}
+130 -157
View File
@@ -474,26 +474,26 @@
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b"
integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
"@eslint/config-array@^0.23.3":
version "0.23.3"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.3.tgz#3f4a93dd546169c09130cbd10f2415b13a20a219"
integrity sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==
"@eslint/config-array@^0.23.5":
version "0.23.5"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.5.tgz#56e86d243049195d8acc0c06a1b3dfdc3fa3de95"
integrity sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==
dependencies:
"@eslint/object-schema" "^3.0.3"
"@eslint/object-schema" "^3.0.5"
debug "^4.3.1"
minimatch "^10.2.4"
"@eslint/config-helpers@^0.5.3":
version "0.5.3"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.3.tgz#721fe6bbb90d74b0c80d6ff2428e5bbcb002becb"
integrity sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==
"@eslint/config-helpers@^0.5.5":
version "0.5.5"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.5.tgz#ae16134e4792ac5fbdc533548a24ac1ea9f7f3ae"
integrity sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==
dependencies:
"@eslint/core" "^1.1.1"
"@eslint/core" "^1.2.1"
"@eslint/core@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.1.1.tgz#450f3d2be2d463ccd51119544092256b4e88df32"
integrity sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==
"@eslint/core@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.2.1.tgz#c1da7cd1b82fa8787f98b5629fb811848a1b63ce"
integrity sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==
dependencies:
"@types/json-schema" "^7.0.15"
@@ -517,17 +517,17 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-10.0.1.tgz#1e8a876f50117af8ab67e47d5ad94d38d6622583"
integrity sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==
"@eslint/object-schema@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.3.tgz#5bf671e52e382e4adc47a9906f2699374637db6b"
integrity sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==
"@eslint/object-schema@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.5.tgz#88e9bf4d11d2b19c082e78ebe7ce88724a5eb091"
integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==
"@eslint/plugin-kit@^0.6.1":
version "0.6.1"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz#eb9e6689b56ce8bc1855bb33090e63f3fc115e8e"
integrity sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==
"@eslint/plugin-kit@^0.7.1":
version "0.7.1"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz#c4125fd015eceeb09b793109fdbcd4dd0a02d346"
integrity sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==
dependencies:
"@eslint/core" "^1.1.1"
"@eslint/core" "^1.2.1"
levn "^0.4.1"
"@humanfs/core@^0.19.1":
@@ -890,10 +890,10 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@langchain/core@^1.1.38":
version "1.1.38"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.1.38.tgz#b5939b2e77af408771c9bb95bf56a5ad8183313e"
integrity sha512-C340wH1YL10CiVOFlEpQMp0zQE85/eBLKX/gi1Lv7shAyUmR3CQ0t/mXlCd5RsZa6ntAN1kDJnp64ArWey9XAA==
"@langchain/core@^1.1.42":
version "1.1.42"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.1.42.tgz#607bfd34c1414c9cbeb2859fda1b254a14c28085"
integrity sha512-d0tN96BrwPMryYyWR9VfyAntSivn7EQrZCe5Kpxum93tcjTXbKKmKvItFec8AluQt88iTcmAJrahUZUNfzGwTA==
dependencies:
"@cfworker/json-schema" "^4.0.2"
"@standard-schema/spec" "^1.1.0"
@@ -904,7 +904,6 @@
langsmith ">=0.5.0 <1.0.0"
mustache "^4.2.0"
p-queue "^6.6.2"
uuid "^11.1.0"
zod "^3.25.76 || ^4"
"@langchain/langgraph-checkpoint@^1.0.1":
@@ -914,23 +913,23 @@
dependencies:
uuid "^10.0.0"
"@langchain/langgraph-sdk@~1.8.1":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.8.3.tgz#deedc9bddd863693c6ab5f3a74058548b040377d"
integrity sha512-Py0S5yVtlOHz410aEsSGLRKjtsK2giDvfPS1JjAjTdcs71khuJufFtUZFwmwdJCbsG4DaGurRLHOAJu9jO4a0g==
"@langchain/langgraph-sdk@~1.8.9":
version "1.8.10"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.8.10.tgz#bd175cda1ab629845ef28b22b45f3bdd1f65b029"
integrity sha512-wrB3rkRw5KAmsqezwvKP3midT4qJrV6Hj9XJMYo+cbvXC4HYpSAmyY/VriSyeTFRbLG/OP/pY2Yz+9Z54nSaXQ==
dependencies:
"@types/json-schema" "^7.0.15"
p-queue "^9.0.1"
p-retry "^7.1.1"
uuid "^13.0.0"
"@langchain/langgraph@^1.2.6":
version "1.2.6"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.2.6.tgz#e6a578f96fbbc1c54ba78e0298884671dad5d195"
integrity sha512-5cX402dNGN6w9+0mlMU2dgUiKx6Y1tlENp7x05e9ByDbQCHSDc0kyqRWNFLGc7vatQ92S4ylxQrcCJvi8Fr4SQ==
"@langchain/langgraph@^1.2.9":
version "1.2.9"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.2.9.tgz#4f0ba159062c3649c83cf5f621ef926b96933e32"
integrity sha512-3c7BtGycHC2v9p6w/Hv8L7kEl1YnZYOQTDJtmAp3knk6JOedO7d2bYP3y0SRyhv5orUEGf/KGvx8ZsB/ideP7g==
dependencies:
"@langchain/langgraph-checkpoint" "^1.0.1"
"@langchain/langgraph-sdk" "~1.8.1"
"@langchain/langgraph-sdk" "~1.8.9"
"@standard-schema/spec" "1.1.0"
uuid "^10.0.0"
@@ -1086,11 +1085,6 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
"@types/uuid@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d"
integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==
"@types/yargs-parser@*":
version "21.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
@@ -1103,100 +1097,100 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz#ad40e492f1931f46da1bd888e52b9e56df9063aa"
integrity sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==
"@typescript-eslint/eslint-plugin@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz#781bc6f9002982cfaf75a185240e24ad7276628a"
integrity sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==
dependencies:
"@eslint-community/regexpp" "^4.12.2"
"@typescript-eslint/scope-manager" "8.58.0"
"@typescript-eslint/type-utils" "8.58.0"
"@typescript-eslint/utils" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/scope-manager" "8.59.1"
"@typescript-eslint/type-utils" "8.59.1"
"@typescript-eslint/utils" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
ignore "^7.0.5"
natural-compare "^1.4.0"
ts-api-utils "^2.5.0"
"@typescript-eslint/parser@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.58.0.tgz#da04ece1967b6c2fe8f10c3473dabf3825795ef7"
integrity sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==
"@typescript-eslint/parser@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.59.1.tgz#835d20a62350659a082a1ae2a60b822c40488905"
integrity sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==
dependencies:
"@typescript-eslint/scope-manager" "8.58.0"
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/typescript-estree" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/scope-manager" "8.59.1"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/typescript-estree" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
debug "^4.4.3"
"@typescript-eslint/project-service@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.58.0.tgz#66ceda0aabf7427aec3e2713fa43eb278dead2aa"
integrity sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==
"@typescript-eslint/project-service@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.59.1.tgz#49efe87c37ef84262f23df8bf62fdc56698ca6fe"
integrity sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.58.0"
"@typescript-eslint/types" "^8.58.0"
"@typescript-eslint/tsconfig-utils" "^8.59.1"
"@typescript-eslint/types" "^8.59.1"
debug "^4.4.3"
"@typescript-eslint/scope-manager@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz#e304142775e49a1b7ac3c8bf2536714447c72cab"
integrity sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==
"@typescript-eslint/scope-manager@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz#ed90d054fc3db2d0c81464db3a953a94fb85bb58"
integrity sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==
dependencies:
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
"@typescript-eslint/tsconfig-utils@8.58.0", "@typescript-eslint/tsconfig-utils@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz#c5a8edb21f31e0fdee565724e1b984171c559482"
integrity sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==
"@typescript-eslint/tsconfig-utils@8.59.1", "@typescript-eslint/tsconfig-utils@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz#ba2a779a444f1d5cb92a606f9b209d239fd4cab1"
integrity sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==
"@typescript-eslint/type-utils@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz#ce0e72cd967ffbbe8de322db6089bd4374be352f"
integrity sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==
"@typescript-eslint/type-utils@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz#9c83d3f2ed9187a815e8120f72c08317e513e409"
integrity sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==
dependencies:
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/typescript-estree" "8.58.0"
"@typescript-eslint/utils" "8.58.0"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/typescript-estree" "8.59.1"
"@typescript-eslint/utils" "8.59.1"
debug "^4.4.3"
ts-api-utils "^2.5.0"
"@typescript-eslint/types@8.58.0", "@typescript-eslint/types@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.58.0.tgz#e94ae7abdc1c6530e71183c1007b61fa93112a5a"
integrity sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==
"@typescript-eslint/types@8.59.1", "@typescript-eslint/types@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.59.1.tgz#c1d014d3f03a97e0113a8899fc9d4e45a7fb0ca9"
integrity sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==
"@typescript-eslint/typescript-estree@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz#ed233faa8e2f2a2e1357c3e7d553d6465a0ee59a"
integrity sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==
"@typescript-eslint/typescript-estree@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz#4391fadf98a22c869c5b6522dbf4e491e53e351a"
integrity sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==
dependencies:
"@typescript-eslint/project-service" "8.58.0"
"@typescript-eslint/tsconfig-utils" "8.58.0"
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/project-service" "8.59.1"
"@typescript-eslint/tsconfig-utils" "8.59.1"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
debug "^4.4.3"
minimatch "^10.2.2"
semver "^7.7.3"
tinyglobby "^0.2.15"
ts-api-utils "^2.5.0"
"@typescript-eslint/utils@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.58.0.tgz#21a74a7963b0d288b719a4121c7dd555adaab3c3"
integrity sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==
"@typescript-eslint/utils@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.59.1.tgz#cf6204d69701bbbc5b150f98c18aeef0a42c10bd"
integrity sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==
dependencies:
"@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.58.0"
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/typescript-estree" "8.58.0"
"@typescript-eslint/scope-manager" "8.59.1"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/typescript-estree" "8.59.1"
"@typescript-eslint/visitor-keys@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz#2abd55a4be70fd55967aceaba4330b9ba9f45189"
integrity sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==
"@typescript-eslint/visitor-keys@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz#b5cba576287a3eeb0b400b62813189abcc3f976a"
integrity sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==
dependencies:
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/types" "8.59.1"
eslint-visitor-keys "^5.0.0"
"@ungap/structured-clone@^1.3.0":
@@ -1782,13 +1776,6 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
console-table-printer@^2.12.1:
version "2.15.0"
resolved "https://registry.yarnpkg.com/console-table-printer/-/console-table-printer-2.15.0.tgz#5c808204640b8f024d545bde8aabe5d344dfadc1"
integrity sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==
dependencies:
simple-wcswidth "^1.1.2"
convert-source-map@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
@@ -1921,10 +1908,10 @@ doctrine@^2.1.0:
dependencies:
esutils "^2.0.2"
dotenv@^17.3.1:
version "17.3.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.3.1.tgz#2706f5b0165e45a1503348187b8468f87fe6aae2"
integrity sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==
dotenv@^17.4.2:
version "17.4.2"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.4.2.tgz#c07e54a746e11eba021dd9e1047ced5afdc1c034"
integrity sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==
dunder-proto@^1.0.0, dunder-proto@^1.0.1:
version "1.0.1"
@@ -2275,17 +2262,17 @@ eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be"
integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==
eslint@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.1.0.tgz#9ca98e654e642ab2e1af6d1e9d8613857ac341b4"
integrity sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==
eslint@^10.2.1:
version "10.2.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.2.1.tgz#224b2a6caeb34473eddcf918762363e2e063222a"
integrity sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==
dependencies:
"@eslint-community/eslint-utils" "^4.8.0"
"@eslint-community/regexpp" "^4.12.2"
"@eslint/config-array" "^0.23.3"
"@eslint/config-helpers" "^0.5.3"
"@eslint/core" "^1.1.1"
"@eslint/plugin-kit" "^0.6.1"
"@eslint/config-array" "^0.23.5"
"@eslint/config-helpers" "^0.5.5"
"@eslint/core" "^1.2.1"
"@eslint/plugin-kit" "^0.7.1"
"@humanfs/node" "^0.16.6"
"@humanwhocodes/module-importer" "^1.0.1"
"@humanwhocodes/retry" "^0.4.2"
@@ -2695,7 +2682,7 @@ graceful-fs@^4.2.11:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
handlebars@^4.7.8:
handlebars@^4.7.9:
version "4.7.9"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.9.tgz#6f139082ab58dc4e5a0e51efe7db5ae890d56a0f"
integrity sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==
@@ -3688,16 +3675,12 @@ keyv@^4.5.4:
json-buffer "3.0.1"
"langsmith@>=0.5.0 <1.0.0":
version "0.5.4"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.5.4.tgz#f75b82b08e30db72a7d1d595b341e9666bd525e5"
integrity sha512-qYkNIoKpf0ZYt+cYzrDV+XI3FCexApmZmp8EMs3eDTMv0OvrHMLoxJ9IpkeoXJSX24+GPk0/jXjKx2hWerpy9w==
version "0.5.20"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.5.20.tgz#4021847d2ccd5a86c5eb96060f9bb5f19f80eca5"
integrity sha512-ULhLM8RswvQDXufLtNtvclHrWCBx8Cb5UPI6lAZC+8Dq59iHsVPz/3Ac9khWNm1VIvChRsuykixD/WrmzuuA3Q==
dependencies:
"@types/uuid" "^10.0.0"
chalk "^4.1.2"
console-table-printer "^2.12.1"
p-queue "^6.6.2"
semver "^7.6.3"
uuid "^10.0.0"
p-queue "6.6.2"
uuid "10.0.0"
leven@^3.1.0:
version "3.1.0"
@@ -4007,7 +3990,7 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
p-queue@^6.6.2:
p-queue@6.6.2, p-queue@^6.6.2:
version "6.6.2"
resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
@@ -4146,10 +4129,10 @@ prettier-linter-helpers@^1.0.1:
dependencies:
fast-diff "^1.1.2"
prettier@^3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173"
integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==
prettier@^3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0"
integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==
pretty-format@30.2.0, pretty-format@^30.0.0:
version "30.2.0"
@@ -4303,7 +4286,7 @@ semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.5.3, semver@^7.5.4, semver@^7.6.3, semver@^7.7.2, semver@^7.7.3:
semver@^7.5.3, semver@^7.5.4, semver@^7.7.2, semver@^7.7.3, semver@^7.7.4:
version "7.7.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a"
integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==
@@ -4411,11 +4394,6 @@ signal-exit@^4.0.1:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
simple-wcswidth@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz#66722f37629d5203f9b47c5477b1225b85d6525b"
integrity sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@@ -4644,18 +4622,18 @@ ts-api-utils@^2.5.0:
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1"
integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==
ts-jest@^29.4.6:
version "29.4.6"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.6.tgz#51cb7c133f227396818b71297ad7409bb77106e9"
integrity sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==
ts-jest@^29.4.9:
version "29.4.9"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.9.tgz#47dc33d0f5c36bddcedd16afefae285e0b049d2d"
integrity sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==
dependencies:
bs-logger "^0.2.6"
fast-json-stable-stringify "^2.1.0"
handlebars "^4.7.8"
handlebars "^4.7.9"
json5 "^2.2.3"
lodash.memoize "^4.1.2"
make-error "^1.3.6"
semver "^7.7.3"
semver "^7.7.4"
type-fest "^4.41.0"
yargs-parser "^21.1.1"
@@ -4870,16 +4848,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
uuid@^10.0.0:
uuid@10.0.0, uuid@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==
uuid@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912"
integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==
uuid@^13.0.0:
version "13.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-13.0.0.tgz#263dc341b19b4d755eb8fe36b78d95a6b65707e8"
@@ -9,8 +9,8 @@
},
"dependencies": {
"@js-monorepo-example/shared": "*",
"@langchain/core": "^1.1.38",
"@langchain/langgraph": "^1.2.6"
"@langchain/core": "^1.1.42",
"@langchain/langgraph": "^1.2.9"
},
"devDependencies": {
"typescript": "^5.9.3"
+5 -5
View File
@@ -17,18 +17,18 @@
"lint": "eslint 'apps/**/*.ts' 'libs/**/*.ts'"
},
"devDependencies": {
"turbo": "^2.9.3",
"turbo": "^2.9.7",
"typescript": "^5.9.3",
"@tsconfig/recommended": "^1.0.13",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "^10.0.1",
"eslint": "^10.1.0",
"eslint": "^10.2.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-no-instanceof": "^1.0.1",
"eslint-plugin-prettier": "^5.5.5",
"@typescript-eslint/eslint-plugin": "^8.58.0",
"@typescript-eslint/parser": "^8.58.0",
"prettier": "^3.8.1"
"@typescript-eslint/eslint-plugin": "^8.59.1",
"@typescript-eslint/parser": "^8.59.1",
"prettier": "^3.8.3"
}
}
+152 -223
View File
@@ -19,26 +19,26 @@
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b"
integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
"@eslint/config-array@^0.23.3":
version "0.23.3"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.3.tgz#3f4a93dd546169c09130cbd10f2415b13a20a219"
integrity sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==
"@eslint/config-array@^0.23.5":
version "0.23.5"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.5.tgz#56e86d243049195d8acc0c06a1b3dfdc3fa3de95"
integrity sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==
dependencies:
"@eslint/object-schema" "^3.0.3"
"@eslint/object-schema" "^3.0.5"
debug "^4.3.1"
minimatch "^10.2.4"
"@eslint/config-helpers@^0.5.3":
version "0.5.3"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.3.tgz#721fe6bbb90d74b0c80d6ff2428e5bbcb002becb"
integrity sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==
"@eslint/config-helpers@^0.5.5":
version "0.5.5"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.5.tgz#ae16134e4792ac5fbdc533548a24ac1ea9f7f3ae"
integrity sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==
dependencies:
"@eslint/core" "^1.1.1"
"@eslint/core" "^1.2.1"
"@eslint/core@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.1.1.tgz#450f3d2be2d463ccd51119544092256b4e88df32"
integrity sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==
"@eslint/core@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.2.1.tgz#c1da7cd1b82fa8787f98b5629fb811848a1b63ce"
integrity sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==
dependencies:
"@types/json-schema" "^7.0.15"
@@ -62,17 +62,17 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-10.0.1.tgz#1e8a876f50117af8ab67e47d5ad94d38d6622583"
integrity sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==
"@eslint/object-schema@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.3.tgz#5bf671e52e382e4adc47a9906f2699374637db6b"
integrity sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==
"@eslint/object-schema@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.5.tgz#88e9bf4d11d2b19c082e78ebe7ce88724a5eb091"
integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==
"@eslint/plugin-kit@^0.6.1":
version "0.6.1"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz#eb9e6689b56ce8bc1855bb33090e63f3fc115e8e"
integrity sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==
"@eslint/plugin-kit@^0.7.1":
version "0.7.1"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz#c4125fd015eceeb09b793109fdbcd4dd0a02d346"
integrity sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==
dependencies:
"@eslint/core" "^1.1.1"
"@eslint/core" "^1.2.1"
levn "^0.4.1"
"@humanfs/core@^0.19.1":
@@ -103,10 +103,10 @@
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-9.0.0.tgz#4d0a3f127058043bf2e7ee169eaf30ed901302f3"
integrity sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==
"@langchain/core@^1.1.38":
version "1.1.38"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.1.38.tgz#b5939b2e77af408771c9bb95bf56a5ad8183313e"
integrity sha512-C340wH1YL10CiVOFlEpQMp0zQE85/eBLKX/gi1Lv7shAyUmR3CQ0t/mXlCd5RsZa6ntAN1kDJnp64ArWey9XAA==
"@langchain/core@^1.1.42":
version "1.1.42"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.1.42.tgz#607bfd34c1414c9cbeb2859fda1b254a14c28085"
integrity sha512-d0tN96BrwPMryYyWR9VfyAntSivn7EQrZCe5Kpxum93tcjTXbKKmKvItFec8AluQt88iTcmAJrahUZUNfzGwTA==
dependencies:
"@cfworker/json-schema" "^4.0.2"
"@standard-schema/spec" "^1.1.0"
@@ -117,7 +117,6 @@
langsmith ">=0.5.0 <1.0.0"
mustache "^4.2.0"
p-queue "^6.6.2"
uuid "^11.1.0"
zod "^3.25.76 || ^4"
"@langchain/langgraph-checkpoint@^1.0.1":
@@ -127,23 +126,23 @@
dependencies:
uuid "^10.0.0"
"@langchain/langgraph-sdk@~1.8.1":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.8.3.tgz#deedc9bddd863693c6ab5f3a74058548b040377d"
integrity sha512-Py0S5yVtlOHz410aEsSGLRKjtsK2giDvfPS1JjAjTdcs71khuJufFtUZFwmwdJCbsG4DaGurRLHOAJu9jO4a0g==
"@langchain/langgraph-sdk@~1.8.9":
version "1.8.10"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.8.10.tgz#bd175cda1ab629845ef28b22b45f3bdd1f65b029"
integrity sha512-wrB3rkRw5KAmsqezwvKP3midT4qJrV6Hj9XJMYo+cbvXC4HYpSAmyY/VriSyeTFRbLG/OP/pY2Yz+9Z54nSaXQ==
dependencies:
"@types/json-schema" "^7.0.15"
p-queue "^9.0.1"
p-retry "^7.1.1"
uuid "^13.0.0"
"@langchain/langgraph@^1.2.6":
version "1.2.6"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.2.6.tgz#e6a578f96fbbc1c54ba78e0298884671dad5d195"
integrity sha512-5cX402dNGN6w9+0mlMU2dgUiKx6Y1tlENp7x05e9ByDbQCHSDc0kyqRWNFLGc7vatQ92S4ylxQrcCJvi8Fr4SQ==
"@langchain/langgraph@^1.2.9":
version "1.2.9"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.2.9.tgz#4f0ba159062c3649c83cf5f621ef926b96933e32"
integrity sha512-3c7BtGycHC2v9p6w/Hv8L7kEl1YnZYOQTDJtmAp3knk6JOedO7d2bYP3y0SRyhv5orUEGf/KGvx8ZsB/ideP7g==
dependencies:
"@langchain/langgraph-checkpoint" "^1.0.1"
"@langchain/langgraph-sdk" "~1.8.1"
"@langchain/langgraph-sdk" "~1.8.9"
"@standard-schema/spec" "1.1.0"
uuid "^10.0.0"
@@ -167,35 +166,35 @@
resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.13.tgz#269fce3ad04ca70b93269ff44cca81b950f542da"
integrity sha512-sySRuBfMKyKO/j2ZAhR8kSembhjuPEV4Ra3AHtmWLq51+iGaudr45crPSzNC5b7/Ctrh9dfUpBuTlYrH6rM58Q==
"@turbo/darwin-64@2.9.3":
version "2.9.3"
resolved "https://registry.yarnpkg.com/@turbo/darwin-64/-/darwin-64-2.9.3.tgz#5d782d341ddebc20822d09ba1393238d4467ae7b"
integrity sha512-P8foouaP+y/p+hhEGBoZpzMbpVvUMwPjDpcy6wN7EYfvvyISD1USuV27qWkczecihwuPJzQ1lDBuL8ERcavTyg==
"@turbo/darwin-64@2.9.7":
version "2.9.7"
resolved "https://registry.yarnpkg.com/@turbo/darwin-64/-/darwin-64-2.9.7.tgz#46fddae01ea7817192dc6ff36a678cc3879b753b"
integrity sha512-wnvOWuVWJ5EUHNKxExEWiGlTeVpLG1L0PCu5MUozyC1P2SHGiWsmpW6/yAuShH91Fa2TAHOvdCRBzriZh4j4Eg==
"@turbo/darwin-arm64@2.9.3":
version "2.9.3"
resolved "https://registry.yarnpkg.com/@turbo/darwin-arm64/-/darwin-arm64-2.9.3.tgz#75cd662d9c2a572532ce82d7cae1a30e2497d693"
integrity sha512-SIzEkvtNdzdI50FJDaIQ6kQGqgSSdFPcdn0wqmmONN6iGKjy6hsT+EH99GP65FsfV7DLZTh2NmtTIRl2kdoz5Q==
"@turbo/darwin-arm64@2.9.7":
version "2.9.7"
resolved "https://registry.yarnpkg.com/@turbo/darwin-arm64/-/darwin-arm64-2.9.7.tgz#cd7cf3a024509af0f59ae3885216d62fd090fdb6"
integrity sha512-mA0FIPMwwN3lodDkQYaGxj6PeT7ZaN5aCEbkKn/WB+ZB9yJdVWA4J83GH7t43jqDc5dcnVluVN5UFx3plRiXhA==
"@turbo/linux-64@2.9.3":
version "2.9.3"
resolved "https://registry.yarnpkg.com/@turbo/linux-64/-/linux-64-2.9.3.tgz#de693a996071654a0bea51c7ed9dc6fdc3950ae8"
integrity sha512-pLRwFmcHHNBvsCySLS6OFabr/07kDT2pxEt/k6eBf/3asiVQZKJ7Rk88AafQx2aYA641qek4RsXvYO3JYpiBug==
"@turbo/linux-64@2.9.7":
version "2.9.7"
resolved "https://registry.yarnpkg.com/@turbo/linux-64/-/linux-64-2.9.7.tgz#25ee9a2cb3042498a3203e14653b14041105020f"
integrity sha512-fEbUYpgb5l7P+q+5tsWF2gw+/GSjUsuUTcnfm+f0lozUjgcjLKyOat6PgtAChmIFcTPchCL/8rJ3TvkBy01gfA==
"@turbo/linux-arm64@2.9.3":
version "2.9.3"
resolved "https://registry.yarnpkg.com/@turbo/linux-arm64/-/linux-arm64-2.9.3.tgz#88fc6fe572d83c5488eeae9b564b57c335f4d856"
integrity sha512-gy6ApUroC2Nzv+qjGtE/uPNkhHAFU4c8God+zd5Aiv9L9uBgHlxVJpHT3XWl5xwlJZ2KWuMrlHTaS5kmNB+q1Q==
"@turbo/linux-arm64@2.9.7":
version "2.9.7"
resolved "https://registry.yarnpkg.com/@turbo/linux-arm64/-/linux-arm64-2.9.7.tgz#5b19d6249a50cb8153b8f2bc22ad10bbda614fbe"
integrity sha512-VkUjulo9ytfHKUHOS5gy0XPoh4CTKPXWCL8nLdrlHVi9fSut31ECeUqnm/dAbETP5D4xo9mH9XkJ+qMzGe/zmg==
"@turbo/windows-64@2.9.3":
version "2.9.3"
resolved "https://registry.yarnpkg.com/@turbo/windows-64/-/windows-64-2.9.3.tgz#a7812e4f33972f74daed93b2ac8845f88a68c775"
integrity sha512-d0YelTX6hAsB7kIEtGB3PzIzSfAg3yDoUlHwuwJc3adBXUsyUIs0YLG+1NNtuhcDOUGnWQeKUoJ2pGWvbpRj7w==
"@turbo/windows-64@2.9.7":
version "2.9.7"
resolved "https://registry.yarnpkg.com/@turbo/windows-64/-/windows-64-2.9.7.tgz#900645776fa44ff8333e801d890992736f6dbc72"
integrity sha512-/GWdY6/x4aIHqkYJq596Rpdk1x0MkpRPkJcLAoB3yGRwyUms0+u2F1GnV54IbyAZTeKLRWSJKzNC+QwVGdYchA==
"@turbo/windows-arm64@2.9.3":
version "2.9.3"
resolved "https://registry.yarnpkg.com/@turbo/windows-arm64/-/windows-arm64-2.9.3.tgz#1891a89fe87d501e63a7a4367082f3afec18fa6f"
integrity sha512-/08CwpKJl3oRY8nOlh2YgilZVJDHsr60XTNxRhuDeuFXONpUZ5X+Nv65izbG/xBew9qxcJFbDX9/sAmAX+ITcQ==
"@turbo/windows-arm64@2.9.7":
version "2.9.7"
resolved "https://registry.yarnpkg.com/@turbo/windows-arm64/-/windows-arm64-2.9.7.tgz#e678d73fdbcbb12a679403fe7ba070fb67f37810"
integrity sha512-xBBgxCC5PK2+WZ1PPRZdp+aJ0bMBcEbweXWux3RUHJvX9ZodcoQySkrW6qt+ahb+uk8ZjyQodLfDwtVSoYds1w==
"@types/esrecurse@^4.3.1":
version "4.3.1"
@@ -217,105 +216,100 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/uuid@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d"
integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==
"@typescript-eslint/eslint-plugin@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz#ad40e492f1931f46da1bd888e52b9e56df9063aa"
integrity sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==
"@typescript-eslint/eslint-plugin@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz#781bc6f9002982cfaf75a185240e24ad7276628a"
integrity sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==
dependencies:
"@eslint-community/regexpp" "^4.12.2"
"@typescript-eslint/scope-manager" "8.58.0"
"@typescript-eslint/type-utils" "8.58.0"
"@typescript-eslint/utils" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/scope-manager" "8.59.1"
"@typescript-eslint/type-utils" "8.59.1"
"@typescript-eslint/utils" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
ignore "^7.0.5"
natural-compare "^1.4.0"
ts-api-utils "^2.5.0"
"@typescript-eslint/parser@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.58.0.tgz#da04ece1967b6c2fe8f10c3473dabf3825795ef7"
integrity sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==
"@typescript-eslint/parser@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.59.1.tgz#835d20a62350659a082a1ae2a60b822c40488905"
integrity sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==
dependencies:
"@typescript-eslint/scope-manager" "8.58.0"
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/typescript-estree" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/scope-manager" "8.59.1"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/typescript-estree" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
debug "^4.4.3"
"@typescript-eslint/project-service@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.58.0.tgz#66ceda0aabf7427aec3e2713fa43eb278dead2aa"
integrity sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==
"@typescript-eslint/project-service@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.59.1.tgz#49efe87c37ef84262f23df8bf62fdc56698ca6fe"
integrity sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.58.0"
"@typescript-eslint/types" "^8.58.0"
"@typescript-eslint/tsconfig-utils" "^8.59.1"
"@typescript-eslint/types" "^8.59.1"
debug "^4.4.3"
"@typescript-eslint/scope-manager@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz#e304142775e49a1b7ac3c8bf2536714447c72cab"
integrity sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==
"@typescript-eslint/scope-manager@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz#ed90d054fc3db2d0c81464db3a953a94fb85bb58"
integrity sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==
dependencies:
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
"@typescript-eslint/tsconfig-utils@8.58.0", "@typescript-eslint/tsconfig-utils@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz#c5a8edb21f31e0fdee565724e1b984171c559482"
integrity sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==
"@typescript-eslint/tsconfig-utils@8.59.1", "@typescript-eslint/tsconfig-utils@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz#ba2a779a444f1d5cb92a606f9b209d239fd4cab1"
integrity sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==
"@typescript-eslint/type-utils@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz#ce0e72cd967ffbbe8de322db6089bd4374be352f"
integrity sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==
"@typescript-eslint/type-utils@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz#9c83d3f2ed9187a815e8120f72c08317e513e409"
integrity sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==
dependencies:
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/typescript-estree" "8.58.0"
"@typescript-eslint/utils" "8.58.0"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/typescript-estree" "8.59.1"
"@typescript-eslint/utils" "8.59.1"
debug "^4.4.3"
ts-api-utils "^2.5.0"
"@typescript-eslint/types@8.58.0", "@typescript-eslint/types@^8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.58.0.tgz#e94ae7abdc1c6530e71183c1007b61fa93112a5a"
integrity sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==
"@typescript-eslint/types@8.59.1", "@typescript-eslint/types@^8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.59.1.tgz#c1d014d3f03a97e0113a8899fc9d4e45a7fb0ca9"
integrity sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==
"@typescript-eslint/typescript-estree@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz#ed233faa8e2f2a2e1357c3e7d553d6465a0ee59a"
integrity sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==
"@typescript-eslint/typescript-estree@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz#4391fadf98a22c869c5b6522dbf4e491e53e351a"
integrity sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==
dependencies:
"@typescript-eslint/project-service" "8.58.0"
"@typescript-eslint/tsconfig-utils" "8.58.0"
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/visitor-keys" "8.58.0"
"@typescript-eslint/project-service" "8.59.1"
"@typescript-eslint/tsconfig-utils" "8.59.1"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/visitor-keys" "8.59.1"
debug "^4.4.3"
minimatch "^10.2.2"
semver "^7.7.3"
tinyglobby "^0.2.15"
ts-api-utils "^2.5.0"
"@typescript-eslint/utils@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.58.0.tgz#21a74a7963b0d288b719a4121c7dd555adaab3c3"
integrity sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==
"@typescript-eslint/utils@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.59.1.tgz#cf6204d69701bbbc5b150f98c18aeef0a42c10bd"
integrity sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==
dependencies:
"@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.58.0"
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/typescript-estree" "8.58.0"
"@typescript-eslint/scope-manager" "8.59.1"
"@typescript-eslint/types" "8.59.1"
"@typescript-eslint/typescript-estree" "8.59.1"
"@typescript-eslint/visitor-keys@8.58.0":
version "8.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz#2abd55a4be70fd55967aceaba4330b9ba9f45189"
integrity sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==
"@typescript-eslint/visitor-keys@8.59.1":
version "8.59.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz#b5cba576287a3eeb0b400b62813189abcc3f976a"
integrity sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==
dependencies:
"@typescript-eslint/types" "8.58.0"
"@typescript-eslint/types" "8.59.1"
eslint-visitor-keys "^5.0.0"
acorn-jsx@^5.3.2:
@@ -343,13 +337,6 @@ ajv@^6.14.0:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
ansi-styles@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
@@ -508,38 +495,11 @@ camelcase@6:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
console-table-printer@^2.12.1:
version "2.14.6"
resolved "https://registry.yarnpkg.com/console-table-printer/-/console-table-printer-2.14.6.tgz#edfe0bf311fa2701922ed509443145ab51e06436"
integrity sha512-MCBl5HNVaFuuHW6FGbL/4fB7N/ormCy+tQ+sxTrF6QtSbSNETvPuOVbkJBhzDgYhvjWGrTma4eYJa37ZuoQsPw==
dependencies:
simple-wcswidth "^1.0.1"
cross-spawn@^7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
@@ -826,17 +786,17 @@ eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be"
integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==
eslint@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.1.0.tgz#9ca98e654e642ab2e1af6d1e9d8613857ac341b4"
integrity sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==
eslint@^10.2.1:
version "10.2.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.2.1.tgz#224b2a6caeb34473eddcf918762363e2e063222a"
integrity sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==
dependencies:
"@eslint-community/eslint-utils" "^4.8.0"
"@eslint-community/regexpp" "^4.12.2"
"@eslint/config-array" "^0.23.3"
"@eslint/config-helpers" "^0.5.3"
"@eslint/core" "^1.1.1"
"@eslint/plugin-kit" "^0.6.1"
"@eslint/config-array" "^0.23.5"
"@eslint/config-helpers" "^0.5.5"
"@eslint/core" "^1.2.1"
"@eslint/plugin-kit" "^0.7.1"
"@humanfs/node" "^0.16.6"
"@humanwhocodes/module-importer" "^1.0.1"
"@humanwhocodes/retry" "^0.4.2"
@@ -1059,11 +1019,6 @@ has-bigints@^1.0.2:
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe"
integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
@@ -1372,16 +1327,12 @@ keyv@^4.5.4:
json-buffer "3.0.1"
"langsmith@>=0.5.0 <1.0.0":
version "0.5.4"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.5.4.tgz#f75b82b08e30db72a7d1d595b341e9666bd525e5"
integrity sha512-qYkNIoKpf0ZYt+cYzrDV+XI3FCexApmZmp8EMs3eDTMv0OvrHMLoxJ9IpkeoXJSX24+GPk0/jXjKx2hWerpy9w==
version "0.5.20"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.5.20.tgz#4021847d2ccd5a86c5eb96060f9bb5f19f80eca5"
integrity sha512-ULhLM8RswvQDXufLtNtvclHrWCBx8Cb5UPI6lAZC+8Dq59iHsVPz/3Ac9khWNm1VIvChRsuykixD/WrmzuuA3Q==
dependencies:
"@types/uuid" "^10.0.0"
chalk "^4.1.2"
console-table-printer "^2.12.1"
p-queue "^6.6.2"
semver "^7.6.3"
uuid "^10.0.0"
p-queue "6.6.2"
uuid "10.0.0"
levn@^0.4.1:
version "0.4.1"
@@ -1528,7 +1479,7 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
p-queue@^6.6.2:
p-queue@6.6.2, p-queue@^6.6.2:
version "6.6.2"
resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
@@ -1607,10 +1558,10 @@ prettier-linter-helpers@^1.0.1:
dependencies:
fast-diff "^1.1.2"
prettier@^3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173"
integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==
prettier@^3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0"
integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==
punycode@^2.1.0:
version "2.3.1"
@@ -1690,11 +1641,6 @@ semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.6.3:
version "7.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
semver@^7.7.3:
version "7.7.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a"
@@ -1783,11 +1729,6 @@ side-channel@^1.1.0:
side-channel-map "^1.0.1"
side-channel-weakmap "^1.0.2"
simple-wcswidth@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz#66722f37629d5203f9b47c5477b1225b85d6525b"
integrity sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==
stop-iteration-iterator@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad"
@@ -1838,13 +1779,6 @@ strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
@@ -1880,17 +1814,17 @@ tsconfig-paths@^3.15.0:
minimist "^1.2.6"
strip-bom "^3.0.0"
turbo@^2.9.3:
version "2.9.3"
resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.9.3.tgz#4f0adf2c8677edd7b46d400629c99e9e26e58e44"
integrity sha512-J/VUvsGRykPb9R8Kh8dHVBOqioDexLk9BhLCU/ZybRR+HN9UR3cURdazFvNgMDt9zPP8TF6K73Z+tplfmi0PqQ==
turbo@^2.9.7:
version "2.9.7"
resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.9.7.tgz#da6d5a821f0bde5174e1a55af6690dad5c2fb004"
integrity sha512-epxzqVO2s0IxcSWcgb+qKrtco8isfe7g3VtiS6hkYnEK4A9XQDZbrtavQ6MtWR1KoQn+1fUomaQth2rfRHlUlg==
optionalDependencies:
"@turbo/darwin-64" "2.9.3"
"@turbo/darwin-arm64" "2.9.3"
"@turbo/linux-64" "2.9.3"
"@turbo/linux-arm64" "2.9.3"
"@turbo/windows-64" "2.9.3"
"@turbo/windows-arm64" "2.9.3"
"@turbo/darwin-64" "2.9.7"
"@turbo/darwin-arm64" "2.9.7"
"@turbo/linux-64" "2.9.7"
"@turbo/linux-arm64" "2.9.7"
"@turbo/windows-64" "2.9.7"
"@turbo/windows-arm64" "2.9.7"
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
@@ -1966,16 +1900,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
uuid@^10.0.0:
uuid@10.0.0, uuid@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==
uuid@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912"
integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==
uuid@^13.0.0:
version "13.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-13.0.0.tgz#263dc341b19b4d755eb8fe36b78d95a6b65707e8"
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.4.21"
__version__ = "0.4.26"
+2 -2
View File
@@ -1,4 +1,4 @@
from .entrypoint import main
from .cli import cli
if __name__ == "__main__":
main()
cli()
+124
View File
@@ -0,0 +1,124 @@
"""Shared ignore-file handling for local source filtering."""
import pathlib
from dataclasses import dataclass
import pathspec
_ALWAYS_EXCLUDE = [
"__pycache__/",
".git/",
".venv/",
"venv/",
"node_modules/",
".tox/",
".mypy_cache/",
]
_ALWAYS_EXCLUDE_NAMES = frozenset(
pattern.rstrip("/").split("/")[-1] for pattern in _ALWAYS_EXCLUDE
)
_GLOB_CHARS = frozenset("*?[")
@dataclass(frozen=True, slots=True)
class _NegatedDockerignoreHints:
exact_dirs: frozenset[pathlib.PurePosixPath] = frozenset()
wildcard_prefixes: frozenset[pathlib.PurePosixPath] = frozenset()
recurse_all: bool = False
def requires_dir_walk(self, path: pathlib.PurePosixPath) -> bool:
if self.recurse_all or path in self.exact_dirs:
return True
return any(
path == prefix or path in prefix.parents or prefix in path.parents
for prefix in self.wildcard_prefixes
)
def _build_ignore_spec(
directory: pathlib.Path, *, include_gitignore: bool = True
) -> pathspec.PathSpec:
"""Build a PathSpec combining built-in exclusions with ignore files.
Always excludes common non-source directories (`_ALWAYS_EXCLUDE`). On top
of that, patterns from `.dockerignore` are merged in. `.gitignore` patterns
are optional because some callers need Docker build-context semantics,
while archive creation wants both files.
"""
lines: list[str] = list(_ALWAYS_EXCLUDE)
ignore_files = [".dockerignore"]
if include_gitignore:
ignore_files.append(".gitignore")
for name in ignore_files:
ignore_file = directory / name
if ignore_file.is_file():
lines.extend(ignore_file.read_text(encoding="utf-8").splitlines())
return pathspec.PathSpec.from_lines("gitwildmatch", lines)
def _is_always_excluded(path: pathlib.PurePosixPath, *, is_dir: bool) -> bool:
"""Whether `path` lives inside a built-in excluded directory."""
parent_parts = path.parts if is_dir else path.parts[:-1]
return any(part in _ALWAYS_EXCLUDE_NAMES for part in parent_parts)
def _build_dockerignore_negation_hints(
directory: pathlib.Path,
) -> _NegatedDockerignoreHints:
"""Summarize which ignored directories must still be traversed.
Most negations only require walking a small, concrete chain of parent
directories (for example `!assets/keep.txt` requires entering `assets/`).
Broader glob negations may force a wider walk.
"""
ignore_file = directory / ".dockerignore"
if not ignore_file.is_file():
return _NegatedDockerignoreHints()
exact_dirs: set[pathlib.PurePosixPath] = set()
wildcard_prefixes: set[pathlib.PurePosixPath] = set()
recurse_all = False
for raw_line in ignore_file.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#") or line.startswith("\\!"):
continue
if line.startswith("\\#"):
line = line[1:]
if not line.startswith("!"):
continue
pattern = line[1:].lstrip("/")
while pattern.startswith("./"):
pattern = pattern[2:]
pattern = pattern.rstrip("/")
parts = [part for part in pattern.split("/") if part and part != "."]
if not parts:
recurse_all = True
continue
wildcard_index = next(
(
idx
for idx, part in enumerate(parts)
if any(char in part for char in _GLOB_CHARS)
),
None,
)
if wildcard_index is not None:
literal_parts = parts[:wildcard_index]
if not literal_parts:
recurse_all = True
continue
wildcard_prefixes.add(pathlib.PurePosixPath(*literal_parts))
continue
parent_parts = parts[:-1]
for idx in range(1, len(parent_parts) + 1):
exact_dirs.add(pathlib.PurePosixPath(*parent_parts[:idx]))
return _NegatedDockerignoreHints(
exact_dirs=frozenset(exact_dirs),
wildcard_prefixes=frozenset(wildcard_prefixes),
recurse_all=recurse_all,
)
+10 -3
View File
@@ -26,8 +26,15 @@ class LogData(TypedDict):
params: dict[str, Any]
def get_anonymized_params(kwargs: dict[str, Any]) -> dict[str, bool]:
params = {}
def get_anonymized_params(
kwargs: dict[str, Any], *, cli_command: str
) -> dict[str, bool | str]:
params: dict[str, bool | str] = {}
if cli_command == "deploy" and (
analytics_source := os.getenv("LANGGRAPH_CLI_ANALYTICS_SOURCE")
):
params["source"] = analytics_source
# anonymize params with values
if config := kwargs.get("config"):
@@ -88,7 +95,7 @@ def log_command(func):
"python_version": platform.python_version(),
"cli_version": __version__,
"cli_command": func.__name__,
"params": get_anonymized_params(kwargs),
"params": get_anonymized_params(kwargs, cli_command=func.__name__),
}
background_thread = threading.Thread(target=log_data, args=(data,))
+1 -24
View File
@@ -9,35 +9,12 @@ from contextlib import contextmanager
import click
import pathspec
from langgraph_cli._ignore import _build_ignore_spec
from langgraph_cli.config import Config, _assemble_local_deps
_WARN_SIZE = 50 * 1024 * 1024 # 50 MB
_MAX_SIZE = 200 * 1024 * 1024 # 200 MB
_ALWAYS_EXCLUDE = [
"__pycache__/",
".git/",
".venv/",
"venv/",
"node_modules/",
".tox/",
".mypy_cache/",
]
def _build_ignore_spec(directory: pathlib.Path) -> pathspec.PathSpec:
"""Build a PathSpec combining built-in exclusions with .dockerignore and .gitignore.
Always excludes common non-source directories (_ALWAYS_EXCLUDE). On top of
that, patterns from .dockerignore and .gitignore (if present) are merged in.
"""
lines: list[str] = list(_ALWAYS_EXCLUDE)
for name in (".dockerignore", ".gitignore"):
ignore_file = directory / name
if ignore_file.is_file():
lines.extend(ignore_file.read_text(encoding="utf-8").splitlines())
return pathspec.PathSpec.from_lines("gitwildmatch", lines)
def _tar_filter(tarinfo: tarfile.TarInfo) -> tarfile.TarInfo | None:
"""Strip symlinks, hardlinks, and traversal paths from archive."""
+20 -2
View File
@@ -35,6 +35,12 @@ DISALLOWED_BUILD_COMMAND_CHARS = [
# This blocks background execution (cmd &) while allowing command
# chaining (cmd1 && cmd2) which is common in build commands.
_SINGLE_AMPERSAND_RE = re.compile(r"(?<!&)&(?:&&)*(?!&)")
_API_VERSION_PATTERN = re.compile(
r"^(?P<major>\d+)"
r"(?:\.(?P<minor>\d+))?"
r"(?:\.(?P<patch>\d+))?"
r"(?:(?:\.|)(?:[A-Za-z][0-9A-Za-z]*))?$"
)
def has_disallowed_build_command_content(command: str) -> bool:
@@ -123,6 +129,18 @@ def _parse_node_version(version_str: str) -> int:
) from None
def _parse_api_version_parts(version_str: str) -> tuple[int, ...]:
"""Parse an API version into numeric components.
Supports optional prerelease suffixes, e.g. `0.9.0rc1`.
"""
version_core = version_str.split("-", 1)[0]
match = _API_VERSION_PATTERN.fullmatch(version_core)
if not match:
raise ValueError("Version must be major or major.minor or major.minor.patch.")
return tuple(int(part) for part in match.groups() if part is not None)
def _is_node_graph(spec: str | dict) -> bool:
"""Check if a graph is a Node.js graph based on the file extension."""
if isinstance(spec, dict):
@@ -176,12 +194,12 @@ def validate_config(config: Config) -> Config:
)
if api_version:
try:
parts = tuple(map(int, api_version.split("-")[0].split(".")))
parts = _parse_api_version_parts(api_version)
if len(parts) > 3:
raise ValueError(
"Version must be major or major.minor or major.minor.patch."
)
except TypeError:
except (TypeError, ValueError):
raise click.UsageError(
f"Invalid version format: {api_version}.\n\n"
"Pin to a minor version, e.g.:\n"
+325 -86
View File
@@ -115,6 +115,172 @@ class BuildResult:
show_build_logs_on_failure: bool = False
# ---------------------------------------------------------------------------
# Structured output emitter
# ---------------------------------------------------------------------------
_emitter: "_Emitter | None" = None
_no_input: bool = False
class _Emitter:
"""Dual-mode output: JSON-lines (``--json``) or human-readable click text."""
def __init__(self, json_mode: bool) -> None:
self._json = json_mode
@property
def json_mode(self) -> bool:
return self._json
# -- Structured event helpers ------------------------------------------
def step(self, step: int, message: str, **extra: object) -> None:
if self._json:
self._write({"event": "step", "step": step, "message": message, **extra})
else:
click.secho(f"{step}. {message}", fg="cyan")
def info(self, message: str, **extra: object) -> None:
if self._json:
self._write({"event": "info", "message": message, **extra})
else:
click.secho(f" {message}", fg="green")
def warn(self, message: str, **extra: object) -> None:
"""Warning nested under a step. Text mode indents; JSON mode strips leading whitespace."""
if self._json:
self._write({"event": "warn", "message": message.lstrip(), **extra})
else:
click.secho(f" {message}", fg="yellow")
def note(self, message: str, **extra: object) -> None:
"""Top-level banner (pre-step). Text mode does not indent."""
if self._json:
self._write({"event": "note", "message": message, **extra})
else:
click.secho(message, fg="yellow")
def error(self, message: str, **extra: object) -> None:
if self._json:
self._write({"event": "error", "message": message, **extra})
else:
click.secho(f" {message}", fg="red")
def status_change(
self,
status: str,
elapsed_seconds: float,
finished: bool = False,
) -> None:
mins, secs = divmod(int(elapsed_seconds), 60)
elapsed_str = f"{mins}m {secs:02d}s" if mins else f"{secs}s"
if self._json:
self._write(
{
"event": "status_change",
"status": status,
"elapsed_seconds": round(elapsed_seconds, 1),
"message": f"{status}... ({elapsed_str})",
}
)
else:
click.echo(f" {status}... ({elapsed_str})")
def log(self, message: str) -> None:
if self._json:
self._write({"event": "log", "message": message})
else:
click.echo(f" | {message}")
def status_url(self, url: str) -> None:
if self._json:
self._write({"event": "status_url", "url": url})
else:
click.secho(f" View status: {url}", fg="cyan")
def result(
self,
status: str,
*,
deployment_id: str,
url: str | None = None,
status_url: str | None = None,
fallback_status_message: str | None = None,
) -> None:
if self._json:
if status == "succeeded":
message = "Deployment successful!"
elif status == "failed":
message = "Deployment failed"
else:
message = "Timed out waiting for deployment."
payload: dict = {
"event": "result",
"status": status,
"deployment_id": deployment_id,
"message": message,
}
if url:
payload["url"] = url
if status_url:
payload["status_url"] = status_url
self._write(payload)
else:
if status == "succeeded":
click.secho(" Deployment successful!", fg="green")
if url:
click.secho(f" URL: {url}", fg="green")
if status_url:
click.secho(f" View status: {status_url}", fg="green")
elif status == "failed":
click.secho(" Deployment failed", fg="red")
if status_url:
click.secho(f" View status: {status_url}", fg="red")
elif status == "timed_out":
click.secho(" Timed out waiting for deployment.", fg="yellow")
if status_url:
click.secho(f" Check status at: {status_url}", fg="yellow")
elif fallback_status_message:
click.secho(f" {fallback_status_message}", fg="yellow")
def heartbeat(self, status: str, elapsed_seconds: float) -> None:
if self._json:
mins, secs = divmod(int(elapsed_seconds), 60)
elapsed_str = f"{mins}m {secs:02d}s" if mins else f"{secs}s"
self._write(
{
"event": "heartbeat",
"status": status,
"elapsed_seconds": round(elapsed_seconds, 1),
"message": f"{status}... ({elapsed_str})",
}
)
def upload_progress(self, size_mb: float, pct: int) -> None:
if self._json:
self._write(
{
"event": "upload_progress",
"size_mb": round(size_mb, 1),
"pct": pct,
}
)
else:
click.echo(f"\r Uploading ({size_mb:.1f} MB)... {pct}%", nl=False)
def _write(self, obj: dict) -> None:
import sys as _sys
_sys.stdout.write(json_mod.dumps(obj, default=str) + "\n")
_sys.stdout.flush()
def _get_emitter() -> _Emitter:
"""Return the module-level emitter (falls back to text mode)."""
return _emitter or _Emitter(json_mode=False)
# ---------------------------------------------------------------------------
# Validators
# ---------------------------------------------------------------------------
@@ -172,15 +338,16 @@ def find_deployment_id_by_name(
# ---------------------------------------------------------------------------
def normalize_image_name(value: str | None) -> str:
"""Sanitize a deployment/directory name into a valid Docker repository name.
def normalize_name(value: str | None) -> str:
"""Sanitize a deployment/directory name into a valid deployment name.
Docker repository names must be lowercase and may only contain
[a-z0-9._-]. Invalid characters are replaced with hyphens.
LangSmith Deployment names only allow lowercase
alphanumeric characters and hyphens ([a-z0-9-]).
Invalid characters are replaced with hyphens.
"""
if not value:
return "app"
slug = re.sub(r"[^a-z0-9._-]+", "-", value.lower()).strip("-.")
slug = re.sub(r"[^a-z0-9-]+", "-", value.lower()).strip("-")
return slug or "app"
@@ -307,9 +474,8 @@ def _resolve_env_path(
if isinstance(env_field, str):
env_path = (config_path.parent / env_field).resolve()
if not env_path.exists():
click.secho(
f"Warning: env file '{env_field}' specified in langgraph.json not found.",
fg="yellow",
_get_emitter().note(
f"Warning: env file '{env_field}' specified in langgraph.json not found."
)
return None
return env_path
@@ -343,7 +509,7 @@ def _secrets_from_env(
secrets: list[dict[str, str]] = []
for name, value in env_vars.items():
if name in RESERVED_ENV_VARS:
click.secho(f" Skipping reserved env var: {name}", fg="yellow")
_get_emitter().note(f"Skipping reserved env var: {name}")
continue
if not value:
continue
@@ -386,8 +552,8 @@ def _resolve_build_mode(
# ---------------------------------------------------------------------------
def _log_deploy_step(step: int, message: str) -> None:
click.secho(f"{step}. {message}", fg="cyan")
def _log_deploy_step(step: int, message: str, **extra: object) -> None:
_get_emitter().step(step, message, **extra)
def _resolve_deployment(
@@ -411,12 +577,13 @@ def _resolve_deployment(
found_id = _call_host_backend_with_optional_tenant(
client, lambda c: find_deployment_id_by_name(c, name)
)
em = _get_emitter()
if found_id:
deployment_id = str(found_id)
click.secho(f" Found existing deployment (ID: {deployment_id})", fg="green")
em.info(f"Found existing deployment (ID: {deployment_id})")
else:
needs_creation = True
click.secho(not_found_message, fg="yellow")
em.warn(not_found_message)
return deployment_id, needs_creation, step + 1
@@ -444,7 +611,7 @@ def _create_deployment(
raise HostBackendError(
"POST /v2/deployments succeeded but response missing a valid 'id'"
)
click.secho(f" Deployment ID: {created_id}", fg="green")
_get_emitter().info(f"Deployment ID: {created_id}", deployment_id=created_id)
return created_id, step + 1
@@ -458,21 +625,36 @@ def _smith_dashboard_base_url(host_url: str | None) -> str:
hostname = parsed.hostname or ""
if hostname in ("localhost", "127.0.0.1"):
return host_url.rstrip("/")
if hostname.startswith("eu."):
return "https://eu.smith.langchain.com"
api_host_suffix = "api.host.langchain.com"
if hostname == api_host_suffix:
return "https://smith.langchain.com"
if hostname.endswith(f".{api_host_suffix}"):
prefix = hostname[: -(len(api_host_suffix) + 1)]
return f"https://{prefix}.smith.langchain.com"
return "https://smith.langchain.com"
def _print_deployment_status_url(
def _get_deployment_status_url(
updated: object, deployment_id: str, host_url: str | None = None
) -> None:
"""Print the deployment status URL when tenant metadata is available."""
) -> str | None:
"""Compute the LangSmith dashboard URL for a deployment, if possible."""
tenant_id = updated.get("tenant_id") if isinstance(updated, dict) else None
if not tenant_id:
return
return None
base = _smith_dashboard_base_url(host_url)
status_url = f"{base}/o/{tenant_id}/host/deployments/{deployment_id}"
click.secho(f" View status: {status_url}", fg="cyan")
return f"{base}/o/{tenant_id}/host/deployments/{deployment_id}"
def _emit_deployment_status_url(
updated: object, deployment_id: str, host_url: str | None = None
) -> str | None:
"""Emit the deployment status URL and return it."""
url = _get_deployment_status_url(updated, deployment_id, host_url)
if url:
_get_emitter().status_url(url)
return url
def _poll_revision_status(
@@ -486,6 +668,7 @@ def _poll_revision_status(
on_interrupt: Callable[[str], None] | None = None,
) -> tuple[str, str | None]:
"""Poll latest revision status until terminal status or timeout."""
em = _get_emitter()
revisions_resp = client.list_revisions(deployment_id, limit=1)
resources = (
revisions_resp.get("resources", []) if isinstance(revisions_resp, dict) else []
@@ -497,7 +680,11 @@ def _poll_revision_status(
last_status = ""
deadline = time.time() + timeout_seconds
start_time = time.monotonic()
with Progress(message=progress_message, elapsed=True) as set_progress:
last_heartbeat = start_time
json_mode = em.json_mode
with Progress(
message=progress_message, elapsed=True, json_mode=json_mode
) as set_progress:
while time.time() < deadline:
try:
rev = client.get_revision(deployment_id, revision_id)
@@ -514,14 +701,15 @@ def _poll_revision_status(
if status != last_status:
set_progress("")
if last_status:
elapsed = time.monotonic() - start_time
mins, secs = divmod(int(elapsed), 60)
elapsed_str = f"{mins}m {secs:02d}s" if mins else f"{secs}s"
click.echo(f" {last_status}... ({elapsed_str})")
em.status_change(last_status, time.monotonic() - start_time)
last_status = status
if status in _TERMINAL_STATUSES:
break
set_progress(f"{status}...")
last_heartbeat = time.monotonic()
elif json_mode and time.monotonic() - last_heartbeat > 10:
em.heartbeat(last_status, time.monotonic() - start_time)
last_heartbeat = time.monotonic()
if on_poll is not None:
on_poll(status, revision_id, set_progress)
@@ -538,8 +726,10 @@ def _print_deployment_result(
last_status: str,
*,
dashboard_label: str,
status_url: str | None = None,
) -> None:
"""Print final deployment status and raise on failure."""
em = _get_emitter()
dep_info = client.get_deployment(deployment_id)
custom_url = None
if isinstance(dep_info, dict):
@@ -548,24 +738,28 @@ def _print_deployment_result(
custom_url = sc.get("custom_url")
if last_status == "DEPLOYED":
click.secho(" Deployment successful!", fg="green")
if custom_url:
click.secho(f" URL: {custom_url}", fg="green")
em.result(
"succeeded",
deployment_id=deployment_id,
url=custom_url,
status_url=status_url,
)
elif last_status in ("BUILD_FAILED", "DEPLOY_FAILED", "CREATE_FAILED"):
click.secho(f" Deployment failed: {last_status}", fg="red")
em.result(
"failed",
deployment_id=deployment_id,
status_url=status_url,
)
raise click.exceptions.Exit(1)
else:
click.secho(
f" Timed out waiting for deployment (last status: {last_status}).",
fg="yellow",
em.result(
"timed_out",
deployment_id=deployment_id,
status_url=status_url,
fallback_status_message=(
f"Check status in the LangSmith {dashboard_label}."
),
)
if custom_url:
click.secho(f" Check status at: {custom_url}", fg="yellow")
else:
click.secho(
f" Check status in the LangSmith {dashboard_label}.",
fg="yellow",
)
# ---------------------------------------------------------------------------
@@ -594,14 +788,16 @@ def _docker_config_for_token(registry_host: str, token: str):
# ---------------------------------------------------------------------------
_UPLOAD_TIMEOUT_SECONDS = 300
_BYTES_PER_MIB = 1_048_576
class _ProgressReader:
"""File-like wrapper that displays upload progress via click."""
"""File-like wrapper that reports upload progress via the emitter."""
def __init__(self, fobj, file_size: int):
def __init__(self, fobj, file_size: int, emitter: "_Emitter"):
self._fobj = fobj
self._file_size = file_size
self._emitter = emitter
self._uploaded = 0
def read(self, size=-1):
@@ -611,10 +807,7 @@ class _ProgressReader:
pct = (
int(self._uploaded * 100 / self._file_size) if self._file_size else 100
)
click.echo(
f"\r Uploading ({self._file_size / 1_048_576:.1f} MB)... {pct}%",
nl=False,
)
self._emitter.upload_progress(self._file_size / _BYTES_PER_MIB, pct)
return data
def __len__(self):
@@ -626,10 +819,13 @@ def _upload_to_gcs(signed_url: str, file_path: str, file_size: int) -> None:
import urllib.error
import urllib.request
em = _get_emitter()
with open(file_path, "rb") as f:
reader = _ProgressReader(f, file_size, em)
req = urllib.request.Request(
signed_url,
data=_ProgressReader(f, file_size),
data=reader,
method="PUT",
headers={
"Content-Type": "application/gzip",
@@ -644,7 +840,8 @@ def _upload_to_gcs(signed_url: str, file_path: str, file_size: int) -> None:
raise click.ClickException(
f"Upload failed with status {err.code}: {detail}"
) from None
click.echo()
if not em.json_mode:
click.echo()
# ---------------------------------------------------------------------------
@@ -752,7 +949,7 @@ def _run_local_build(
if "://" in normalized_registry:
normalized_registry = normalized_registry.split("//", 1)[1]
repo_seed = image_name or name or config.parent.name
repo_name = normalize_image_name(repo_seed)
repo_name = normalize_name(repo_seed)
tag_value = normalize_image_tag(tag)
remote_image = f"{normalized_registry}/{repo_name}:{tag_value}"
@@ -811,9 +1008,8 @@ def _run_local_build(
break
except click.exceptions.Exit:
if attempt < max_push_retries - 1:
click.secho(
f" Push failed, retrying (attempt {attempt + 2} of {max_push_retries})...",
fg="yellow",
_get_emitter().warn(
f" Push failed, retrying (attempt {attempt + 2} of {max_push_retries})..."
)
else:
raise
@@ -847,9 +1043,10 @@ def _run_remote_build(
"""Upload source tarball and trigger a remote build."""
from langgraph_cli.archive import create_archive
em = _get_emitter()
_log_deploy_step(step, "Creating source archive")
with create_archive(config, config_json) as (archive_path, file_size, config_rel):
click.secho(f" Archive created ({file_size / 1_048_576:.1f} MB)", fg="green")
em.info(f"Archive created ({file_size / _BYTES_PER_MIB:.1f} MB)")
step += 1
_log_deploy_step(step, "Requesting upload URL")
@@ -897,12 +1094,12 @@ def _run_remote_build(
if has_output:
set_progress("")
if not logs_header_printed:
click.echo(f" {status} (build logs):")
em.info(f"{status} (build logs):")
logs_header_printed = True
for entry in entries:
msg = entry.get("message", "")
if msg:
click.echo(f" | {msg}")
em.log(msg)
log_offset = logs_resp.get("next_offset") or log_offset
if has_output:
set_progress(f"{status}...")
@@ -910,11 +1107,10 @@ def _run_remote_build(
pass
def _handle_interrupt(revision_id: str) -> None:
click.secho(
f"\n Interrupted. Deployment ID: {deployment_id}, Revision ID: {revision_id}",
fg="yellow",
em.warn(
f"\nInterrupted. Deployment ID: {deployment_id}, Revision ID: {revision_id}"
)
click.secho(" The build will continue remotely.", fg="yellow")
em.warn("The build will continue remotely.")
return BuildResult(
updated=updated if isinstance(updated, dict) else {},
@@ -952,12 +1148,20 @@ def _create_host_backend_client(
resolved_api_key = val
break
if not resolved_api_key:
if _no_input:
raise click.ClickException(
"No LangSmith API key found. Set LANGSMITH_API_KEY in the "
"environment or .env file."
)
click.secho(
"No LangSmith API key found. Create one at Settings > API Keys in LangSmith.",
fg="yellow",
)
resolved_api_key = click.prompt("Enter LangSmith API key", hide_input=True)
return HostBackendClient(host_url, resolved_api_key)
tenant_id = env_vars.get("LANGSMITH_TENANT_ID") or os.environ.get(
"LANGSMITH_TENANT_ID"
)
return HostBackendClient(host_url, resolved_api_key, tenant_id=tenant_id)
def _call_host_backend_with_optional_tenant(
@@ -982,6 +1186,12 @@ def _call_host_backend_with_optional_tenant(
and err.status_code == 403
and "requires workspace specification" in err.message
):
if _no_input:
raise click.ClickException(
"API key is org-scoped and requires a workspace ID. "
"Set LANGSMITH_TENANT_ID in your .env file or "
"use a workspace-scoped API key."
) from None
click.secho(
"Your API key is org-scoped and requires a workspace ID.",
fg="yellow",
@@ -1189,6 +1399,19 @@ def _deploy_base_options(
"if Docker is not available locally."
),
),
click.option(
"--json",
"json_output",
is_flag=True,
default=False,
help="Emit structured JSON-lines to stdout instead of human-readable text.",
),
click.option(
"--no-input",
is_flag=True,
default=False,
help="Never prompt for input; fail with an error if a required value is missing.",
),
]
if include_docker_args:
# Only attach build args to the default command; on the group they
@@ -1256,36 +1479,50 @@ def _deploy_cmd(
no_wait: bool,
remote_build_flag: bool | None,
docker_build_args: Sequence[str],
json_output: bool,
no_input: bool,
):
click.secho(
"Note: 'langgraph deploy' is in beta. Expect frequent updates and improvements.",
fg="yellow",
global _emitter, _no_input
_emitter = _Emitter(json_mode=json_output)
_no_input = no_input
em = _emitter
em.note(
"Note: 'langgraph deploy' is in beta. Expect frequent updates and improvements."
)
click.echo()
if not json_output:
click.echo()
# -- 1. Preflight --
validate_deploy_commands(install_command, build_command)
config_json = langgraph_cli.config.validate_config_file(config)
warn_non_wolfi_distro(config_json)
warn_non_wolfi_distro(config_json, emit=em.note)
env_vars = _parse_env_from_config(config_json, config)
if not deployment_id and not name:
name = env_vars.get(_DEPLOYMENT_NAME_ENV)
if not deployment_id and not name:
default_name = normalize_image_name(pathlib.Path.cwd().name)
name = click.prompt("Deployment name", default=default_name)
env_path = _resolve_env_path(config_json, config)
if env_path is not None:
set_key(str(env_path), _DEPLOYMENT_NAME_ENV, name)
click.echo(f"Saved deployment name to {env_path}")
default_name = normalize_name(pathlib.Path.cwd().name)
if no_input:
name = default_name
else:
name = click.prompt("Deployment name", default=default_name)
if name and not deployment_id:
name = normalize_name(name)
if not no_input:
env_path = _resolve_env_path(config_json, config)
if env_path is not None:
set_key(str(env_path), _DEPLOYMENT_NAME_ENV, name)
em.info(f"Saved deployment name to {env_path}")
secrets = _secrets_from_env(_env_without_deployment_name(env_vars))
use_remote_build, local_build_error = _resolve_build_mode(remote_build_flag)
if use_remote_build and remote_build_flag is None and local_build_error:
click.secho(f"{local_build_error}\nUsing remote build instead.", fg="yellow")
click.echo()
em.note(f"{local_build_error}\nUsing remote build instead.")
if not json_output:
click.echo()
# -- 2. Resolve / create deployment --
client = _create_host_backend_client(host_url, api_key, env_vars=env_vars)
@@ -1297,9 +1534,9 @@ def _deploy_cmd(
deployment_id,
name,
not_found_message=(
" No deployment found. Will create."
"No deployment found. Will create."
if use_remote_build
else " No deployment found. Will create after build."
else "No deployment found. Will create after build."
),
)
@@ -1350,10 +1587,14 @@ def _deploy_cmd(
)
# -- 4. Shared wait + result --
_print_deployment_status_url(build_result.updated, deployment_id, host_url)
dep_status_url = _emit_deployment_status_url(
build_result.updated,
deployment_id,
host_url,
)
if no_wait:
click.secho(f" {build_result.no_result_message}", fg="green")
em.info(build_result.no_result_message)
return
last_status, revision_id = _poll_revision_status(
@@ -1366,7 +1607,7 @@ def _deploy_cmd(
on_interrupt=build_result.on_interrupt,
)
if not last_status:
click.secho(f" {build_result.no_result_message}", fg="green")
em.info(build_result.no_result_message)
return
if (
@@ -1375,7 +1616,7 @@ def _deploy_cmd(
and not verbose
and revision_id is not None
):
click.secho(" Last build log lines:", fg="red")
em.error("Last build log lines:")
try:
logs_resp = client.get_build_logs(
deployment_id,
@@ -1387,19 +1628,17 @@ def _deploy_cmd(
for entry in entries:
msg = entry.get("message", "")
if msg:
click.echo(f" | {msg}")
em.log(msg)
except Exception:
click.secho(" (failed to fetch build logs)", fg="red")
click.secho(
" Re-run with --verbose to see full build output.",
fg="yellow",
)
em.error("(failed to fetch build logs)")
em.warn("Re-run with --verbose to see full build output.")
_print_deployment_result(
client,
deployment_id,
last_status,
dashboard_label="Deployment dashboard",
status_url=dep_status_url,
)
-74
View File
@@ -1,74 +0,0 @@
"""User-facing entrypoint for the LangGraph CLI."""
from __future__ import annotations
import os
import pathlib
import sys
from collections.abc import Sequence
import click
from .cli import cli
_GO_CLI_FLAG = "LANGGRAPH_USE_GO_CLI"
_GO_CLI_PATH_ENV = "LANGGRAPH_GO_CLI_PATH"
_CALLING_PYTHON_ENV = "LANGGRAPH_CALLING_PYTHON"
_TRUE_VALUES = frozenset({"1", "true", "yes", "on"})
def _legacy_cli(argv: Sequence[str] | None = None) -> None:
cli.main(args=list(argv) if argv is not None else None, prog_name="langgraph")
def _should_use_go_cli() -> bool:
value = os.environ.get(_GO_CLI_FLAG, "")
return value.strip().lower() in _TRUE_VALUES
def _bundled_go_cli_path() -> pathlib.Path:
binary_name = "langgraph.exe" if os.name == "nt" else "langgraph"
return pathlib.Path(__file__).resolve().parent / "bin" / binary_name
def _resolve_go_cli_path() -> pathlib.Path | None:
override = os.environ.get(_GO_CLI_PATH_ENV)
if override:
path = pathlib.Path(override).expanduser()
return path.resolve()
bundled = _bundled_go_cli_path()
if bundled.is_file():
return bundled
return None
def _exec_go_cli(argv: Sequence[str]) -> None:
path = _resolve_go_cli_path()
if path is None:
raise click.ClickException(
"Go CLI requested via LANGGRAPH_USE_GO_CLI, but no langgraph binary was "
"found. Set LANGGRAPH_GO_CLI_PATH or install a wheel that bundles the "
"binary."
)
if not path.is_file():
raise click.ClickException(
f"LANGGRAPH_GO_CLI_PATH points to a missing file: {path}"
)
env = os.environ.copy()
env.setdefault(_CALLING_PYTHON_ENV, sys.executable)
os.execvpe(str(path), [str(path), *argv], env)
def main(argv: Sequence[str] | None = None) -> None:
args = list(sys.argv[1:] if argv is None else argv)
try:
if _should_use_go_cli():
_exec_go_cli(args)
_legacy_cli(args)
except click.ClickException as exc:
exc.show()
raise SystemExit(exc.exit_code) from exc
+7 -1
View File
@@ -12,10 +12,11 @@ class Progress:
while True:
yield from "|/-\\"
def __init__(self, *, message="", elapsed: bool = False):
def __init__(self, *, message="", elapsed: bool = False, json_mode: bool = False):
self.message = message
self._base_message = message
self._show_elapsed = elapsed
self._json_mode = json_mode
# use this to make sure we don't kill thread when we set msg to ""
self._stop = threading.Event()
# signalled when the spinner has no text on screen
@@ -69,6 +70,9 @@ class Progress:
self._line_clear.set()
def __enter__(self) -> Callable[[str], None]:
if self._json_mode:
return lambda message: None
if sys.stdout.isatty():
self.thread = threading.Thread(target=self.spinner_task)
self.thread.start()
@@ -90,6 +94,8 @@ class Progress:
return set_message
def __exit__(self, exception, value, tb):
if self._json_mode:
return
if sys.stdout.isatty():
self.message = ""
self._stop.set()
+37 -14
View File
@@ -1,5 +1,7 @@
"""General-purpose utilities shared across the LangGraph CLI."""
from collections.abc import Callable
import click
@@ -7,21 +9,42 @@ def clean_empty_lines(input_str: str):
return "\n".join(filter(None, input_str.splitlines()))
def warn_non_wolfi_distro(config_json: dict) -> None:
"""Show warning if image_distro is not set to 'wolfi'."""
def warn_non_wolfi_distro(
config_json: dict,
*,
emit: Callable[[str], None] | None = None,
) -> None:
"""Show warning if image_distro is not set to 'wolfi'.
When ``emit`` is provided, each warning line is sent through it (used by
callers that need JSON-aware output). Otherwise falls back to colored
``click.secho`` output.
"""
image_distro = config_json.get("image_distro", "debian") # Default is debian
if image_distro != "wolfi":
click.secho(
"⚠️ Security Recommendation: Consider switching to Wolfi Linux for enhanced security.",
fg="yellow",
bold=True,
if image_distro == "wolfi":
return
if emit is not None:
emit(
"⚠️ Security Recommendation: Consider switching to Wolfi Linux for enhanced security."
)
click.secho(
" Wolfi is a security-oriented, minimal Linux distribution designed for containers.",
fg="yellow",
emit(
" Wolfi is a security-oriented, minimal Linux distribution designed for containers."
)
click.secho(
' To switch, add \'"image_distro": "wolfi"\' to your langgraph.json config file.',
fg="yellow",
emit(
' To switch, add \'"image_distro": "wolfi"\' to your langgraph.json config file.'
)
click.secho("") # Empty line for better readability
return
click.secho(
"⚠️ Security Recommendation: Consider switching to Wolfi Linux for enhanced security.",
fg="yellow",
bold=True,
)
click.secho(
" Wolfi is a security-oriented, minimal Linux distribution designed for containers.",
fg="yellow",
)
click.secho(
' To switch, add \'"image_distro": "wolfi"\' to your langgraph.json config file.',
fg="yellow",
)
click.secho("") # Empty line for better readability
+50 -11
View File
@@ -10,7 +10,13 @@ except ModuleNotFoundError: # pragma: no cover - exercised on Python 3.10.
import tomli as tomllib
import click
import pathspec
from langgraph_cli._ignore import (
_build_dockerignore_negation_hints,
_build_ignore_spec,
_is_always_excluded,
)
from langgraph_cli.schemas import Config
@@ -440,16 +446,32 @@ def _container_root_for_uv_lock_package(
def _uv_lock_package_copy_items(
package: UvLockPackage, plan: UvLockPlan
package: UvLockPackage,
plan: UvLockPlan,
ignore_spec: pathspec.PathSpec,
) -> tuple[tuple[pathlib.PurePosixPath, pathlib.PurePosixPath], ...]:
# Skip entries that .dockerignore / built-in exclusions would strip from
# the build context. Emitting `ADD <path>` for a file that Docker has
# filtered out causes the build to fail with
# "failed to compute cache key: <path> not found".
if package.root != plan.project_root:
relative_root = pathlib.PurePosixPath(
*package.root.relative_to(plan.project_root).parts
)
if _is_always_excluded(relative_root, is_dir=True) or ignore_spec.match_file(
f"{relative_root.as_posix()}/"
):
raise click.UsageError(
f"Workspace member '{package.name}' at {relative_root} is "
"excluded from the Docker build context, but uv.lock requires "
"it to be copied into the build context. Remove the matching "
"pattern or drop the member from [tool.uv.workspace].members."
)
return ((relative_root, plan.container_roots[package.root]),)
root_container = plan.container_roots[package.root]
workspace_member_roots = plan.all_workspace_roots - {plan.project_root}
negated_dockerignore_hints = _build_dockerignore_negation_hints(plan.project_root)
def iter_entries(
current_dir: pathlib.Path,
@@ -461,18 +483,32 @@ def _uv_lock_package_copy_items(
# and excluded entirely otherwise.
continue
descendant_member_roots = [
ws_root
for ws_root in workspace_member_roots
if child in ws_root.parents
]
if child.is_dir() and descendant_member_roots:
entries.extend(iter_entries(child))
continue
relative_child = pathlib.PurePosixPath(
*child.relative_to(plan.project_root).parts
)
is_dir = child.is_dir()
if _is_always_excluded(relative_child, is_dir=is_dir):
continue
ignored = ignore_spec.match_file(
f"{relative_child.as_posix()}/" if is_dir else relative_child.as_posix()
)
is_workspace_parent = is_dir and any(
child in ws_root.parents for ws_root in workspace_member_roots
)
if is_workspace_parent:
entries.extend(iter_entries(child))
continue
if (
is_dir
and ignored
and negated_dockerignore_hints.requires_dir_walk(relative_child)
):
entries.extend(iter_entries(child))
continue
if ignored:
continue
entries.append(
(relative_child, root_container.joinpath(*relative_child.parts))
)
@@ -956,10 +992,13 @@ def python_config_to_docker_uv_lock(
docker_plan.add_raw("# -- End of uv.lock dependencies install --")
docker_plan.add_blank()
ignore_spec = _build_ignore_spec(plan.project_root, include_gitignore=False)
for package in plan.install_order:
package_label = package.root.relative_to(plan.project_root).as_posix() or "."
docker_plan.add_raw(f"# -- Adding workspace package {package_label} --")
for source, destination in _uv_lock_package_copy_items(package, plan):
for source, destination in _uv_lock_package_copy_items(
package, plan, ignore_spec
):
docker_plan.add_raw(copy_from_project_root(source, destination.as_posix()))
docker_plan.add_instruction(
"WORKDIR", plan.container_roots[package.root].as_posix()
+3 -6
View File
@@ -23,18 +23,18 @@ dependencies = [
path = "langgraph_cli/__init__.py"
[project.optional-dependencies]
inmem = [
"langgraph-api>=0.5.35,<0.8.0 ; python_version >= '3.11'",
"langgraph-api>=0.5.35,<0.9.0 ; python_version >= '3.11'",
"langgraph-runtime-inmem>=0.7 ; python_version >= '3.11'",
]
[project.urls]
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/cli"
Twitter = "https://x.com/LangChain"
Twitter = "https://x.com/langchain_oss"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
[project.scripts]
langgraph = "langgraph_cli.entrypoint:main"
langgraph = "langgraph_cli.cli:cli"
[dependency-groups]
test = [
@@ -61,9 +61,6 @@ default-groups = ['dev']
[tool.hatch.build.targets.wheel]
include = ["langgraph_cli"]
[tool.hatch.build.targets.wheel.hooks.custom]
path = "hatch_build.py"
[tool.pytest.ini_options]
addopts = "--strict-markers --strict-config --durations=5 -vv"
asyncio_mode = "auto"
@@ -99,6 +99,13 @@ class TestBuildIgnoreSpec:
assert spec.match_file("app.log")
assert spec.match_file("mod.pyc")
def test_can_skip_gitignore(self, tmp_path):
(tmp_path / ".dockerignore").write_text("*.log\n")
(tmp_path / ".gitignore").write_text("*.pyc\n")
spec = _build_ignore_spec(tmp_path, include_gitignore=False)
assert spec.match_file("app.log")
assert not spec.match_file("mod.pyc")
def test_no_ignore_files_only_builtins(self, tmp_path):
spec = _build_ignore_spec(tmp_path)
assert spec.match_file("__pycache__/")
+376
View File
@@ -4,6 +4,7 @@ import os
import pathlib
import tempfile
import textwrap
from unittest.mock import patch
import click
import pytest
@@ -1855,6 +1856,364 @@ def test_config_to_docker_uv_lock_supports_single_uv_project_root():
assert additional_contexts == {}
def test_config_to_docker_uv_lock_skips_dockerignore_entries():
"""Entries filtered by .dockerignore / built-in excludes must not appear
as ADD lines. Docker fails to compute the cache key for paths that the
build context has stripped."""
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
project_root = tmpdir_path / "single"
project_root.mkdir()
(project_root / "uv.lock").write_text("# uv lock file\n")
(project_root / "pyproject.toml").write_text(
textwrap.dedent(
"""
[project]
name = "single-app"
version = "0.1.0"
dependencies = ["httpx>=0.28"]
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
"""
).strip()
+ "\n"
)
(project_root / "langgraph.json").write_text("{}\n")
(project_root / "src").mkdir()
(project_root / "src" / "agent.py").write_text("graph = object()\n")
(project_root / "README.md").write_text("# hi\n")
# Built-in exclusions — must never appear as ADD lines.
(project_root / ".git").mkdir()
(project_root / ".git" / "HEAD").write_text("ref: refs/heads/main\n")
(project_root / ".venv").mkdir()
(project_root / ".venv" / "pyvenv.cfg").write_text("home = /usr\n")
(project_root / "__pycache__").mkdir()
(project_root / "__pycache__" / "x.cpython-311.pyc").write_bytes(b"\x00")
# .dockerignore excludes .gitignore and a custom path.
(project_root / ".dockerignore").write_text(".gitignore\nsecrets.env\n")
(project_root / ".gitignore").write_text("*.pyc\n")
(project_root / "secrets.env").write_text("TOKEN=abc\n")
config = validate_config(
{
"python_version": "3.11",
"graphs": {"agent": "./src/agent.py:graph"},
"source": {"kind": "uv"},
}
)
docker, _ = config_to_docker(
project_root / "langgraph.json",
config,
base_image="langchain/langgraph-api:0.2.47",
)
for excluded in (
"ADD .git ",
"ADD .gitignore ",
"ADD .venv ",
"ADD __pycache__ ",
"ADD secrets.env ",
):
assert excluded not in docker, (
f"{excluded!r} should be filtered out of Dockerfile:\n{docker}"
)
# The .dockerignore itself is still part of the context and should be
# ADDed (Docker needs it at build time, and archive.py includes it).
assert "ADD .dockerignore /deps/workspace/.dockerignore" in docker
assert "ADD src /deps/workspace/src" in docker
assert "ADD README.md /deps/workspace/README.md" in docker
def test_config_to_docker_uv_lock_does_not_apply_gitignore():
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
project_root = tmpdir_path / "single"
project_root.mkdir()
(project_root / "uv.lock").write_text("# uv lock file\n")
(project_root / "pyproject.toml").write_text(
textwrap.dedent(
"""
[project]
name = "single-app"
version = "0.1.0"
dependencies = ["httpx>=0.28"]
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
"""
).strip()
+ "\n"
)
(project_root / "langgraph.json").write_text("{}\n")
(project_root / "src").mkdir()
(project_root / "src" / "agent.py").write_text("graph = object()\n")
(project_root / "README.md").write_text("# hi\n")
(project_root / ".gitignore").write_text("README.md\n")
config = validate_config(
{
"python_version": "3.11",
"graphs": {"agent": "./src/agent.py:graph"},
"source": {"kind": "uv"},
}
)
docker, _ = config_to_docker(
project_root / "langgraph.json",
config,
base_image="langchain/langgraph-api:0.2.47",
)
assert "ADD README.md /deps/workspace/README.md" in docker
def test_config_to_docker_uv_lock_skips_dockerignore_entries_in_workspace():
"""Multi-member workspace: ignore patterns must filter root-level entries
AND entries encountered while recursing into directories that contain
workspace members (the `descendant_member_roots` branch)."""
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
project_root, config_path = _write_uv_lock_workspace(
tmpdir_path,
agent_dependencies=["workspace-root", "shared", "httpx>=0.28"],
root_sources="[tool.uv.sources]\nshared = { workspace = true }\nworkspace-root = { workspace = true }",
agent_sources="[tool.uv.sources]\nshared = { workspace = true }\nworkspace-root = { workspace = true }",
)
root_src = project_root / "src" / "workspace_root"
root_src.mkdir(parents=True)
(root_src / "__init__.py").write_text("__all__ = []\n")
(project_root / "README.md").write_text("workspace root package\n")
# A non-member sibling of the `apps/agent` member that should be
# filtered out via .dockerignore. This exercises the recursion into
# `apps/` where `apps/agent` is kept (it's a member) but its sibling is
# filtered.
(project_root / "apps" / "scratch.txt").write_text("scratch\n")
# A root-level path that .dockerignore excludes.
(project_root / "secrets.env").write_text("TOKEN=abc\n")
(project_root / ".dockerignore").write_text("secrets.env\napps/scratch.txt\n")
config = validate_config(
{
"python_version": "3.11",
"graphs": {
"agent": "../../apps/agent/src/agent/graph.py:graph",
},
"source": {"kind": "uv", "root": "../..", "package": "agent"},
}
)
docker, _ = config_to_docker(
config_path, config, base_image="langchain/langgraph-api:0.2.47"
)
assert "COPY --from=uv-workspace-root src /deps/workspace/src" in docker
assert (
"COPY --from=uv-workspace-root README.md /deps/workspace/README.md"
in docker
)
assert (
"COPY --from=uv-workspace-root .dockerignore /deps/workspace/.dockerignore"
in docker
)
assert "secrets.env" not in docker
assert "apps/scratch.txt" not in docker
# Workspace members themselves are still copied via their own per-member
# COPY line — the sibling filter must not disturb this.
assert (
"COPY --from=uv-workspace-root apps/agent /deps/workspace/apps/agent"
in docker
)
def test_config_to_docker_uv_lock_preserves_negated_dockerignore_descendants():
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
project_root = tmpdir_path / "single"
project_root.mkdir()
(project_root / "uv.lock").write_text("# uv lock file\n")
(project_root / "pyproject.toml").write_text(
textwrap.dedent(
"""
[project]
name = "single-app"
version = "0.1.0"
dependencies = ["httpx>=0.28"]
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
"""
).strip()
+ "\n"
)
(project_root / "langgraph.json").write_text("{}\n")
(project_root / "src").mkdir()
(project_root / "src" / "agent.py").write_text("graph = object()\n")
(project_root / "assets").mkdir()
(project_root / "assets" / "keep.txt").write_text("keep\n")
(project_root / "assets" / "drop.txt").write_text("drop\n")
(project_root / ".dockerignore").write_text("assets/\n!assets/keep.txt\n")
config = validate_config(
{
"python_version": "3.11",
"graphs": {"agent": "./src/agent.py:graph"},
"source": {"kind": "uv"},
}
)
docker, _ = config_to_docker(
project_root / "langgraph.json",
config,
base_image="langchain/langgraph-api:0.2.47",
)
assert "ADD assets /deps/workspace/assets" not in docker
assert "ADD assets/keep.txt /deps/workspace/assets/keep.txt" in docker
assert "assets/drop.txt" not in docker
def test_config_to_docker_uv_lock_prunes_unrelated_ignored_subtrees():
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
project_root = tmpdir_path / "single"
project_root.mkdir()
(project_root / "uv.lock").write_text("# uv lock file\n")
(project_root / "pyproject.toml").write_text(
textwrap.dedent(
"""
[project]
name = "single-app"
version = "0.1.0"
dependencies = ["httpx>=0.28"]
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
"""
).strip()
+ "\n"
)
(project_root / "langgraph.json").write_text("{}\n")
(project_root / "src").mkdir()
(project_root / "src" / "agent.py").write_text("graph = object()\n")
(project_root / "assets").mkdir()
(project_root / "assets" / "keep.txt").write_text("keep\n")
(project_root / "vendor").mkdir()
(project_root / "vendor" / "huge.txt").write_text("large\n")
(project_root / ".dockerignore").write_text(
"vendor/\nassets/\n!assets/keep.txt\n"
)
config = validate_config(
{
"python_version": "3.11",
"graphs": {"agent": "./src/agent.py:graph"},
"source": {"kind": "uv"},
}
)
original_iterdir = pathlib.Path.iterdir
def guarded_iterdir(self):
if self == project_root / "vendor":
raise AssertionError("should not walk unrelated ignored subtree")
return original_iterdir(self)
with patch.object(
pathlib.Path, "iterdir", autospec=True, side_effect=guarded_iterdir
):
docker, _ = config_to_docker(
project_root / "langgraph.json",
config,
base_image="langchain/langgraph-api:0.2.47",
)
assert "ADD assets/keep.txt /deps/workspace/assets/keep.txt" in docker
assert "vendor/huge.txt" not in docker
def test_config_to_docker_uv_lock_never_reincludes_always_excluded_subtrees():
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
project_root = tmpdir_path / "single"
project_root.mkdir()
(project_root / "uv.lock").write_text("# uv lock file\n")
(project_root / "pyproject.toml").write_text(
textwrap.dedent(
"""
[project]
name = "single-app"
version = "0.1.0"
dependencies = ["httpx>=0.28"]
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
"""
).strip()
+ "\n"
)
(project_root / "langgraph.json").write_text("{}\n")
(project_root / "src").mkdir()
(project_root / "src" / "agent.py").write_text("graph = object()\n")
(project_root / ".venv" / "pkg").mkdir(parents=True)
(project_root / ".venv" / "pkg" / "keep.txt").write_text("keep\n")
(project_root / "node_modules" / "pkg").mkdir(parents=True)
(project_root / "node_modules" / "pkg" / "package.json").write_text("{}\n")
(project_root / ".dockerignore").write_text(
"!.venv/pkg/keep.txt\n!node_modules/pkg/package.json\n"
)
config = validate_config(
{
"python_version": "3.11",
"graphs": {"agent": "./src/agent.py:graph"},
"source": {"kind": "uv"},
}
)
docker, _ = config_to_docker(
project_root / "langgraph.json",
config,
base_image="langchain/langgraph-api:0.2.47",
)
assert ".venv/pkg/keep.txt" not in docker
assert "node_modules/pkg/package.json" not in docker
assert "ADD src /deps/workspace/src" in docker
def test_config_to_docker_uv_lock_rejects_ignored_workspace_member():
"""A workspace member matched by .dockerignore cannot be copied into the
build context uv.lock requires it, so fail loudly with a clear message."""
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
project_root, config_path = _write_uv_lock_workspace(
tmpdir_path,
agent_sources="[tool.uv.sources]\nshared = { workspace = true }",
)
(project_root / ".dockerignore").write_text("libs/shared\n")
config = validate_config(
{
"python_version": "3.11",
"graphs": {"agent": "../../apps/agent/src/agent/graph.py:graph"},
"source": {"kind": "uv", "root": "../..", "package": "agent"},
"auth": {"path": "../../libs/shared/src/shared/auth.py:create_auth"},
}
)
with pytest.raises(
click.UsageError, match=r"Workspace member 'shared' at libs/shared"
):
config_to_docker(
config_path, config, base_image="langchain/langgraph-api:0.2.47"
)
def test_config_to_docker_uv_lock_rejects_invalid_source_package_type():
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = pathlib.Path(tmpdir)
@@ -2585,6 +2944,23 @@ def test_docker_tag_with_api_version(in_config: bool):
assert tag == f"langchain/langgraph-server:{version}-py3.11"
@pytest.mark.parametrize("in_config", [False, True])
@pytest.mark.parametrize("version", ["0.9.0rc1", "0.9.0.dev1"])
def test_docker_tag_with_prerelease_api_version(version: str, in_config: bool):
"""Test docker_tag with prerelease and dev api_version values."""
config = validate_config(
{
"python_version": "3.11",
"dependencies": ["."],
"graphs": {"agent": "./agent.py:graph"},
"api_version": version if in_config else None,
}
)
tag = docker_tag(config, api_version=version if not in_config else None)
assert tag == f"langchain/langgraph-api:{version}-py3.11"
def test_config_to_docker_with_api_version():
"""Test config_to_docker function with api_version parameter."""
+272 -11
View File
@@ -1,6 +1,8 @@
import base64
import io
import json
import os
import sys
import click
import httpx
@@ -8,12 +10,15 @@ import pytest
from langgraph_cli.deploy import (
_call_host_backend_with_optional_tenant,
_create_host_backend_client,
_docker_config_for_token,
_Emitter,
_env_without_deployment_name,
_parse_env_from_config,
_resolve_env_path,
normalize_image_name,
_smith_dashboard_base_url,
normalize_image_tag,
normalize_name,
)
from langgraph_cli.host_backend import HostBackendClient, HostBackendError
@@ -40,30 +45,33 @@ class TestDockerConfigForToken:
assert "gcr.io" in data["auths"]
class TestNormalizeImageName:
class TestNormalizeName:
def test_simple_name(self):
assert normalize_image_name("myapp") == "myapp"
assert normalize_name("myapp") == "myapp"
def test_uppercase_lowered(self):
assert normalize_image_name("MyApp") == "myapp"
assert normalize_name("MyApp") == "myapp"
def test_special_chars_replaced(self):
assert normalize_image_name("my app!@#v2") == "my-app-v2"
assert normalize_name("my app!@#v2") == "my-app-v2"
def test_dots_and_hyphens_kept(self):
assert normalize_image_name("my-app.v2") == "my-app.v2"
def test_dots_replaced_with_hyphens(self):
assert normalize_name("my-app.v2") == "my-app-v2"
def test_underscores_replaced_with_hyphens(self):
assert normalize_name("simple_graph_name") == "simple-graph-name"
def test_leading_trailing_stripped(self):
assert normalize_image_name("--my-app..") == "my-app"
assert normalize_name("--my-app..") == "my-app"
def test_empty_string_returns_app(self):
assert normalize_image_name("") == "app"
assert normalize_name("") == "app"
def test_none_returns_app(self):
assert normalize_image_name(None) == "app"
assert normalize_name(None) == "app"
def test_all_invalid_chars_returns_app(self):
assert normalize_image_name("!!!") == "app"
assert normalize_name("!!!") == "app"
class TestNormalizeImageTag:
@@ -271,3 +279,256 @@ class TestCallHostBackendWithOptionalTenant:
_call_host_backend_with_optional_tenant(
client, lambda c: c.list_deployments()
)
def test_workspace_prompt_blocked_by_no_input(self, monkeypatch):
"""With _no_input=True, 403 requiring workspace should raise ClickException."""
import langgraph_cli.deploy as deploy_mod
monkeypatch.setattr(deploy_mod, "_no_input", True)
requires_workspace = '{"detail":"requires workspace specification"}'
client = self._make_client(
lambda req: httpx.Response(403, text=requires_workspace)
)
with pytest.raises(click.ClickException, match="workspace"):
_call_host_backend_with_optional_tenant(
client, lambda c: c.list_deployments()
)
# ---------------------------------------------------------------------------
# _Emitter JSON mode
# ---------------------------------------------------------------------------
class TestEmitterJsonMode:
"""Verify that _Emitter in json_mode writes valid JSON-lines to stdout."""
def _capture(self, fn):
"""Run fn with stdout captured and return parsed JSON objects."""
buf = io.StringIO()
old = sys.stdout
sys.stdout = buf
try:
fn()
finally:
sys.stdout = old
lines = [line for line in buf.getvalue().splitlines() if line.strip()]
return [json.loads(line) for line in lines]
def test_step_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.step(1, "Building image"))
assert len(events) == 1
assert events[0]["event"] == "step"
assert events[0]["step"] == 1
assert events[0]["message"] == "Building image"
def test_info_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.info("All good"))
assert events[0]["event"] == "info"
assert events[0]["message"] == "All good"
def test_warn_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.warn("Careful"))
assert events[0]["event"] == "warn"
def test_error_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.error("Boom"))
assert events[0]["event"] == "error"
assert events[0]["message"] == "Boom"
def test_status_change_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.status_change("building", 12.345))
assert events[0]["event"] == "status_change"
assert events[0]["status"] == "building"
assert events[0]["elapsed_seconds"] == 12.3
assert events[0]["message"] == "building... (12s)"
def test_status_change_event_with_minutes(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.status_change("deploying", 95.0))
assert events[0]["message"] == "deploying... (1m 35s)"
def test_log_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.log("some output"))
assert events[0] == {"event": "log", "message": "some output"}
def test_status_url_event(self):
em = _Emitter(json_mode=True)
events = self._capture(
lambda: em.status_url("https://smith.langchain.com/deploy/123")
)
assert events[0]["event"] == "status_url"
assert events[0]["url"] == "https://smith.langchain.com/deploy/123"
def test_result_event_full(self):
em = _Emitter(json_mode=True)
events = self._capture(
lambda: em.result(
"succeeded",
deployment_id="dep-1",
url="https://app.example.com",
status_url="https://smith.langchain.com/deploy/dep-1",
)
)
assert events[0]["event"] == "result"
assert events[0]["status"] == "succeeded"
assert events[0]["deployment_id"] == "dep-1"
assert events[0]["message"] == "Deployment successful!"
assert events[0]["url"] == "https://app.example.com"
assert events[0]["status_url"] == "https://smith.langchain.com/deploy/dep-1"
def test_result_event_minimal(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.result("failed", deployment_id="dep-2"))
assert events[0]["event"] == "result"
assert events[0]["status"] == "failed"
assert events[0]["message"] == "Deployment failed"
assert "url" not in events[0]
assert "status_url" not in events[0]
def test_heartbeat_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.heartbeat("building", 30.789))
assert events[0]["event"] == "heartbeat"
assert events[0]["elapsed_seconds"] == 30.8
assert events[0]["message"] == "building... (30s)"
def test_heartbeat_silent_in_text_mode(self, capsys):
em = _Emitter(json_mode=False)
em.heartbeat("building", 10.0)
captured = capsys.readouterr()
assert captured.out == ""
def test_upload_progress_event(self):
em = _Emitter(json_mode=True)
events = self._capture(lambda: em.upload_progress(5.678, 42))
assert events[0]["event"] == "upload_progress"
assert events[0]["size_mb"] == 5.7
assert events[0]["pct"] == 42
# ---------------------------------------------------------------------------
# _Emitter text mode (non-json)
# ---------------------------------------------------------------------------
class TestEmitterTextMode:
"""Verify that _Emitter in text mode uses click.echo/click.secho."""
def test_step_writes_text(self, capsys):
em = _Emitter(json_mode=False)
em.step(1, "Hello")
captured = capsys.readouterr()
assert "1. Hello" in captured.out
def test_log_writes_text(self, capsys):
em = _Emitter(json_mode=False)
em.log("my line")
captured = capsys.readouterr()
assert "my line" in captured.out
def test_result_succeeded_text(self, capsys):
em = _Emitter(json_mode=False)
em.result("succeeded", deployment_id="d1", url="https://app.test")
captured = capsys.readouterr()
lines = [line.strip() for line in captured.out.splitlines() if line.strip()]
assert "Deployment successful!" in lines
assert "URL: https://app.test" in lines
# ---------------------------------------------------------------------------
# --no-input guard on _create_host_backend_client
# ---------------------------------------------------------------------------
class TestCreateHostBackendClientNoInput:
def test_raises_when_no_api_key_and_no_input(self, monkeypatch, tmp_path):
import langgraph_cli.deploy as deploy_mod
monkeypatch.setattr(deploy_mod, "_no_input", True)
monkeypatch.delenv("LANGSMITH_API_KEY", raising=False)
monkeypatch.delenv("LANGCHAIN_API_KEY", raising=False)
monkeypatch.delenv("LANGGRAPH_HOST_API_KEY", raising=False)
with pytest.raises(click.ClickException, match="API key"):
_create_host_backend_client(
host_url="https://api.example.com",
api_key=None,
env_vars={},
)
def test_succeeds_with_api_key_in_env(self, monkeypatch, tmp_path):
import langgraph_cli.deploy as deploy_mod
monkeypatch.setattr(deploy_mod, "_no_input", True)
monkeypatch.setenv("LANGSMITH_API_KEY", "lsv2_test")
client = _create_host_backend_client(
host_url="https://api.example.com",
api_key=None,
env_vars={},
)
assert client is not None
class TestSmithDashboardBaseUrl:
def test_none_returns_default(self):
assert _smith_dashboard_base_url(None) == "https://smith.langchain.com"
def test_empty_returns_default(self):
assert _smith_dashboard_base_url("") == "https://smith.langchain.com"
def test_prod_host_url(self):
assert (
_smith_dashboard_base_url("https://api.host.langchain.com")
== "https://smith.langchain.com"
)
def test_dev_host_url(self):
assert (
_smith_dashboard_base_url("https://dev.api.host.langchain.com")
== "https://dev.smith.langchain.com"
)
def test_eu_host_url(self):
assert (
_smith_dashboard_base_url("https://eu.api.host.langchain.com")
== "https://eu.smith.langchain.com"
)
def test_staging_host_url(self):
assert (
_smith_dashboard_base_url("https://staging.api.host.langchain.com")
== "https://staging.smith.langchain.com"
)
def test_localhost(self):
assert (
_smith_dashboard_base_url("http://localhost:8080")
== "http://localhost:8080"
)
def test_localhost_trailing_slash(self):
assert (
_smith_dashboard_base_url("http://localhost:8080/")
== "http://localhost:8080"
)
def test_127_0_0_1(self):
assert (
_smith_dashboard_base_url("http://127.0.0.1:3000")
== "http://127.0.0.1:3000"
)
def test_unknown_domain_returns_default(self):
assert (
_smith_dashboard_base_url("https://custom.example.com")
== "https://smith.langchain.com"
)
@@ -1,116 +0,0 @@
import pathlib
import sys
import pytest
from langgraph_cli import entrypoint
def test_main_uses_legacy_cli_when_go_flag_disabled(monkeypatch):
captured = {}
def fake_legacy(argv):
captured["argv"] = list(argv)
monkeypatch.delenv("LANGGRAPH_USE_GO_CLI", raising=False)
monkeypatch.setattr(entrypoint, "_legacy_cli", fake_legacy)
entrypoint.main(["build", "-t", "demo"])
assert captured == {"argv": ["build", "-t", "demo"]}
def test_main_execs_go_cli_when_flag_enabled(monkeypatch, tmp_path):
binary_path = tmp_path / "langgraph"
binary_path.write_text("")
captured = {}
def fake_execvpe(file, args, env):
captured["file"] = file
captured["args"] = args
captured["env"] = env.copy()
raise SystemExit(0)
monkeypatch.setenv("LANGGRAPH_USE_GO_CLI", "1")
monkeypatch.setenv("LANGGRAPH_GO_CLI_PATH", str(binary_path))
monkeypatch.delenv("LANGGRAPH_CALLING_PYTHON", raising=False)
monkeypatch.setattr(entrypoint.os, "execvpe", fake_execvpe)
with pytest.raises(SystemExit, match="0"):
entrypoint.main(["dev", "--port", "8000"])
assert captured["file"] == str(binary_path)
assert captured["args"] == [str(binary_path), "dev", "--port", "8000"]
assert captured["env"]["LANGGRAPH_CALLING_PYTHON"] == sys.executable
def test_main_preserves_existing_calling_python(monkeypatch, tmp_path):
binary_path = tmp_path / "langgraph"
binary_path.write_text("")
captured = {}
def fake_execvpe(file, args, env):
captured["env"] = env.copy()
raise SystemExit(0)
monkeypatch.setenv("LANGGRAPH_USE_GO_CLI", "true")
monkeypatch.setenv("LANGGRAPH_GO_CLI_PATH", str(binary_path))
monkeypatch.setenv("LANGGRAPH_CALLING_PYTHON", "/custom/python")
monkeypatch.setattr(entrypoint.os, "execvpe", fake_execvpe)
with pytest.raises(SystemExit, match="0"):
entrypoint.main(["dev"])
assert captured["env"]["LANGGRAPH_CALLING_PYTHON"] == "/custom/python"
def test_main_errors_when_go_cli_requested_but_binary_missing(
monkeypatch, capsys, tmp_path
):
missing_path = tmp_path / "missing-langgraph"
monkeypatch.setenv("LANGGRAPH_USE_GO_CLI", "1")
monkeypatch.setenv("LANGGRAPH_GO_CLI_PATH", str(missing_path))
with pytest.raises(SystemExit, match="1"):
entrypoint.main(["build"])
err = capsys.readouterr().err
assert "LANGGRAPH_GO_CLI_PATH points to a missing file" in err
def test_resolve_go_cli_path_prefers_override(monkeypatch, tmp_path):
override = tmp_path / "custom-langgraph"
override.write_text("")
bundled = tmp_path / "bin" / "langgraph"
bundled.parent.mkdir()
bundled.write_text("")
monkeypatch.setenv("LANGGRAPH_GO_CLI_PATH", str(override))
monkeypatch.setattr(entrypoint, "_bundled_go_cli_path", lambda: bundled)
assert entrypoint._resolve_go_cli_path() == override.resolve()
def test_resolve_go_cli_path_uses_bundled_binary(monkeypatch, tmp_path):
bundled = tmp_path / "bin" / "langgraph"
bundled.parent.mkdir()
bundled.write_text("")
monkeypatch.delenv("LANGGRAPH_GO_CLI_PATH", raising=False)
monkeypatch.setattr(entrypoint, "_bundled_go_cli_path", lambda: bundled)
assert entrypoint._resolve_go_cli_path() == bundled
def test_resolve_go_cli_path_returns_none_when_nothing_available(monkeypatch):
monkeypatch.delenv("LANGGRAPH_GO_CLI_PATH", raising=False)
monkeypatch.setattr(
entrypoint,
"_bundled_go_cli_path",
lambda: pathlib.Path("/definitely/not/present/langgraph"),
)
assert entrypoint._resolve_go_cli_path() is None
@@ -1,67 +0,0 @@
import os
import shutil
import subprocess
import pytest
@pytest.fixture
def go_cli_env():
if os.environ.get("LANGGRAPH_USE_GO_CLI") != "1":
pytest.skip("Go CLI smoke tests only run when LANGGRAPH_USE_GO_CLI=1")
langgraph = shutil.which("langgraph")
assert langgraph is not None, "langgraph executable is not installed"
env = os.environ.copy()
env["LANGGRAPH_USE_GO_CLI"] = "1"
return langgraph, env
def test_go_cli_help(go_cli_env):
langgraph, env = go_cli_env
result = subprocess.run(
[langgraph, "--help"],
capture_output=True,
text=True,
env=env,
check=False,
)
assert result.returncode == 0, result.stderr
assert "Usage: langgraph" in result.stdout
def test_go_cli_validate(go_cli_env, tmp_path):
langgraph, env = go_cli_env
config_path = tmp_path / "langgraph.json"
config_path.write_text(
'{"dependencies": ["langchain"], "graphs": {"agent": "./agent.py:graph"}}'
)
result = subprocess.run(
[langgraph, "validate", "-c", str(config_path)],
capture_output=True,
text=True,
env=env,
check=False,
)
assert result.returncode == 0, result.stderr
assert "is valid" in result.stdout
def test_go_cli_dev_help(go_cli_env):
langgraph, env = go_cli_env
result = subprocess.run(
[langgraph, "dev", "--help"],
capture_output=True,
text=True,
env=env,
check=False,
)
assert result.returncode == 0, result.stderr
assert "Run LangGraph API server in development mode" in result.stdout
+1 -1
View File
@@ -5,7 +5,7 @@ description = "uv workspace monorepo example for LangGraph CLI integration test"
requires-python = ">=3.11"
dependencies = [
"langgraph>=0.6.0,<2",
"langchain-core>=0.2.14",
"langchain-core>=1.3.3",
]
[tool.uv.workspace]
+21 -8
View File
@@ -21,7 +21,7 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "langchain-core", specifier = ">=0.2.14" },
{ name = "langchain-core", specifier = ">=1.3.3" },
{ name = "langgraph", specifier = ">=0.6.0,<2" },
{ name = "shared", editable = "libs/shared" },
]
@@ -215,10 +215,11 @@ wheels = [
[[package]]
name = "langchain-core"
version = "1.2.27"
version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsonpatch" },
{ name = "langchain-protocol" },
{ name = "langsmith" },
{ name = "packaging" },
{ name = "pydantic" },
@@ -227,9 +228,21 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "uuid-utils" },
]
sdist = { url = "https://files.pythonhosted.org/packages/13/5c/56d19a252bbb26247b7a7cd20821d48804d7ca03212fec709cd8db7c2516/langchain_core-1.2.27.tar.gz", hash = "sha256:c18372e4c4c1454d49bf23a2e484431e71bd39b64173a0f621f0fc283d7183a4", size = 844935, upload-time = "2026-04-07T14:56:32.364Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/ae/8b74458fc3850ec3d150eb9f45e857db129dafa801fb5cf173dfc9f8bbf3/langchain_core-1.3.3.tar.gz", hash = "sha256:fa510a5db8efdc0c6ff41c0939fb5c00a0183c11f6b84233e892e3227ff69182", size = 915041, upload-time = "2026-05-05T19:02:36.612Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c1/c3/6e0865bc130c448270eb9511b47863a3f9145cdb519b19f6e4758fa63d6f/langchain_core-1.2.27-py3-none-any.whl", hash = "sha256:9ecd6b0393b969fe88f6b9b309367134080ab095946d79e6937dd3911aa42bd5", size = 508315, upload-time = "2026-04-07T14:56:30.93Z" },
{ url = "https://files.pythonhosted.org/packages/1f/01/4771b7ab2af1d1aba5b710bd8f13d9225c609425214b357590a17b01be77/langchain_core-1.3.3-py3-none-any.whl", hash = "sha256:18aae8506f37da7f74398492279a7d6efcee4f8e23c4c41c7af080eeb7ef7bd1", size = 543857, upload-time = "2026-05-05T19:02:34.52Z" },
]
[[package]]
name = "langchain-protocol"
version = "0.0.15"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4f/24/9777489d6fbbee64af0c8f96d4f840239c408cf694f3394672807dafc490/langchain_protocol-0.0.15.tar.gz", hash = "sha256:9ab2d11ee73944754f10e037e717098d3a6796f0e58afa9cadda6154e7655ade", size = 5862, upload-time = "2026-05-01T22:30:04.748Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/7a/9c97a7b9cbe4c5dc6a44cdb1545450c28f0c8ce89b9c1f0ee7fbad896263/langchain_protocol-0.0.15-py3-none-any.whl", hash = "sha256:461eb794358f83d5e42635a5797799ffec7b4702314e34edf73ac21e75d3ef79", size = 6982, upload-time = "2026-05-01T22:30:03.877Z" },
]
[[package]]
@@ -290,7 +303,7 @@ wheels = [
[[package]]
name = "langsmith"
version = "0.7.26"
version = "0.7.31"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -303,9 +316,9 @@ dependencies = [
{ name = "xxhash" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/86/6de4f6f0451a9658f26f633e0bb090552a4dafd7df3f1ae7f0d40558e67e/langsmith-0.7.26.tar.gz", hash = "sha256:a3e06f3d689ce7195717aa6b8f91082319819ec7ea9b9a62cdcd3d9dc25bfc7b", size = 1146118, upload-time = "2026-04-06T15:01:03.336Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e6/11/696019490992db5c87774dc20515529ef42a01e1d770fb754ed6d9b12fb0/langsmith-0.7.31.tar.gz", hash = "sha256:331ee4f7c26bb5be4022b9859b7d7b122cbf8c9d01d9f530114c1914b0349ffb", size = 1178480, upload-time = "2026-04-14T17:55:41.242Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/8e/7eb7d65ce62e98e74b9f18f193ea7ac3996d4fbd71fffcc67d0f7ba3103e/langsmith-0.7.26-py3-none-any.whl", hash = "sha256:fe5c877972cea450c1c48251c8fae0f18543c8d19dfdb9ff9a9c4263763dde4e", size = 360160, upload-time = "2026-04-06T15:01:01.516Z" },
{ url = "https://files.pythonhosted.org/packages/1d/a1/a013cf458c301cda86a213dd153ce0a01c93f1ab5833f951e6a44c9763ce/langsmith-0.7.31-py3-none-any.whl", hash = "sha256:0291d49203f6e80dda011af1afda61eb0595a4d697adb684590a8805e1d61fb6", size = 373276, upload-time = "2026-04-14T17:55:39.677Z" },
]
[[package]]
@@ -711,7 +724,7 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "langchain-core", specifier = ">=0.2.14" },
{ name = "langchain-core", specifier = ">=1.3.3" },
{ name = "langgraph", specifier = ">=0.6.0,<2" },
]

Some files were not shown because too many files have changed in this diff Show More