Compare commits

..
Author SHA1 Message Date
Sera 62e86bb6ca feat(sdk-py): add select to threads.get_state and get_history 2026-11-03 09:13:41 -05:00
ataller-lc 3c916d7505 feat(sdk-py): remove fairness_weight from PrioritySettings (#9441)
`PrioritySettings` no longer has `fairness_weight`, since a group's
weight belongs to the group rather than to each thread. The docstrings
now match what the server does with the other two keys.

- The server stores `priority` and `fairness_group` on the thread.
langgraph-api 0.18.0, the first stable release with the priority
callback, ignores a `fairness_weight` and logs a warning, so callbacks
written against 0.4.9 to 0.4.11 keep working. A callback annotated `->
PrioritySettings` that still returns the key gets a type-checker error.
- `PrioritySettings.priority`'s docstring and examples now say -5 runs
first and 5 runs last, matching the server. 0.4.9 to 0.4.11 say the
reverse, and their example gives premium users 3, which runs them after
everyone at the default 0.
- `fairness_group` is currently disabled: it has no effect on run order
for now, and its docstring says so. It also gives the limit: at most 256
characters, and an empty string takes the default, like a missing key.
- The changelog gets a `0.4.12` section, and the old `Unreleased`
heading becomes `0.4.11 and earlier`, since everything under it has
shipped.

## Test plan

- [x] `test_every_settings_key_is_optional` expects only `priority` and
`fairness_group`, and fails with the key put back.
- [x] `make test` in `libs/sdk-py`: 639 passed. `make lint` clean.
2026-11-03 01:03:26 +00:00
Elior Nataf Lackritz 9791c686ee fix(sdk-py): rejoin a run stream that closes before the run ends (#9554)
Fixes #9016

A run stream whose response ends cleanly before the run does, say on a
proxy timeout or a server restart, looked like a finished run:
`HttpClient.stream` and `SyncHttpClient.stream` reconnect to the
`Location` only when the connection fails, so the caller got a cut
answer and no error. Servers generally send no `end` event when a run
ends, so a clean close alone cannot tell the cases apart, and
reconnecting on every clean close would end every finished run in five
empty reconnects and an error.

**The rule.** After a clean close, when the stream's `Location` is a
run's stream (`<prefix>/threads/{thread_id}/runs/{run_id}/stream`, any
query) and no `end` or `error` event arrived across reconnects, the
client sends `GET <prefix>/threads/{thread_id}/runs/{run_id}` with the
reconnect headers, but `Accept: application/json` and no
`Last-Event-ID`.

- `pending` or `running`, or a 429, 5xx or transport error (the restart
that closed the stream can fail the check too): handled like a dropped
connection, with the same `Last-Event-ID`, attempt budget, backoff and
warnings, so repeated closes with no new events end in the usual
max-attempts error.
- Any other status, a 4xx other than 429, or a body that isn't a run:
the stream ends as it does today. The check never raises and uses the
client's normal timeout.

Thread streams, streams without a `Location`, and the threadless
`/runs/{run_id}/stream` `Location` older servers send for stateless runs
are unchanged. A stateless run whose `Location` names its temporary
thread is checked like any other run: it rejoins while the run is
pending or running, and once the run ends and the thread is deleted the
check gets a 404 and the stream ends. As after a drop, runs created
without `stream_resumable=True` reconnect without a cursor and warn.

**Cost.** One extra GET at the end of every run stream that ends without
an `end` or `error` event. It returns the whole run record, `kwargs`
(the run's input) included, so a run started with a large input
downloads it again. With a server that sends neither event, that is
every run stream, so the iterator finishes about one round trip later.

**Tests.** The new tests mock the transport, and `main` stops at the
first clean close. 38 of the 50 new cases fail on `main`; the 12
no-check cases pass on both.

Thanks to @Bruce-Yii for the report and root cause, and to
@anushamukka9, whose #9023 proposed reconnecting after a clean close;
this version asks the run first.

Paired JS change: https://github.com/langchain-ai/langgraphjs/pull/3185
2026-11-02 19:57:22 +00:00
dependabot[bot] ee3c043bdd chore(deps): bump langgraph from 1.2.3 to 1.2.14 in /libs/cli (#9512)
Bumps [langgraph](https://github.com/langchain-ai/langgraph) from 1.2.3
to 1.2.14.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraph/releases">langgraph's
releases</a>.</em></p>
<blockquote>
<h2>langgraph==1.2.14</h2>
<p>Changes since 1.2.13</p>
<ul>
<li>release(langgraph): 1.2.14 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9215">#9215</a>)</li>
</ul>
<h2>langgraph==1.2.13</h2>
<p>Changes since 1.2.12</p>
<ul>
<li>fix(langgraph): fork before replaying an update checkpoint the
thread moved past (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9170">#9170</a>)</li>
<li>fix(langgraph): keep an update_state on an older checkpoint out of
its other branches (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9165">#9165</a>)</li>
<li>fix(langgraph): keep DeltaChannel counters on every update_state
path (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9142">#9142</a>)</li>
<li>release(langgraph): 1.2.13 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9205">#9205</a>)</li>
<li>fix(langgraph): don't walk history for a DeltaChannel that was never
written (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9141">#9141</a>)</li>
<li>fix(langgraph): don't replay an abandoned branch into a DeltaChannel
fork (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8548">#8548</a>)</li>
<li>chore(deps): bump notebook from 7.5.6 to 7.5.7 in /libs/langgraph
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9168">#9168</a>)</li>
<li>fix(langgraph): hydrate subgraph delta channels with the
caller-resolved saver (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8538">#8538</a>)</li>
<li>fix(langgraph): stop reporting answered interrupts in get_state (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9103">#9103</a>)</li>
<li>chore(deps-dev): bump the minor-and-patch group across 1 directory
with 5 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9155">#9155</a>)</li>
<li>chore(deps-dev): bump the minor-and-patch group in
/libs/checkpoint-conformance with 2 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9145">#9145</a>)</li>
<li>chore(deps): bump the minor-and-patch group in /libs/sdk-py with 5
updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9149">#9149</a>)</li>
<li>chore(deps): bump tornado from 6.5.8 to 6.5.9 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9160">#9160</a>)</li>
<li>chore(deps-dev): bump types-requests from 2.33.0.20260712 to
2.33.0.20260906 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9156">#9156</a>)</li>
<li>chore(deps): bump pyjwt from 2.15.0 to 2.15.1 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9140">#9140</a>)</li>
<li>chore(deps): bump urllib3 from 2.7.0 to 2.8.0 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9138">#9138</a>)</li>
<li>chore(deps): bump pyjwt from 2.14.0 to 2.15.0 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9139">#9139</a>)</li>
<li>chore(deps): bump pyjwt from 2.13.0 to 2.14.0 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9126">#9126</a>)</li>
</ul>
<h2>langgraph==1.2.12</h2>
<p>Changes since 1.2.11</p>
<ul>
<li>release(langgraph): 1.2.12 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8987">#8987</a>)</li>
<li>chore(deps): bump soupsieve from 2.8.4 to 2.9 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8958">#8958</a>)</li>
<li>feat(langgraph): add response_schema to interrupt() (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8886">#8886</a>)</li>
<li>fix(langgraph): type undeclared v3 stream projections (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8596">#8596</a>)</li>
<li>chore(langgraph): bump mistune to 3.3.4 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8804">#8804</a>)</li>
<li>chore(deps): bump the minor-and-patch group across 1 directory with
7 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8779">#8779</a>)</li>
<li>chore(deps): bump the minor-and-patch group across 1 directory with
4 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8782">#8782</a>)</li>
<li>chore(deps): bump tornado from 6.5.7 to 6.5.8 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8792">#8792</a>)</li>
<li>chore(deps): bump the major group in /libs/langgraph with 2 updates
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8783">#8783</a>)</li>
<li>fix(langgraph): detect subgraphs from bytecode instead of source (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8569">#8569</a>)</li>
</ul>
<h2>langgraph==1.2.11</h2>
<p>Changes since 1.2.10</p>
<ul>
<li>release(langgraph): 1.2.11 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8595">#8595</a>)</li>
<li>feat(langgraph): expose <code>trace_policy</code> on
<code>add_node</code> (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8523">#8523</a>)</li>
<li>chore(deps): bump the minor-and-patch group across 1 directory with
7 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8533">#8533</a>)</li>
<li>chore(deps): bump the minor-and-patch group across 1 directory with
5 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8532">#8532</a>)</li>
<li>release(checkpoint-postgres): 3.1.2 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8565">#8565</a>)</li>
<li>release(checkpoint): 4.2.0 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8563">#8563</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/70dd64065bffaa3b6ab61a33f1f020fb54db8efa"><code>70dd640</code></a>
release(langgraph): 1.2.14 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9215">#9215</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/55b0e36c51d2922b11640b980fcfca1064243a96"><code>55b0e36</code></a>
release(sdk-py): 0.4.6 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9214">#9214</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/d89c7e100ec6fc1512544d23b0a7c5169699ef3e"><code>d89c7e1</code></a>
fix(sdk-py): percent-encode thread_id and assistant_id in thread stream
reque...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/2d942085e214ef6b99b6f54ed4d544a7c7c5ac56"><code>2d94208</code></a>
fix(langgraph): fork before replaying an update checkpoint the thread
moved p...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/456e2b1acc67c8c6c13bbc0e0ca8ba7e11503d17"><code>456e2b1</code></a>
fix(langgraph): keep an update_state on an older checkpoint out of its
other ...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/1b84cb08733a2a0a102ed19845bf20511dcc6535"><code>1b84cb0</code></a>
fix(langgraph): keep DeltaChannel counters on every update_state path
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9142">#9142</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/8e15e0d0bcffa6d3d5a776206e6e8163a3595463"><code>8e15e0d</code></a>
release(langgraph): 1.2.13 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9205">#9205</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/524b320e30a31d2660ee1a7e9b312882fe4a6c08"><code>524b320</code></a>
fix(langgraph): don't walk history for a DeltaChannel that was never
written ...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/9a0394d88b2211f299dcd69df92db3480c69ee61"><code>9a0394d</code></a>
fix(langgraph): don't replay an abandoned branch into a DeltaChannel
fork (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8">#8</a>...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/7dc9195e4141c8fbd8118581b3dd61d158628aa8"><code>7dc9195</code></a>
chore(deps): bump notebook from 7.5.6 to 7.5.7 in /libs/langgraph (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9168">#9168</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langgraph/compare/1.2.3...1.2.14">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-11-01 20:33:29 -08:00
dependabot[bot] d5b927ae4c chore(deps): bump the minor-and-patch group in /libs/cli/js-examples with 9 updates (#9528)
Bumps the minor-and-patch group in /libs/cli/js-examples with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [@langchain/core](https://github.com/langchain-ai/langchainjs) |
`1.2.13` | `1.3.1` |
|
[@langchain/langgraph](https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core)
| `1.4.18` | `1.5.3` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.70.1` | `8.72.1` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.70.1` | `8.72.1` |
| [dotenv](https://github.com/motdotla/dotenv) | `18.0.4` | `18.0.9` |
| [eslint](https://github.com/eslint/eslint) | `10.11.0` | `10.13.0` |
|
[eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier)
| `5.5.6` | `5.5.7` |
| [prettier](https://github.com/prettier/prettier) | `3.9.9` | `3.9.10`
|
| [ts-jest](https://github.com/kulshekhar/ts-jest) | `29.4.14` |
`29.4.15` |

Updates `@langchain/core` from 1.2.13 to 1.3.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchainjs/releases">@​langchain/core's
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.3.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/12026">#12026</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/178af31ee41b7e36fa2971af61ade6a5460b6d2e"><code>178af31</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- fix(core): deliver callbacks once to a handler registered twice by
merged configs</p>
<p>Merging two runnable configs that both carry the same callback
handler, as happens when an agent's <code>streamEvents()</code> runs
inside a LangGraph node with tracing on, registered that handler twice,
so it received every callback twice: every streamed chunk arrived twice.
The configured callback manager now keeps each handler once, in order,
including a handler passed as a plain object of methods that merged
configs wrapped twice.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/12042">#12042</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/50343ba94c2388b3d3a7d4d6cd0977fa60a90294"><code>50343ba</code></a>
Thanks <a href="https://github.com/sera-wi"><code>@​sera-wi</code></a>!
- fix(core): include node <code>metadata</code> in
<code>Graph.toJSON()</code> output when set, matching Python</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/12048">#12048</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/304d190a30343ecb8ae5f06081a2b5a6d93d9968"><code>304d190</code></a>
Thanks <a href="https://github.com/sera-wi"><code>@​sera-wi</code></a>!
- fix(core): escape <code>drawMermaid</code> node metadata labels and
leave out object values, so metadata with brackets or objects no longer
breaks Mermaid parsing</p>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.3.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11986">#11986</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/2caf64da3f02730f3cd2aab0c15de6eab41a01da"><code>2caf64d</code></a>
Thanks <a
href="https://github.com/richardscarrott"><code>@​richardscarrott</code></a>!
- feat: stream tool-call args as <code>tool-call-delta</code> slices</p>
<p>Tool-call and server-tool-call args now stream as
<code>tool-call-delta</code> events, which carry only the new slice of
the args, instead of <code>block-delta</code> snapshots of the args so
far. A large tool call now costs about the same bytes on the wire as the
same length of text.</p>
<ul>
<li>This covers the compat bridge (any model that only implements
<code>_streamResponseChunks</code>), the OpenAI-compatible converter
(OpenAI Completions, Groq, Mistral, IBM, OpenRouter, Perplexity,
DeepSeek, Fireworks, Together AI and xAI Completions), OpenAI and xAI
Responses, Anthropic (client and server tool calls) and Bedrock
Converse. Google and Ollama still send one complete snapshot per
call.</li>
<li>A tool call's <code>id</code>, <code>name</code> and
<code>index</code> go on <code>content-block-start</code>, which carries
no args. An <code>id</code> or <code>name</code> that arrives later
reaches consumers on <code>content-block-finish</code>.</li>
<li>If you read raw events and their <code>block-delta</code>
<code>fields.args</code> mid-stream, or write agent stream transformers
that do, read <code>tool-call-delta</code> instead. <code>await
stream</code>, <code>.toolCalls</code> and the assembled message are
unchanged.</li>
<li>Upgrade your clients first. Older clients mishandle
<code>tool-call-delta</code>. You need
<code>@langchain/langgraph-sdk</code> 1.12.6 or later;
<code>@langchain/react</code>, <code>@langchain/vue</code>,
<code>@langchain/svelte</code> or <code>@langchain/angular</code> 1.2.6
or later; and <code>langchain-core</code> 1.6.13 or later for Python
clients.</li>
<li>To hold back, pin <code>@langchain/core</code> below 1.3.0. That
also keeps you on <code>@langchain/openai</code>,
<code>@langchain/anthropic</code>, <code>@langchain/aws</code> and
<code>@langchain/xai</code> versions that send snapshots.</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.20</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11922">#11922</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d71c542fb1d9cd1ceb310a4759591c024cf0d57d"><code>d71c542</code></a>
Thanks <a
href="https://github.com/richardscarrott"><code>@​richardscarrott</code></a>!
- feat(core): add <code>midConversationSystemMessages</code> and
<code>midConversationTools</code> to <code>ModelProfile</code>. They say
whether a chat model sends a system message after the first turn in
place, and whether it can add a tool through a message rather than the
request's tool list. Either way the prompt prefix before it stays
unchanged.</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.19</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11982">#11982</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/5af4684bd19a16bb59950801da92245733482fb3"><code>5af4684</code></a>
Thanks <a
href="https://github.com/richardscarrott"><code>@​richardscarrott</code></a>!
- feat(core): read <code>tool-call-delta</code> in
<code>ChatModelStream</code></p>
<ul>
<li><code>ChatModelStream</code> appends <code>tool-call-delta</code>
slices to a tool call's args, and still replaces them on
<code>block-delta</code></li>
<li>It ignores delta types it doesn't recognize instead of throwing
<code>Unknown delta type</code></li>
</ul>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.18</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11956">#11956</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/93d62b008e13e10f28724a5ab09ab6eda584f1cb"><code>93d62b0</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
Require langsmith &gt;=0.11.0 to include the restored NullableHeaders
type declarations.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11822">#11822</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d3e6fe66a5f902fa615f076ca71d9541f1291166"><code>d3e6fe6</code></a>
Thanks <a href="https://github.com/dqbd"><code>@​dqbd</code></a>! - Add
explicit agent address support to <code>LangChainTracer</code>, inherit
project and address destinations together, and delegate default
destination resolution to the LangSmith SDK.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11150">#11150</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/cea15cf9ef5216125b24f23bb5ad06b7c1a91b33"><code>cea15cf</code></a>
Thanks <a
href="https://github.com/mdnanocom"><code>@​mdnanocom</code></a>! -
fix(core): defer tool call chunk collapsing in AIMessageChunk to fix
O(n²) streaming aggregation</p>
<p><code>AIMessageChunk</code> eagerly re-ran
<code>collapseToolCallChunks</code> in its constructor on every streamed
delta <code>concat</code>, re-parsing all accumulated tool args each
time — O(n²) over a stream, which can block the event loop for seconds
on long tool-call streams. Collapsing now runs lazily, once, on first
read of <code>tool_calls</code> / <code>invalid_tool_calls</code>.</p>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.17</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/e8c5dc82ba52cff3bc08cd71f963a69596d7ea4d"><code>e8c5dc8</code></a>
chore: version packages (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12047">#12047</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/b5135cc189adbf313f4e92c1527f96a40a546977"><code>b5135cc</code></a>
feat(openai,langchain): support Decisions API and agent middleware (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/11930">#11930</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/52d2ba8ea50149f2314df63b79582f8508034a3b"><code>52d2ba8</code></a>
fix(aws): use this.caller in ChatBedrockConverse to support retries (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/11463">#11463</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/178af31ee41b7e36fa2971af61ade6a5460b6d2e"><code>178af31</code></a>
fix(core): deliver callbacks once to a handler registered twice by
merged con...</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/304d190a30343ecb8ae5f06081a2b5a6d93d9968"><code>304d190</code></a>
fix(core): make drawMermaid node metadata labels safe (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12048">#12048</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/50343ba94c2388b3d3a7d4d6cd0977fa60a90294"><code>50343ba</code></a>
fix(core): include node metadata in Graph.toJSON() (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12042">#12042</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/d1d0692d8498d7e1521c854d90af0395535df4d8"><code>d1d0692</code></a>
chore: version packages (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12044">#12044</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/95b191b1928ee8f4e20e51aa821f5c5fd4b8b3da"><code>95b191b</code></a>
feat(openai): refresh model profiles from models.dev (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12046">#12046</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/81577b3e84c3f38581e76e8335faa89511e99d74"><code>81577b3</code></a>
fix(langchain): re-throw a drained tool from the agent's ToolNode (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12043">#12043</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/4cbf0f5938710ce1669c679febdbea41b733a100"><code>4cbf0f5</code></a>
chore(deps): bump the npm_and_yarn group across 2 directories with 1
update (...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchainjs/compare/@langchain/core@1.2.13...@langchain/core@1.3.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@langchain/langgraph` from 1.4.18 to 1.5.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraphjs/releases">@​langchain/langgraph's
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/langgraph-api</code><a
href="https://github.com/1"><code>@​1</code></a>.5.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2961">#2961</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/1631fd895723c4553345ceb5d9fc425b68bda959"><code>1631fd8</code></a>
Thanks <a href="https://github.com/soarez"><code>@​soarez</code></a>! -
order parallel writes by task path, like langgraph (Python) — one sort
key for live execution and every saver, instead of the task-id order <a
href="https://redirect.github.com/langchain-ai/langgraphjs/issues/2544">#2544</a>
added for DeltaChannel writes</p>
<p>When two or more tasks in one superstep write the same
<code>DeltaChannel</code>, their writes are now applied and replayed in
<code>(task_path, task_id, idx)</code> order — the order Python's
langgraph#8544 established — instead of task-id order. Task ids are
uuid5 hashes that include the checkpoint id, so the old order permuted
parallel writers from run to run and disagreed with Python.</p>
<ul>
<li><code>BaseCheckpointSaver.putWrites</code> takes an optional
<code>taskPath</code> (the serialized task path,
<code>taskPathStr</code>), and <code>getTuple</code>/<code>list</code>
must return <code>pendingWrites</code> in the new
<code>writesSortKey</code> order. <code>writesSortKey</code> and
<code>compareWritesSortKeys</code> are new exports of
<code>@langchain/langgraph-checkpoint</code>; every saver (memory,
SQLite, Postgres, Redis incl. ShallowRedisSaver, MongoDB) stores the
path and sorts in JS — no database collation is involved.</li>
<li>SQLite and Postgres gain a <code>task_path</code> column (migrated
automatically on <code>setup()</code>; read-only pre-column SQLite
databases keep reading, with their rows keeping the old order). Redis
and MongoDB documents gain a <code>task_path</code> field. Rows written
before the upgrade keep their old order.</li>
<li>Exit-durability resumes replay delta writes in live order (port of
langgraph#9114): the resumed superstep's writes interleave with the
loaded ones by task path, later supersteps sort after every real write,
and writes loaded with the checkpoint are no longer stored twice.</li>
<li><code>bulkUpdateState</code> seals the first update superstep:
DeltaChannels touched by the base checkpoint's pending writes (and whose
version moved) snapshot on the update checkpoint, so a finished
sibling's writes and the update's can't replay in the wrong order
(partial port of langgraph#8548).</li>
<li><code>bulkUpdateState</code>/<code>updateState</code> update tasks
carry <code>(__interrupt__, i)</code> task paths, as in langgraph#9128,
so several updates in one superstep writing the same DeltaChannel replay
in the order given.</li>
<li>Send task paths become <code>[PUSH, i, false]</code> and
node-error-handler paths become <code>[*failedTask.path[:3],
&quot;node_error_handler&quot;, false]</code> (the failed task's first
three path elements), matching Python's shapes —
<code>langgraph_path</code> task metadata changes accordingly, and task
ids of functional <code>task()</code> calls under a Send or handler task
change with them. The SDK's Send-task detection accepts both the old and
new shapes.</li>
</ul>
<p>Upgrade notes:</p>
<ul>
<li><code>PostgresSaver</code>: run <code>setup()</code> once after
upgrading, from one process, before the new version serves traffic. The
<code>task_path</code> column comes from the first new migration since
the saver shipped, and until it runs, reads and writes fail with
<code>column &quot;task_path&quot; does not exist</code>. Several
processes running <code>setup()</code> at once on a database that still
needs the migration can fail with a duplicate key error on
<code>checkpoint_migrations</code>.</li>
<li>Third-party checkpoint savers remain source-compatible but must now
persist the <code>taskPath</code> they are given and return
<code>pendingWrites</code> in <code>writesSortKey</code> order; the
conformance suite's new base tests enforce this. A saver that ignores
the new argument replays in task-id order and can rebuild a different
DeltaChannel value than the run produced.</li>
<li>An incomplete superstep containing stored delta writes that must
interleave with resumed or retried task writes is affected by the
upgrade, whether the incompleteness came from an interrupt, a task
error, a crash, or a drain: finish such threads with the old runtime
before upgrading writers. A single interrupted node with no finished
delta-writing siblings is safe to resume. The same applies to resuming
work that includes functional <code>task()</code> calls under a Send or
error-handler task, whose child task ids change.</li>
<li><code>@langchain/langgraph-checkpoint</code> 1.2.0 must be released
before any package requiring its new exports; the consumers' minimum
ranges are raised accordingly.</li>
</ul>
</li>
<li>
<p>Updated dependencies []:</p>
<ul>
<li><code>@​langchain/langgraph-ui</code><a
href="https://github.com/1"><code>@​1</code></a>.5.3</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/langgraph-cli</code><a
href="https://github.com/1"><code>@​1</code></a>.5.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/1631fd895723c4553345ceb5d9fc425b68bda959"><code>1631fd8</code></a>]:
<ul>
<li><code>@​langchain/langgraph-api</code><a
href="https://github.com/1"><code>@​1</code></a>.5.3</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/langgraph-ui</code><a
href="https://github.com/1"><code>@​1</code></a>.5.3</h2>
<p>No changes in this release.</p>
<h2><code>@​langchain/langgraph</code><a
href="https://github.com/1"><code>@​1</code></a>.5.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/3125">#3125</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/e24ec8c3ff3cf4191c4988e84a26c405816976cd"><code>e24ec8c</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- Change the default <code>DeltaChannel</code> snapshot frequency from
1000 to 100 updates, matching Python. Channels now persist full
snapshots more frequently to reduce replay latency at the cost of
increased storage. Set <code>snapshotFrequency: 1000</code> explicitly
to retain the previous cadence.</li>
<li>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/f3dfbb2c3f6c4ff5d30aceca027ab19fca021bde"><code>f3dfbb2</code></a>]:
<ul>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.12.8</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/langgraph-api</code><a
href="https://github.com/1"><code>@​1</code></a>.5.2</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@​langchain/langgraph-ui</code><a
href="https://github.com/1"><code>@​1</code></a>.5.2</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/langgraph-cli</code><a
href="https://github.com/1"><code>@​1</code></a>.5.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2887">#2887</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/8f8eaa8686dab8ba9d35eac9934c590ecbbe11f6"><code>8f8eaa8</code></a>
Thanks <a
href="https://github.com/syachamaneni-lc"><code>@​syachamaneni-lc</code></a>!
- add --agent-id and --agent-environment flags</li>
<li>Updated dependencies []:</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraphjs/blob/main/libs/langgraph-core/CHANGELOG.md">@​langchain/langgraph's
changelog</a>.</em></p>
<blockquote>
<h2>1.5.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/3125">#3125</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/e24ec8c3ff3cf4191c4988e84a26c405816976cd"><code>e24ec8c</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- Change the default <code>DeltaChannel</code> snapshot frequency from
1000 to 100 updates, matching Python. Channels now persist full
snapshots more frequently to reduce replay latency at the cost of
increased storage. Set <code>snapshotFrequency: 1000</code> explicitly
to retain the previous cadence.</li>
<li>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/f3dfbb2c3f6c4ff5d30aceca027ab19fca021bde"><code>f3dfbb2</code></a>]:
<ul>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.12.8</li>
</ul>
</li>
</ul>
<h2>1.5.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/3022">#3022</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/a17f6098a853d1051c810f2c8dffa15e4de8f083"><code>a17f609</code></a>
Thanks <a
href="https://github.com/richardscarrott"><code>@​richardscarrott</code></a>!
- Depend on <code>@langchain/protocol</code> 0.0.20, the version
<code>@langchain/langgraph-sdk</code> uses, so installs resolve a single
copy and the protocol event types stay compatible.</li>
<li>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/6e845706b9a7d12f85e9477722908cc9f7003521"><code>6e84570</code></a>,
<a
href="https://github.com/langchain-ai/langgraphjs/commit/a17f6098a853d1051c810f2c8dffa15e4de8f083"><code>a17f609</code></a>]:
<ul>
<li><code>@​langchain/langgraph-checkpoint</code><a
href="https://github.com/1"><code>@​1</code></a>.2.1</li>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.12.6</li>
</ul>
</li>
</ul>
<h2>1.5.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/3017">#3017</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/61b00495dd13da035d6c92a68f3294f60ca54bd7"><code>61b0049</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- A field that changes from a DeltaChannel to a plain reducer, for
example to <code>MessagesAnnotation</code>, keeps its value on threads
written under the DeltaChannel: reads rebuild it from the stored writes
instead of returning the empty default, and the next run adds to it
instead of replacing it.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/3027">#3027</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/af7e421cd35766296cef05d9cdc50b12a794fbeb"><code>af7e421</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- With <code>durability: &quot;exit&quot;</code>, resuming parallel
interrupts one at a time no longer applies a finished task's output
twice: the save at exit keeps only the writes made during that run,
instead of storing the ones loaded with the checkpoint again under new
indexes.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2986">#2986</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/53cc6274ee6cdb0ad7cfbbc3cc53e6f6b1af4410"><code>53cc627</code></a>
Thanks <a href="https://github.com/byhow"><code>@​byhow</code></a>! -
When one answer in a multi-answer resume fails its interrupt's
<code>responseSchema</code>, the other tasks in the step now finish
before the <code>ZodError</code> is thrown, instead of being aborted. A
task aborted after its side effects kept its answer and ran again on the
next resume. The error is unchanged, and the new
<code>isInvalidResume(error)</code> tells it apart from other errors, as
<code>is_invalid_resume</code> does in langgraph (Python).</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/3038">#3038</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/bc010237d9904bedf411c965d14343222e300d74"><code>bc01023</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- <code>messagesStateReducer</code> and
<code>messagesDeltaReducer</code> store message chunks as their
non-chunk class, as Python's <code>add_messages</code> does. A graph run
with <code>streamMode: &quot;messages&quot;</code> or
<code>streamEvents</code> now checkpoints the same
<code>AIMessage</code> as <code>invoke</code>, without a duplicate
<code>tool_call_chunks</code> copy of every tool call, and an in-place
edit to <code>tool_calls</code> (for example a rejected call in
<code>humanInTheLoopMiddleware</code>) is no longer undone when the
thread is reloaded.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/3028">#3028</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/ec1854a53bc5c6d88f3922a80085ca05678d4532"><code>ec1854a</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- Resuming after some parallel interrupts were answered no longer reruns
the nodes and <code>task()</code> calls that already finished, in a
subgraph or when the resume is sent with the id of the thread's latest
checkpoint. Finished tasks now rerun only on time travel, or on a resume
at a checkpoint the thread has moved past.</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/langchain-ai/langgraphjs/commit/2e1fe5b3fffd87b2f7ba260da3726a2c03c2f72f"><code>2e1fe5b</code></a>,
<a
href="https://github.com/langchain-ai/langgraphjs/commit/0acf88db9fdc6441c8b3a162b7a328b9b91e115e"><code>0acf88d</code></a>,
<a
href="https://github.com/langchain-ai/langgraphjs/commit/9cc54cce575ae00deb0bb5946aa457f44f485427"><code>9cc54cc</code></a>,
<a
href="https://github.com/langchain-ai/langgraphjs/commit/4690a5b9a59f4f96790fdce45160f744272ca054"><code>4690a5b</code></a>]:</p>
<ul>
<li><code>@​langchain/langgraph-sdk</code><a
href="https://github.com/1"><code>@​1</code></a>.12.5</li>
</ul>
</li>
</ul>
<h2>1.5.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2961">#2961</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/1631fd895723c4553345ceb5d9fc425b68bda959"><code>1631fd8</code></a>
Thanks <a href="https://github.com/soarez"><code>@​soarez</code></a>! -
order parallel writes by task path, like langgraph (Python) — one sort
key for live execution and every saver, instead of the task-id order <a
href="https://redirect.github.com/langchain-ai/langgraphjs/issues/2544">#2544</a>
added for DeltaChannel writes</p>
<p>When two or more tasks in one superstep write the same
<code>DeltaChannel</code>, their writes are now applied and replayed in
<code>(task_path, task_id, idx)</code> order — the order Python's
langgraph#8544 established — instead of task-id order. Task ids are
uuid5 hashes that include the checkpoint id, so the old order permuted
parallel writers from run to run and disagreed with Python.</p>
<ul>
<li><code>BaseCheckpointSaver.putWrites</code> takes an optional
<code>taskPath</code> (the serialized task path,
<code>taskPathStr</code>), and <code>getTuple</code>/<code>list</code>
must return <code>pendingWrites</code> in the new
<code>writesSortKey</code> order. <code>writesSortKey</code> and
<code>compareWritesSortKeys</code> are new exports of
<code>@langchain/langgraph-checkpoint</code>; every saver (memory,
SQLite, Postgres, Redis incl. ShallowRedisSaver, MongoDB) stores the
path and sorts in JS — no database collation is involved.</li>
<li>SQLite and Postgres gain a <code>task_path</code> column (migrated
automatically on <code>setup()</code>; read-only pre-column SQLite
databases keep reading, with their rows keeping the old order). Redis
and MongoDB documents gain a <code>task_path</code> field. Rows written
before the upgrade keep their old order.</li>
<li>Exit-durability resumes replay delta writes in live order (port of
langgraph#9114): the resumed superstep's writes interleave with the
loaded ones by task path, later supersteps sort after every real write,
and writes loaded with the checkpoint are no longer stored twice.</li>
<li><code>bulkUpdateState</code> seals the first update superstep:
DeltaChannels touched by the base checkpoint's pending writes (and whose
version moved) snapshot on the update checkpoint, so a finished
sibling's writes and the update's can't replay in the wrong order
(partial port of langgraph#8548).</li>
<li><code>bulkUpdateState</code>/<code>updateState</code> update tasks
carry <code>(__interrupt__, i)</code> task paths, as in langgraph#9128,
so several updates in one superstep writing the same DeltaChannel replay
in the order given.</li>
<li>Send task paths become <code>[PUSH, i, false]</code> and
node-error-handler paths become <code>[*failedTask.path[:3],
&quot;node_error_handler&quot;, false]</code> (the failed task's first
three path elements), matching Python's shapes —
<code>langgraph_path</code> task metadata changes accordingly, and task
ids of functional <code>task()</code> calls under a Send or handler task
change with them. The SDK's Send-task detection accepts both the old and
new shapes.</li>
</ul>
<p>Upgrade notes:</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/b09eb0662358457955131fe0bfec1d7c11e7d7c5"><code>b09eb06</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3117">#3117</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/e24ec8c3ff3cf4191c4988e84a26c405816976cd"><code>e24ec8c</code></a>
fix(langgraph): default delta snapshots to 100 updates (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3125">#3125</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/10e72bc10a7f1f98b4e115e0617ae7bc8162718d"><code>10e72bc</code></a>
chore(deps): bump vite, vitest, and launch-editor security fixes (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3091">#3091</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/0c12d5c429c97dc69d6f72cceb269a4dc920f9a5"><code>0c12d5c</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3059">#3059</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/a17f6098a853d1051c810f2c8dffa15e4de8f083"><code>a17f609</code></a>
feat(sdk): read <code>tool-call-delta</code> and ignore unknown delta
variants (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3022">#3022</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/cc29753d529caa5eed97a65e765ba26c0f88c631"><code>cc29753</code></a>
chore: version packages (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3018">#3018</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/53cc6274ee6cdb0ad7cfbbc3cc53e6f6b1af4410"><code>53cc627</code></a>
fix(langgraph): let other tasks finish when a resume value is invalid
(<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/2986">#2986</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/ec1854a53bc5c6d88f3922a80085ca05678d4532"><code>ec1854a</code></a>
fix(langgraph): stop a subgraph from rerunning its finished tasks on
resume (...</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/af7e421cd35766296cef05d9cdc50b12a794fbeb"><code>af7e421</code></a>
fix(langgraph): save a resumed exit run's pending writes once (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3027">#3027</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraphjs/commit/bc010237d9904bedf411c965d14343222e300d74"><code>bc01023</code></a>
fix(langgraph): store message chunks as messages in the messages
reducers (<a
href="https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core/issues/3">#3</a>...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langgraphjs/commits/@langchain/langgraph@1.5.3/libs/langgraph-core">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.70.1 to 8.72.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/eslint-plugin's
releases</a>.</em></p>
<blockquote>
<h2>v8.72.1</h2>
<h2>8.72.1 (2026-10-26)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] false
positives on unresolved conditional types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13062">#13062</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
bound recursive type traversal instead of catching stack overflows (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13039">#13039</a>)</li>
<li><strong>typescript-estree:</strong> use one Program per tsconfig for
the project service in single-run mode (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12988">#12988</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Ghoulberg 👻</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.72.0</h2>
<h2>8.72.0 (2026-10-19)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> lazy load rules (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13010">#13010</a>)</li>
<li><strong>eslint-plugin:</strong> [await-thenable] add
<code>allowMixedPromiseArrays</code> option (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12992">#12992</a>)</li>
<li><strong>typescript-estree:</strong> error on abstract keyword that
precedes export (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13019">#13019</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>ast-spec:</strong> add <code>JSXFragment</code> to
<code>JSXAttribute['value']</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13051">#13051</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-readonly-parameter-types]
check where allowed type aliases are declared (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13016">#13016</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
properties in object literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12704">#12704</a>)</li>
<li><strong>eslint-plugin:</strong> use isTypeLiteralType instead of
type.isLiteral() to include bigint literals on TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13053">#13053</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] check
tuples (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13058">#13058</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] ignore
extra this type argument on TS &lt; 5.1 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13055">#13055</a>)</li>
<li><strong>type-utils:</strong> discriminateAnyType crashes when
calling getAwaitedType on awaited type in TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13054">#13054</a>)</li>
<li><strong>typescript-estree:</strong> include leading and trailing
comments &amp; whitespace in Program range (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12991">#12991</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Junichiro Kasuya <a
href="https://github.com/jksy"><code>@​jksy</code></a></li>
<li>Mitch Fultz <a
href="https://github.com/fitchmultz"><code>@​fitchmultz</code></a></li>
<li>Shaan GS <a
href="https://github.com/ShaanGS"><code>@​ShaanGS</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.71.2</h2>
<!-- 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">@​typescript-eslint/eslint-plugin's
changelog</a>.</em></p>
<blockquote>
<h2>8.72.1 (2026-10-26)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
bound recursive type traversal instead of catching stack overflows (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13039">#13039</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] false
positives on unresolved conditional types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13062">#13062</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Ghoulberg 👻</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.72.0 (2026-10-19)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [await-thenable] add
<code>allowMixedPromiseArrays</code> option (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12992">#12992</a>)</li>
<li><strong>eslint-plugin:</strong> lazy load rules (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13010">#13010</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>type-utils:</strong> discriminateAnyType crashes when
calling getAwaitedType on awaited type in TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13054">#13054</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] ignore
extra this type argument on TS &lt; 5.1 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13055">#13055</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] check
tuples (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13058">#13058</a>)</li>
<li><strong>eslint-plugin:</strong> use isTypeLiteralType instead of
type.isLiteral() to include bigint literals on TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13053">#13053</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
properties in object literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12704">#12704</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-readonly-parameter-types]
check where allowed type aliases are declared (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13016">#13016</a>)</li>
<li><strong>typescript-estree:</strong> include leading and trailing
comments &amp; whitespace in Program range (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12991">#12991</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Junichiro Kasuya <a
href="https://github.com/jksy"><code>@​jksy</code></a></li>
<li>Mitch Fultz <a
href="https://github.com/fitchmultz"><code>@​fitchmultz</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.71.2 (2026-10-12)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-optional-chain] safe fix
when the chain ends with a strict null comparison (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12805">#12805</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
parenthesize constraint in optional tuple elements (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12995">#12995</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unused-vars] ignore type-only
refs for after-used (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12977">#12977</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/4ad6b7553781fa3c93fc26642d7219d2e6624504"><code>4ad6b75</code></a>
chore(release): publish 8.72.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/77576945345c1537635e617ea0d4b34b003b15ff"><code>7757694</code></a>
chore: cleanup typecheck &amp; build and fix race condition (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/13071">#13071</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/1166ba20e9e484167ebfe216fdb38e605e3f7598"><code>1166ba2</code></a>
chore(type-utils): detect methods in isTypeReadonly without reading the
inter...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/2f0343a694bee1ba9815e4a0f946295452deb082"><code>2f0343a</code></a>
fix(eslint-plugin): [no-unnecessary-type-assertion] bound recursive type
trav...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/c49d287f046c794133b12fa94207e5ee3b846ddd"><code>c49d287</code></a>
chore: [ci] test on oldest supported TS version (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/13024">#13024</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/594fe286ec518c6899a9b89141cf3220bcfb9f72"><code>594fe28</code></a>
fix(eslint-plugin): [no-unnecessary-condition] false positives on
unresolved ...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a4c2603d3e4fd43b33bc25b82457716e9f7cef71"><code>a4c2603</code></a>
chore(release): publish 8.72.0</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/c6eb98c17cbeee540d3c48d1406098430b2f0660"><code>c6eb98c</code></a>
feat(eslint-plugin): [await-thenable] add
<code>allowMixedPromiseArrays</code> option (#...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/b382b74579e5514c247ed54d0dfc428f3a173f3a"><code>b382b74</code></a>
fix(type-utils): discriminateAnyType crashes when calling getAwaitedType
on a...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/79edd896790b031457fc0ca6f81c732b29921c9d"><code>79edd89</code></a>
fix(eslint-plugin): [require-array-sort-compare] ignore extra this type
argum...</li>
<li>Additional commits viewable in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.72.1/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.70.1 to 8.72.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/parser's
releases</a>.</em></p>
<blockquote>
<h2>v8.72.1</h2>
<h2>8.72.1 (2026-10-26)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] false
positives on unresolved conditional types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13062">#13062</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
bound recursive type traversal instead of catching stack overflows (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13039">#13039</a>)</li>
<li><strong>typescript-estree:</strong> use one Program per tsconfig for
the project service in single-run mode (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12988">#12988</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Ghoulberg 👻</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.72.0</h2>
<h2>8.72.0 (2026-10-19)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> lazy load rules (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13010">#13010</a>)</li>
<li><strong>eslint-plugin:</strong> [await-thenable] add
<code>allowMixedPromiseArrays</code> option (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12992">#12992</a>)</li>
<li><strong>typescript-estree:</strong> error on abstract keyword that
precedes export (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13019">#13019</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>ast-spec:</strong> add <code>JSXFragment</code> to
<code>JSXAttribute['value']</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13051">#13051</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-readonly-parameter-types]
check where allowed type aliases are declared (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13016">#13016</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
properties in object literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12704">#12704</a>)</li>
<li><strong>eslint-plugin:</strong> use isTypeLiteralType instead of
type.isLiteral() to include bigint literals on TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13053">#13053</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] check
tuples (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13058">#13058</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] ignore
extra this type argument on TS &lt; 5.1 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13055">#13055</a>)</li>
<li><strong>type-utils:</strong> discriminateAnyType crashes when
calling getAwaitedType on awaited type in TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13054">#13054</a>)</li>
<li><strong>typescript-estree:</strong> include leading and trailing
comments &amp; whitespace in Program range (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12991">#12991</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Junichiro Kasuya <a
href="https://github.com/jksy"><code>@​jksy</code></a></li>
<li>Mitch Fultz <a
href="https://github.com/fitchmultz"><code>@​fitchmultz</code></a></li>
<li>Shaan GS <a
href="https://github.com/ShaanGS"><code>@​ShaanGS</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.71.2</h2>
<!-- 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">@​typescript-eslint/parser's
changelog</a>.</em></p>
<blockquote>
<h2>8.72.1 (2026-10-26)</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.72.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.72.0 (2026-10-19)</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.72.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.71.2 (2026-10-12)</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.71.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.71.1 (2026-10-05)</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.71.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.71.0 (2026-09-28)</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.71.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>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/4ad6b7553781fa3c93fc26642d7219d2e6624504"><code>4ad6b75</code></a>
chore(release): publish 8.72.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/77576945345c1537635e617ea0d4b34b003b15ff"><code>7757694</code></a>
chore: cleanup typecheck &amp; build and fix race condition (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/13071">#13071</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a4c2603d3e4fd43b33bc25b82457716e9f7cef71"><code>a4c2603</code></a>
chore(release): publish 8.72.0</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/7488d681f6e0e96c2aaa1eee9c3d1b9b1ef077cb"><code>7488d68</code></a>
chore(release): publish 8.71.2</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/d57b8da272375b6a45270639894a793933208a91"><code>d57b8da</code></a>
chore(release): publish 8.71.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/8695664c5ee3fbb8fd76907fc45e148579cc3162"><code>8695664</code></a>
chore(release): publish 8.71.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.72.1/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `dotenv` from 18.0.4 to 18.0.9
<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/v18.0.8...v18.0.9">18.0.9</a>
(2026-10-14)</h2>
<h3>Changed</h3>
<ul>
<li>Permit DOTENV_PATH being empty string with the CLI (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1076">#1076</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v18.0.7...v18.0.8">18.0.8</a>
(2026-10-14)</h2>
<h3>Changed</h3>
<ul>
<li>Fast parser should strip appended extra quotes (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1073">#1073</a>)</li>
<li>Type detail improvement (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1074">#1074</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v18.0.6...v18.0.7">18.0.7</a>
(2026-10-09)</h2>
<h3>Changed</h3>
<ul>
<li>Treat undefined settings like <code>quiet: true</code> as null
rather than falsey (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1070">#1070</a>)</li>
<li>CLI should honor <code>DOTENV_QUIET</code> setting in
<code>.env</code> file (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1071">#1071</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v18.0.5...v18.0.6">18.0.6</a>
(2026-10-06)</h2>
<h3>Changed</h3>
<ul>
<li>Use smarter <code>parseBoolean</code> not Boolean() to fix override
(<a
href="https://redirect.github.com/motdotla/dotenv/pull/1069">#1069</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v18.0.4...v18.0.5">18.0.5</a>
(2026-09-30)</h2>
<h3>Changed</h3>
<ul>
<li>Fix missing typescript module declaration (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1068">#1068</a>)</li>
<li>Improve performance for large .env files (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1066">#1066</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/motdotla/dotenv/commit/8d1ab850aa628781929f08bfd0d71ba1d1bf0a23"><code>8d1ab85</code></a>
18.0.9</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/59a40ce5876a661d3964cc0269208267174ea39b"><code>59a40ce</code></a>
changelog</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/88228bd02b9ce98a79442ddcd619e4c31199474c"><code>88228bd</code></a>
Merge pull request <a
href="https://redirect.github.com/motdotla/dotenv/issues/1076">#1076</a>
from Enesuygurs/fix/cli-empty-dotenv-path</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/bc6ef941200c2015ffcc17d40c4f8f76ffe24026"><code>bc6ef94</code></a>
fix(cli): load the default .env when DOTENV_PATH is empty</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/6d6e6c58074adc12131ec6ab6eac92fb92484dbc"><code>6d6e6c5</code></a>
Merge pull request <a
href="https://redirect.github.com/motdotla/dotenv/issues/1075">#1075</a>
from kory-kaai/test/fast-parser-branch-coverage</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/10218f1b0fa1f83f0714dc3c8133d0daa54752ff"><code>10218f1</code></a>
test(parse-fast): cover the size-dependent and export-prefix
branches</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/e2fe95e8e952e881787d8207b654c80326f821d8"><code>e2fe95e</code></a>
18.0.8</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/7aa712ab2914c0cf128ea8ccdaf5e918a867db59"><code>7aa712a</code></a>
type detail improvement</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/53405b0b140debc002e41c5abb671d5da5f3e8f9"><code>53405b0</code></a>
Merge pull request <a
href="https://redirect.github.com/motdotla/dotenv/issues/1074">#1074</a>
from himanshu748/codex/fix-dotenv-populate-undefined...</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/0b4843a90d6d70b110d1f1a309f209c3d8d7cb6e"><code>0b4843a</code></a>
changelog</li>
<li>Additional commits viewable in <a
href="https://github.com/motdotla/dotenv/compare/v18.0.4...v18.0.9">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.11.0 to 10.13.0
<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.13.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/48d5cc47e43478670ccf95a74f7a73e8408152b5"><code>48d5cc4</code></a>
feat: report references inside IIFEs in
<code>no-use-before-define</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21238">#21238</a>)
(Irfan fandi)</li>
<li><a
href="https://github.com/eslint/eslint/commit/a39d9fb1fdb5db546f6407580cac531ba3370450"><code>a39d9fb</code></a>
feat: add <code>globalThis</code> to <code>no-obj-calls</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21382">#21382</a>)
(synthex-byte)</li>
<li><a
href="https://github.com/eslint/eslint/commit/66ac58383e076940c11a5d7213ad079028d1140e"><code>66ac583</code></a>
feat: check <code>yield*</code> expressions in
<code>no-unsafe-optional-chaining</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21399">#21399</a>)
(xbinaryx)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/87bd2cef7b2309739c29598fb2be335f4bed71a3"><code>87bd2ce</code></a>
fix: avoid redeclared <code>let</code> when <code>no-var</code> and
<code>one-var</code> fix together (<a
href="https://redirect.github.com/eslint/eslint/issues/21389">#21389</a>)
(Daniel Pinto)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/4783a6806fb73ea4843ac46c6ae3d8285f56c288"><code>4783a68</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/43f0ac90e7858a834a0e68309e6397fb4cfa72dd"><code>43f0ac9</code></a>
docs: clarify parameter description in <code>getText</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21287">#21287</a>)
(lumir)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/b59d889c7c18d8c73144e6ba46e9e7f3e7da0196"><code>b59d889</code></a>
chore: update github/codeql-action action to v4.38.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/21412">#21412</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/95e51a0518b15015304eca9844b3f64c1fc167ca"><code>95e51a0</code></a>
ci: re-enable <code>@typescript-eslint/typescript-eslint</code> in
ecosystem tests (<a
href="https://redirect.github.com/eslint/eslint/issues/21410">#21410</a>)
(Francesco Trotta)</li>
<li><a
href="https://github.com/eslint/eslint/commit/317fcb365f19e912a60d171a9d5a8d1f16fe4660"><code>317fcb3</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21409">#21409</a>)
(ESLint Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/b92decf8ead7cc4082a18a4a0ab28f3c7c589887"><code>b92decf</code></a>
perf: skip no-redeclare scan for single-declaration variables (<a
href="https://redirect.github.com/eslint/eslint/issues/21365">#21365</a>)
(James Ross)</li>
<li><a
href="https://github.com/eslint/eslint/commit/47b7063b3f3f7bc7a5bcb87c082577e4e8b96e24"><code>47b7063</code></a>
chore: update dependency <code>@​eslint/json</code> to ^2.1.1 (<a
href="https://redirect.github.com/eslint/eslint/issues/21398">#21398</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/e5aea414cc00555ecd43f6e608bf80e0f9a6d4d6"><code>e5aea41</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21390">#21390</a>)
(ESLint Bot)</li>
</ul>
<h2>v10.12.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/4618052eed6bb3ef420cf0db0490bbda2fd835a5"><code>4618052</code></a>
feat: handle astral letters in <code>new-cap</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21357">#21357</a>)
(sary)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4ec5168d6d6888e3f7b163eb82963d40e45a75fa"><code>4ec5168</code></a>
feat: allow <code>SourceCode#getText()</code> to accept tokens and
comments (<a
href="https://redirect.github.com/eslint/eslint/issues/21340">#21340</a>)
(electrohyun)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/bc51eee6e3e816622b2dd1bc08acb9cfb2d3a6a7"><code>bc51eee</code></a>
fix: <code>prefer-arrow-callback</code> false positive in conditional
test (<a
href="https://redirect.github.com/eslint/eslint/issues/21373">#21373</a>)
(Daniel Pinto)</li>
<li><a
href="https://github.com/eslint/eslint/commit/bbff86c41521cc435e20754e26007f657a843038"><code>bbff86c</code></a>
fix: skip lines with multiple comments in
<code>max-lines-per-function</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21332">#21332</a>)
(xbinaryx)</li>
<li><a
href="https://github.com/eslint/eslint/commit/efc4d6bdc4c4d1dfba8bdab6a8f44f27a1b1d00a"><code>efc4d6b</code></a>
fix: astral letters in <code>consistent-return</code>,
<code>no-eval</code>, <code>no-invalid-this</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21360">#21360</a>)
(lumir)</li>
<li><a
href="https://github.com/eslint/eslint/commit/93de066d4125f8df013d40305e8009f4634a5cba"><code>93de066</code></a>
fix: prefer-exponentiation-operator autofix for async function base (<a
href="https://redirect.github.com/eslint/eslint/issues/21322">#21322</a>)
(Vladimir Babin)</li>
<li><a
href="https://github.com/eslint/eslint/commit/02e34ffba6adbed72365177c7caee71d950834e5"><code>02e34ff</code></a>
fix: add missing space after <code>else</code> in <code>curly</code>
autofix (<a
href="https://redirect.github.com/eslint/eslint/issues/21355">#21355</a>)
(Pixel)</li>
<li><a
href="https://github.com/eslint/eslint/commit/b14b8bc213ccfc19d3edcfa9ce6af62622661160"><code>b14b8bc</code></a>
fix: correct <code>id-length</code> message for long private names (<a
href="https://redirect.github.com/eslint/eslint/issues/21348">#21348</a>)
(Pixel)</li>
<li><a
href="https://github.com/eslint/eslint/commit/69aac01d898837a934c9e735e0b7881cc1df09e8"><code>69aac01</code></a>
fix: support <code>TSFunctionType</code> in
<code>getFunctionHeadLoc</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21335">#21335</a>)
(xbinaryx)</li>
<li><a
href="https://github.com/eslint/eslint/commit/686630eb34ec1cddf399a4e6eaeebb080116a532"><code>686630e</code></a>
fix: <code>no-loss-of-precision</code> false positive with
<code>0.e5</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21337">#21337</a>)
(sethamus)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/67eb586e2eda290813b329d4f40e605ad696bc4f"><code>67eb586</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/5370d7ee35a6523873f798e87f3ccc73779d5d21"><code>5370d7e</code></a>
docs: clarify <code>one-var</code> <code>separateRequires</code> matches
any <code>require()</code> call (<a
href="https://redirect.github.com/eslint/eslint/issues/21192">#21192</a>)
(sethamus)</li>
<li><a
href="https://github.com/eslint/eslint/commit/8816c1ddfcfa31318ad17cddcfb68acffa30940f"><code>8816c1d</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/3d2e7cedb7409d8a2c5f2c2fafb14fa22790e40e"><code>3d2e7ce</code></a>
docs: fix typo in no-unused-expressions documentation (<a
href="https://redirect.github.com/eslint/eslint/issues/21346">#21346</a>)
(bytedoe)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/152067f0f81f08b6cce9cf8eeaddb8558731b4f6"><code>152067f</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21362">#21362</a>)
(ESLint Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/b56d58e0b1f3c919a6b7661254af760cdd909d6e"><code>b56d58e</code></a>
chore: update github/codeql-action action to v4.38.2 (<a
href="https://redirect.github.com/eslint/eslint/issues/21376">#21376</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/bfaea12ddc30b458fe4cfbb6c306a62b5299c177"><code>bfaea12</code></a>
perf: cache normalized config globals per languageOptions (<a
href="https://redirect.github.com/eslint/eslint/issues/21364">#21364</a>)
(James Ross)</li>
<li><a
href="https://github.com/eslint/eslint/commit/322209ef164e8a00a25ec7ca166969ddc8...

_Description has been truncated_

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-01 16:50:52 -08:00
dependabot[bot] 937d4027bf chore(deps): bump the minor-and-patch group with 6 updates (#9522)
Bumps the minor-and-patch group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [docker/login-action](https://github.com/docker/login-action) |
`4.6.0` | `4.7.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact)
| `7.0.1` | `7.0.2` |
|
[actions/download-artifact](https://github.com/actions/download-artifact)
| `8.0.1` | `8.0.2` |
|
[pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish)
| `1.14.2` | `1.14.3` |
| [actions/setup-python](https://github.com/actions/setup-python) |
`7.0.0` | `7.1.0` |
|
[actions/create-github-app-token](https://github.com/actions/create-github-app-token)
| `3.2.0` | `3.3.0` |

Updates `docker/login-action` from 4.6.0 to 4.7.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.7.0</h2>
<ul>
<li>Preserve explicit ECR settings in <code>registry-auth</code> by <a
href="https://github.com/crazy-max"><code>@​crazy-max</code></a> in <a
href="https://redirect.github.com/docker/login-action/pull/1089">docker/login-action#1089</a></li>
<li>Use registry hosts for scoped credentials by <a
href="https://github.com/crazy-max"><code>@​crazy-max</code></a> in <a
href="https://redirect.github.com/docker/login-action/pull/1088">docker/login-action#1088</a></li>
<li>Raise Docker Hub OIDC max expiry to 6 hours by <a
href="https://github.com/crazy-max"><code>@​crazy-max</code></a> in <a
href="https://redirect.github.com/docker/login-action/pull/1068">docker/login-action#1068</a></li>
<li>Log Docker Hub OIDC token exchange progress by <a
href="https://github.com/crazy-max"><code>@​crazy-max</code></a> in <a
href="https://redirect.github.com/docker/login-action/pull/1061">docker/login-action#1061</a></li>
<li>Bump <code>@​aws-sdk/client-ecr</code> and
<code>@​aws-sdk/client-ecr-public</code> to 3.1154.0 in <a
href="https://redirect.github.com/docker/login-action/pull/1093">docker/login-action#1093</a></li>
<li>Bump <code>@​docker/actions-toolkit</code> from 0.94.0 to 0.107.0 in
<a
href="https://redirect.github.com/docker/login-action/pull/1100">docker/login-action#1100</a></li>
<li>Bump <code>@​humanfs/node</code> from 0.16.7 to 0.16.8 in <a
href="https://redirect.github.com/docker/login-action/pull/1077">docker/login-action#1077</a></li>
<li>Bump csv-parse from 7.0.1 to 7.0.3 in <a
href="https://redirect.github.com/docker/login-action/pull/1078">docker/login-action#1078</a></li>
<li>Bump handlebars from 4.7.9 to 4.7.10 in <a
href="https://redirect.github.com/docker/login-action/pull/1091">docker/login-action#1091</a></li>
<li>Bump js-yaml from 5.2.2 to 5.5.0 in <a
href="https://redirect.github.com/docker/login-action/pull/1066">docker/login-action#1066</a></li>
<li>Bump postcss from 8.5.22 to 8.5.26 in <a
href="https://redirect.github.com/docker/login-action/pull/1071">docker/login-action#1071</a></li>
<li>Bump postcss-selector-parser from 7.1.1 to 7.1.6 in <a
href="https://redirect.github.com/docker/login-action/pull/1092">docker/login-action#1092</a></li>
<li>Bump source-map-js from 1.2.1 to 1.2.2 in <a
href="https://redirect.github.com/docker/login-action/pull/1090">docker/login-action#1090</a></li>
<li>Bump undici from 6.27.0 to 6.29.1 in <a
href="https://redirect.github.com/docker/login-action/pull/1085">docker/login-action#1085</a></li>
<li>Bump uuid from 14.0.1 to 14.0.3 in <a
href="https://redirect.github.com/docker/login-action/pull/1096">docker/login-action#1096</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/docker/login-action/compare/v4.6.0...v4.7.0">https://github.com/docker/login-action/compare/v4.6.0...v4.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/docker/login-action/commit/81c6f93d4bc991e510b342a00b419b3bb4efc5c6"><code>81c6f93</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1100">#1100</a>
from docker/dependabot/npm_and_yarn/docker/actions-t...</li>
<li><a
href="https://github.com/docker/login-action/commit/1b16c8d1815e1d951d87a3522ff48a76d073735a"><code>1b16c8d</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1096">#1096</a>
from docker/dependabot/npm_and_yarn/uuid-14.0.3</li>
<li><a
href="https://github.com/docker/login-action/commit/ac86b5e9e9d2db43b12b2d3afb9140a427d69d65"><code>ac86b5e</code></a>
[dependabot skip] chore: update generated content</li>
<li><a
href="https://github.com/docker/login-action/commit/1476d845f08dd8309a938959a280f2e23c0cba14"><code>1476d84</code></a>
build(deps): bump <code>@​docker/actions-toolkit</code> from 0.94.0 to
0.107.0</li>
<li><a
href="https://github.com/docker/login-action/commit/503cee68d85e94275776fcc6f5315f19207f7567"><code>503cee6</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1095">#1095</a>
from docker/dependabot/github_actions/actions/create...</li>
<li><a
href="https://github.com/docker/login-action/commit/f0935357f5c539aeb7ff6b886ba5434ab76e9de3"><code>f093535</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1099">#1099</a>
from docker/dependabot/github_actions/codecov/codeco...</li>
<li><a
href="https://github.com/docker/login-action/commit/d5ae7f188776587b12b77b45e29ea21332462da9"><code>d5ae7f1</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1098">#1098</a>
from docker/dependabot/github_actions/docker/bake-ac...</li>
<li><a
href="https://github.com/docker/login-action/commit/6f9750797f6e7186f18c069a7e97abc1dcf1006b"><code>6f97507</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1097">#1097</a>
from docker/dependabot/github_actions/docker/bake-ac...</li>
<li><a
href="https://github.com/docker/login-action/commit/006d61fa598847d65f6670d1569545fa2af9cf02"><code>006d61f</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1094">#1094</a>
from docker/dependabot/github_actions/codeql-actions...</li>
<li><a
href="https://github.com/docker/login-action/commit/93f5f522eea5eacabf35cc89b9d19ec23e742471"><code>93f5f52</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/login-action/issues/1093">#1093</a>
from docker/dependabot/npm_and_yarn/aws-sdk-dependen...</li>
<li>Additional commits viewable in <a
href="https://github.com/docker/login-action/compare/dbcb813823bdd20940b903addbd779551569679f...81c6f93d4bc991e510b342a00b419b3bb4efc5c6">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Improves artifact download retries when the service returns HTTP 429
(rate limiting), including honoring valid Retry-After headers.</li>
<li>Updates <code>@​actions/artifact</code> to v6.3.1.</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/tunc-d"><code>@​tunc-d</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/upload-artifact/pull/821">actions/upload-artifact#821</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/upload-artifact/compare/v7.0.1...v7.0.2">https://github.com/actions/upload-artifact/compare/v7.0.1...v7.0.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/upload-artifact/commit/cf430e030ddbb5b0abf93d22962f4752f3646cd9"><code>cf430e0</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-artifact/issues/822">#822</a>
from actions/tunc-d-prepare-upload-v7-0-2</li>
<li><a
href="https://github.com/actions/upload-artifact/commit/c2acdedd35a8824a1552c7137f8c18288c3ea469"><code>c2acded</code></a>
chore: prepare v7.0.2 release</li>
<li><a
href="https://github.com/actions/upload-artifact/commit/4687c1bbaa0a58abb517d90b4bbade6754c80c1a"><code>4687c1b</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/upload-artifact/issues/821">#821</a>
from actions/tunc-d-upload-artifact-package-bump</li>
<li><a
href="https://github.com/actions/upload-artifact/commit/8cbe1bebdb858105d8fdd88f81404d4de4595719"><code>8cbe1be</code></a>
Bump <code>@​actions/artifact</code> to 6.3.1</li>
<li>See full diff in <a
href="https://github.com/actions/upload-artifact/compare/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a...cf430e030ddbb5b0abf93d22962f4752f3646cd9">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/download-artifact` from 8.0.1 to 8.0.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/download-artifact/releases">actions/download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v8.0.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Update the readme by <a
href="https://github.com/danwkennedy"><code>@​danwkennedy</code></a> in
<a
href="https://redirect.github.com/actions/download-artifact/pull/473">actions/download-artifact#473</a></li>
<li>Improves artifact download retries when the service returns HTTP 429
(rate limiting), including honoring valid Retry-After headers.</li>
<li>Update <code>@​actions/artifact</code> to 6.3.1 by <a
href="https://github.com/tunc-d"><code>@​tunc-d</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/492">actions/download-artifact#492</a></li>
<li>Prepare download-artifact v8.0.2 by <a
href="https://github.com/tunc-d"><code>@​tunc-d</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/493">actions/download-artifact#493</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/tunc-d"><code>@​tunc-d</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/download-artifact/pull/492">actions/download-artifact#492</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/download-artifact/compare/v8.0.1...v8.0.2">https://github.com/actions/download-artifact/compare/v8.0.1...v8.0.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/download-artifact/commit/9000827ccba6bdab643e8b6fd33ac0654aef8333"><code>9000827</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/493">#493</a>
from actions/tunc-d-prepare-download-v8-0-2</li>
<li><a
href="https://github.com/actions/download-artifact/commit/e455989ae00f5bdd4054f05d3e972c6bf8a62dc5"><code>e455989</code></a>
Prepare download-artifact v8.0.2</li>
<li><a
href="https://github.com/actions/download-artifact/commit/a00b90c4a2f01d73a8c89552caabca6ea3b43137"><code>a00b90c</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/492">#492</a>
from actions/tunc-d-download-artifact-package-bump</li>
<li><a
href="https://github.com/actions/download-artifact/commit/29bb7ce6af5345aeab5bb4774ce7249ee3ccc27f"><code>29bb7ce</code></a>
Update <code>@​actions/artifact</code> to 6.3.1</li>
<li><a
href="https://github.com/actions/download-artifact/commit/484a0b528fb4d7bd804637ccb632e47a0e638317"><code>484a0b5</code></a>
Update the readme (<a
href="https://redirect.github.com/actions/download-artifact/issues/473">#473</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/download-artifact/compare/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c...9000827ccba6bdab643e8b6fd33ac0654aef8333">compare
view</a></li>
</ul>
</details>
<br />

Updates `pypa/gh-action-pypi-publish` from 1.14.2 to 1.14.3
<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.3</h2>
<!-- raw HTML omitted -->
<blockquote>
<p>[!important]
🚨 This release includes fixes for <a
href="https://github.com/pypa/gh-action-pypi-publish/security/advisories/GHSA-5xjx-3w67-w9x9">GHSA-5xjx-3w67-w9x9</a>
patched up by <a
href="https://github.com/sethmlarson"><code>@​sethmlarson</code></a><a
href="https://github.com/sponsors/sethmlarson">💰</a> in <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/427">#427</a>
and in other places across the ecosystem.</p>
<p>Additional ecosystem and related mitigations beyond you might want to
watch out for:</p>
<ul>
<li><a
href="https://discuss.python.org/t/information-for-python-users-regarding-emudhra-emsign-ca-certificate-security-incident/109576">https://discuss.python.org/t/information-for-python-users-regarding-emudhra-emsign-ca-certificate-security-incident/109576</a></li>
<li><a
href="https://emudhra.we-have-a-problem.com">https://emudhra.we-have-a-problem.com</a></li>
</ul>
</blockquote>
<h2>🛠️ Security Tightening</h2>
<p><a href="https://github.com/webknjaz"><code>@​webknjaz</code></a><a
href="https://github.com/sponsors/webknjaz">💰</a> made sure the
dependency tree within the action runtime uses hash-checking from now on
via <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/pull/420">pypa/gh-action-pypi-publish#420</a>.</p>
<p><strong>🪞 Full Diff</strong>: <a
href="https://github.com/pypa/gh-action-pypi-publish/compare/v1.14.2...v1.14.3">https://github.com/pypa/gh-action-pypi-publish/compare/v1.14.2...v1.14.3</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>💬 Discuss</strong> <a
href="https://bsky.app/profile/webknjaz.me/post/3mydcjweyak2f">on
Bluesky 🦋</a>, <a
href="https://mastodon.social/@webknjaz/117474743338290807">on Mastodon
🐘</a> and <a
href="https://github.com/pypa/gh-action-pypi-publish/discussions/429">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/7245906fb61fa33a13554f9d5fb856fe61758086"><code>7245906</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/gh-action-pypi-publish/issues/427">#427</a>
from sethmlarson/certifi-sigstore</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/331eccb0e13a5c1124ed9f414a079ef2bef1095f"><code>331eccb</code></a>
Upgrade certifi and sigstore</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/f68fee44f9faa979857cda34894b750df5f571ca"><code>f68fee4</code></a>
💰 Sync the FUNDING config</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/c0d6f8e4af2ad8247a7da213b199ea78dac8395e"><code>c0d6f8e</code></a>
Merge branch 'integrity/runtime/deptree' into unstable/v1</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/909009990f9cf7e7c6a64e602638cf9e91af47f8"><code>9090099</code></a>
🧪 Make smoke test deps hash-pinned in CI</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/34e40c24acd7b4a9150956d646c8c7ba667a14f3"><code>34e40c2</code></a>
📌 Bump pip-tools managed transitive deptrees</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/e800ed547ac29cbc9324311809e2f36fe82148f9"><code>e800ed5</code></a>
👷 Enable hash-checking in the runtime deptree 🔗</li>
<li><a
href="https://github.com/pypa/gh-action-pypi-publish/commit/dd8c5f4f1f65ddf01533969008446145facc7b02"><code>dd8c5f4</code></a>
🔧 Make pip-tools config available in container</li>
<li>See full diff in <a
href="https://github.com/pypa/gh-action-pypi-publish/compare/dc37677b2e1c63e2034f94d8a5b11f265b73ba33...7245906fb61fa33a13554f9d5fb856fe61758086">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/setup-python` from 7.0.0 to 7.1.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/setup-python/releases">actions/setup-python's
releases</a>.</em></p>
<blockquote>
<h2>v7.1.0</h2>
<h2>What's Changed</h2>
<h3>Enhancements</h3>
<ul>
<li>Add <code>mirror</code> and <code>mirror-token</code> inputs for
custom Python distribution sources by <a
href="https://github.com/luhenry"><code>@​luhenry</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1302">actions/setup-python#1302</a></li>
<li>Improve error message clarity by <a
href="https://github.com/v-lmvysakh"><code>@​v-lmvysakh</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1351">actions/setup-python#1351</a></li>
</ul>
<h3>Dependency Upgrade</h3>
<ul>
<li>Upgrade browserslist from 4.28.2 to 4.28.9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1353">actions/setup-python#1353</a></li>
<li>Upgrade <code>@​typescript-eslint/parser</code> from 8.62.0 to
8.70.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1343">actions/setup-python#1343</a></li>
<li>Upgrade eslint-plugin-jest from 29.15.2 to 29.16.6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1342">actions/setup-python#1342</a></li>
<li>Upgrade <code>@​vercel/ncc</code> from 0.44.0 to 0.45.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1357">actions/setup-python#1357</a></li>
<li>Upgrade <code>@​types/semver</code> from 7.7.1 to 7.8.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1359">actions/setup-python#1359</a></li>
<li>Upgrade undici from 6.28.0 to 6.29.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1364">actions/setup-python#1364</a></li>
<li>Upgrade ts-jest from 29.4.11 to 29.4.14 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1361">actions/setup-python#1361</a></li>
<li>Upgrade brace-expansion from 5.0.9 to 5.0.12 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1365">actions/setup-python#1365</a></li>
<li>Upgrade prettier from 3.8.4 to 3.9.9 and package.json from 7.0.0 to
7.1.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1363">actions/setup-python#1363</a></li>
<li>Upgrade <code>@​typescript-eslint/eslint-plugin</code> from 8.62.0
to 8.71.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1369">actions/setup-python#1369</a></li>
<li>Upgrade actions/checkout from 7.0.0 to 7.0.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-python/pull/1345">actions/setup-python#1345</a></li>
<li>Upgrade cache to 6.3.0 by <a
href="https://github.com/v-HarithaVattikuti"><code>@​v-HarithaVattikuti</code></a>
in <a
href="https://redirect.github.com/actions/setup-python/pull/1370">actions/setup-python#1370</a></li>
<li>Resolve Basic Validation audit failures by <a
href="https://github.com/v-HarithaVattikuti"><code>@​v-HarithaVattikuti</code></a>
in <a
href="https://redirect.github.com/actions/setup-python/pull/1347">actions/setup-python#1347</a></li>
<li>Resolve npm audit failures by <a
href="https://github.com/v-HarithaVattikuti"><code>@​v-HarithaVattikuti</code></a>
in <a
href="https://redirect.github.com/actions/setup-python/pull/1350">actions/setup-python#1350</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/luhenry"><code>@​luhenry</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/setup-python/pull/1302">actions/setup-python#1302</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/setup-python/compare/v7.0.0...v7.1.0">https://github.com/actions/setup-python/compare/v7.0.0...v7.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/setup-python/commit/6d4fd22b20465ccc0f7354476588e8df7c9ca0e0"><code>6d4fd22</code></a>
Cache upgrade to 6.3.0 (<a
href="https://redirect.github.com/actions/setup-python/issues/1370">#1370</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/ec52d0af729a7c50ced4261d17a07916f43f5b8e"><code>ec52d0a</code></a>
Bump <code>@​typescript-eslint/eslint-plugin</code> from 8.62.0 to
8.71.1 (<a
href="https://redirect.github.com/actions/setup-python/issues/1369">#1369</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/ff73b2463e2f3baa703f830da214b8dfeeaeccf8"><code>ff73b24</code></a>
Bump prettier from 3.8.4 to 3.9.9 and package.json from 7.0.0 to 7.1.0
(<a
href="https://redirect.github.com/actions/setup-python/issues/1363">#1363</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/0e40890053525508c059c61932a6b8614f9e95db"><code>0e40890</code></a>
Bump brace-expansion from 5.0.9 to 5.0.12 (<a
href="https://redirect.github.com/actions/setup-python/issues/1365">#1365</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/3892123fbe74688ccc83ec7c465dc688359e5199"><code>3892123</code></a>
Bump ts-jest from 29.4.11 to 29.4.14 (<a
href="https://redirect.github.com/actions/setup-python/issues/1361">#1361</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/660965736c4aecc62797ea07d351a3c259f14660"><code>6609657</code></a>
Bump undici from 6.28.0 to 6.29.0 (<a
href="https://redirect.github.com/actions/setup-python/issues/1364">#1364</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/e1be392b26fb1917a7b2c455817e4b4dad6544ce"><code>e1be392</code></a>
Bump <code>@​types/semver</code> from 7.7.1 to 7.8.0 (<a
href="https://redirect.github.com/actions/setup-python/issues/1359">#1359</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/06da6279cdbca41027908073e81a4544dc79c06b"><code>06da627</code></a>
Bump <code>@​vercel/ncc</code> from 0.44.0 to 0.45.0 (<a
href="https://redirect.github.com/actions/setup-python/issues/1357">#1357</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/af9bcc7548695e66d75709de6f29e337624b8de8"><code>af9bcc7</code></a>
Bump eslint-plugin-jest from 29.15.2 to 29.16.6 (<a
href="https://redirect.github.com/actions/setup-python/issues/1342">#1342</a>)</li>
<li><a
href="https://github.com/actions/setup-python/commit/7aeabeba5501120d873c50398519dbfd0657031c"><code>7aeabeb</code></a>
Bump <code>@​typescript-eslint/parser</code> from 8.62.0 to 8.70.0 (<a
href="https://redirect.github.com/actions/setup-python/issues/1343">#1343</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/setup-python/compare/5fda3b95a4ea91299a34e894583c3862153e4b97...6d4fd22b20465ccc0f7354476588e8df7c9ca0e0">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/create-github-app-token` from 3.2.0 to 3.3.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/create-github-app-token/releases">actions/create-github-app-token's
releases</a>.</em></p>
<blockquote>
<h2>v3.3.0</h2>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v3.2.0...v3.3.0">3.3.0</a>
(2026-10-12)</h2>
<h3>Features</h3>
<ul>
<li>update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/399">#399</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/a47236f79f79fcbc3532af49fe3a5ded89529b6e">a47236f</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump nested undici from 6.24.0 to 6.29.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/403">#403</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/12461a3a794e67c032f1eff5bb95ece9427eb4fb">12461a3</a>)</li>
<li><strong>deps:</strong> bump the production-dependencies group across
1 directory with 3 updates (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/395">#395</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/a69c19ddfdf8e1ab093900e0064524ce9309c807">a69c19d</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md">actions/create-github-app-token's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v3.2.0...v3.3.0">3.3.0</a>
(2026-10-12)</h2>
<h3>Features</h3>
<ul>
<li>update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/399">#399</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/a47236f79f79fcbc3532af49fe3a5ded89529b6e">a47236f</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump nested undici from 6.24.0 to 6.29.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/403">#403</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/12461a3a794e67c032f1eff5bb95ece9427eb4fb">12461a3</a>)</li>
<li><strong>deps:</strong> bump the production-dependencies group across
1 directory with 3 updates (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/395">#395</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/a69c19ddfdf8e1ab093900e0064524ce9309c807">a69c19d</a>)</li>
</ul>
<h2><a
href="https://github.com/actions/create-github-app-token/compare/v3.1.1...v3.2.0">3.2.0</a>
(2026-05-12)</h2>
<h3>Features</h3>
<ul>
<li>add support for enterprise-level GitHub Apps (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/263">#263</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/952a2a7073df6bfa5f49bc469ec895b6ec1acea4">952a2a7</a>)</li>
<li>support full repository names in <code>repositories</code> input (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/372">#372</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/85eb8dd41472213aed25d1a126460e0069138ab6">85eb8dd</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> bump <code>@​actions/core</code> from 3.0.0
to 3.0.1 in the production-dependencies group (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/364">#364</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/43e5c345bfd4d4f3ecea019ad0042001a09dd857">43e5c34</a>)</li>
<li>validate private-key input (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/376">#376</a>)
(<a
href="https://github.com/actions/create-github-app-token/commit/f24bbd89643991c0de27ae823c01791b2c6bafdd">f24bbd8</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/create-github-app-token/commit/64ab729a6294000f5aa4a4bc0b73d1f32bc45510"><code>64ab729</code></a>
chore(main): release 3.3.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/402">#402</a>)</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/f3a828e4b91180d3a46ac1f4b43fa8384b0dea07"><code>f3a828e</code></a>
ci: create signed dist commits in release workflow (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/404">#404</a>)</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/655017ffa7c6b0c6e4210729fa6bce35cacd8ca0"><code>655017f</code></a>
docs: clarify when owner grants access to all repositories (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/378">#378</a>)</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/12461a3a794e67c032f1eff5bb95ece9427eb4fb"><code>12461a3</code></a>
fix(deps): bump nested undici from 6.24.0 to 6.29.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/403">#403</a>)</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/a47236f79f79fcbc3532af49fe3a5ded89529b6e"><code>a47236f</code></a>
feat: update permission inputs (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/399">#399</a>)</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/a69c19ddfdf8e1ab093900e0064524ce9309c807"><code>a69c19d</code></a>
fix(deps): bump the production-dependencies group across 1 directory
with 3 u...</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/d5cb7ae41d64331820d88810a4878e0942ec992a"><code>d5cb7ae</code></a>
build(deps): bump actions/checkout from 6 to 7 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/384">#384</a>)</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/575da866bd0a42d7f234a8645831d55573252b09"><code>575da86</code></a>
build(deps): bump stefanzweifel/git-auto-commit-action from 7.1.0 to
7.2.0 in...</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/e85f677b44da0d1f08115ce52cdfd8fd2c69874a"><code>e85f677</code></a>
test: omit stack traces from snapshots (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/401">#401</a>)</li>
<li><a
href="https://github.com/actions/create-github-app-token/commit/9419396df4d5672241cfb593dadc19758e54a204"><code>9419396</code></a>
build(deps-dev): bump c8 from 11.0.0 to 12.0.0 (<a
href="https://redirect.github.com/actions/create-github-app-token/issues/400">#400</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/create-github-app-token/compare/bcd2ba49218906704ab6c1aa796996da409d3eb1...64ab729a6294000f5aa4a4bc0b73d1f32bc45510">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-11-01 16:50:44 -08:00
dependabot[bot] 50303e0ba1 chore(deps): bump the minor-and-patch group in /libs/cli/js-monorepo-example with 8 updates (#9526)
Bumps the minor-and-patch group in /libs/cli/js-monorepo-example with 8
updates:

| Package | From | To |
| --- | --- | --- |
| [turbo](https://github.com/vercel/turborepo) | `2.11.5` | `2.11.10` |
| [eslint](https://github.com/eslint/eslint) | `10.11.0` | `10.13.0` |
|
[eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier)
| `5.5.6` | `5.5.7` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.70.1` | `8.72.1` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.70.1` | `8.72.1` |
| [prettier](https://github.com/prettier/prettier) | `3.9.9` | `3.9.10`
|
| [@langchain/core](https://github.com/langchain-ai/langchainjs) |
`1.2.13` | `1.3.1` |
|
[@langchain/langgraph](https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core)
| `1.4.18` | `1.5.3` |

Updates `turbo` from 2.11.5 to 2.11.10
<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.11.10</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>Changelog</h3>
<ul>
<li>fix: Allow non-recursive Turbo commands in root tasks by <a
href="https://github.com/sahyl"><code>@​sahyl</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/14814">vercel/turborepo#14814</a></li>
<li>chore: Release Turborepo 2.11.9 by <a
href="https://github.com/github-actions"><code>@​github-actions</code></a>[bot]
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14824">vercel/turborepo#14824</a></li>
<li>chore(deps): Update dependency <code>@​vercel/geistdocs</code> to v2
by <a
href="https://github.com/vercel-gh-bot-4"><code>@​vercel-gh-bot-4</code></a>[bot]
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14417">vercel/turborepo#14417</a></li>
<li>chore: Update with-gatsby example by <a
href="https://github.com/vercel-gh-bot-3"><code>@​vercel-gh-bot-3</code></a>[bot]
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14796">vercel/turborepo#14796</a></li>
<li>fix: Avoid pnpm batch-shim shutdown hangs on Windows by <a
href="https://github.com/sahyl"><code>@​sahyl</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/14819">vercel/turborepo#14819</a></li>
<li>chore(deps): Update dependency <code>@​vercel/geistdocs</code> to
v2.11.0 by <a
href="https://github.com/vercel-gh-bot-3"><code>@​vercel-gh-bot-3</code></a>[bot]
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14832">vercel/turborepo#14832</a></li>
<li>fix: Exclude tool-managed directories from local dependency inputs
by <a
href="https://github.com/PedroRosalba"><code>@​PedroRosalba</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14696">vercel/turborepo#14696</a></li>
<li>fix: Match git's text=auto binary detection in file hashing by <a
href="https://github.com/PedroRosalba"><code>@​PedroRosalba</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14697">vercel/turborepo#14697</a></li>
<li>fix: Drop <code>@​react-router/dev</code> from Remix match list by
<a
href="https://github.com/Pcmhacker-piro"><code>@​Pcmhacker-piro</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14800">vercel/turborepo#14800</a></li>
<li>fix: Match package configuration files and directory inputs in
task-level affected detection by <a
href="https://github.com/PedroRosalba"><code>@​PedroRosalba</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14804">vercel/turborepo#14804</a></li>
<li>fix: Parse pnpm 8 single-project lockfiles by <a
href="https://github.com/PedroRosalba"><code>@​PedroRosalba</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14806">vercel/turborepo#14806</a></li>
<li>fix: Keep task env vars in the hash when globalEnv excludes inferred
vars by <a
href="https://github.com/PedroRosalba"><code>@​PedroRosalba</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14805">vercel/turborepo#14805</a></li>
<li>docs: Generate the sitemap from Geistdocs sources by <a
href="https://github.com/molebox"><code>@​molebox</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/14865">vercel/turborepo#14865</a></li>
<li>fix: Restore cached symlinks when outputs are already on disk by <a
href="https://github.com/PedroRosalba"><code>@​PedroRosalba</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14807">vercel/turborepo#14807</a></li>
<li>chore: Remove CODEOWNERS file by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a>
with <a href="https://github.com/Copilot"><code>@​Copilot</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14810">vercel/turborepo#14810</a></li>
<li>feat: Enable WebMCP for Turborepo docs by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14828">vercel/turborepo#14828</a></li>
<li>fix: Exit quietly when <code>turbo ls</code> output is piped to a
closed reader by <a
href="https://github.com/drakeo338"><code>@​drakeo338</code></a> in <a
href="https://redirect.github.com/vercel/turborepo/pull/14868">vercel/turborepo#14868</a></li>
<li>fix: Distrust compromised eMudhra intermediate certificates by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14876">vercel/turborepo#14876</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/vercel/turborepo/compare/v2.11.9...v2.11.10">https://github.com/vercel/turborepo/compare/v2.11.9...v2.11.10</a></p>
<h2>Turborepo v2.11.9</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>Changelog</h3>
<ul>
<li>chore: Release Turborepo 2.11.8 by <a
href="https://github.com/github-actions"><code>@​github-actions</code></a>[bot]
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14789">vercel/turborepo#14789</a></li>
<li>perf: Prefetch Cargo metadata during lazy graph construction by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14497">vercel/turborepo#14497</a></li>
<li>perf: Compute Berry lockfile closures once across workspaces by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14499">vercel/turborepo#14499</a></li>
<li>perf: Check cache hit outputs in place instead of on the blocking
pool by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14524">vercel/turborepo#14524</a></li>
<li>perf: Speed up Berry lockfile loading by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14500">vercel/turborepo#14500</a></li>
<li>perf: Parse bun.lock without building a JSON syntax tree by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14502">vercel/turborepo#14502</a></li>
<li>perf: Skip the dry run cache batch window when there is no remote
cache by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14505">vercel/turborepo#14505</a></li>
<li>perf: Emit lockfile closures pre-sorted and cheapen closure
interning by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14525">vercel/turborepo#14525</a></li>
<li>perf: Reuse configuration derived from turbo.json between the shim
and the CLI by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14521">vercel/turborepo#14521</a></li>
<li>feat: Report locked tools-only setup plans by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14483">vercel/turborepo#14483</a></li>
<li>perf: Compute Bun lockfile closures once across workspaces by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14501">vercel/turborepo#14501</a></li>
<li>perf: Reuse open parent directories when restoring cache archives by
<a href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14506">vercel/turborepo#14506</a></li>
<li>perf: Skip dropping the run right before turbo run exits by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14507">vercel/turborepo#14507</a></li>
<li>perf: Match literal input exclusions without compiling globs by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14510">vercel/turborepo#14510</a></li>
<li>perf: Copy pruned package files in parallel by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14512">vercel/turborepo#14512</a></li>
<li>feat: Report proposed first-lock and refresh setup plans by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14571">vercel/turborepo#14571</a></li>
<li>perf: Compress large cache archives with multithreaded zstd by <a
href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a> in
<a
href="https://redirect.github.com/vercel/turborepo/pull/14698">vercel/turborepo#14698</a></li>
<li>perf: Read tracked file stats from directory listings on Windows by
<a href="https://github.com/anthonyshew"><code>@​anthonyshew</code></a>
in <a
href="https://redirect.github.com/vercel/turborepo/pull/14633">vercel/turborepo#14633</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/e521641a142a24abe4554a77fdd6551e47e96fb3"><code>e521641</code></a>
publish 2.11.10 to registry</li>
<li><a
href="https://github.com/vercel/turborepo/commit/aaf5a3afdc7f1f3142ec13b6665ef099b3fb3528"><code>aaf5a3a</code></a>
fix: Distrust compromised eMudhra intermediate certificates (<a
href="https://redirect.github.com/vercel/turborepo/issues/14876">#14876</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/98165036e02c9940e9b22b9ffbed98a721aa24f1"><code>9816503</code></a>
fix: Exit quietly when <code>turbo ls</code> output is piped to a closed
reader (<a
href="https://redirect.github.com/vercel/turborepo/issues/14868">#14868</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/af866e5114c328a25ead2690464607214245b3f8"><code>af866e5</code></a>
feat: Enable WebMCP for Turborepo docs (<a
href="https://redirect.github.com/vercel/turborepo/issues/14828">#14828</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/b42cc68b412d7be1e103f9a014a29b2f56e990e3"><code>b42cc68</code></a>
chore: Remove CODEOWNERS file (<a
href="https://redirect.github.com/vercel/turborepo/issues/14810">#14810</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/921155f01d4abe946ac0265f3b37a763108ec1b3"><code>921155f</code></a>
fix: Restore cached symlinks when outputs are already on disk (<a
href="https://redirect.github.com/vercel/turborepo/issues/14807">#14807</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/9330903fb870682d5c3239d2acac597fd4fb29e8"><code>9330903</code></a>
docs: Generate the sitemap from Geistdocs sources (<a
href="https://redirect.github.com/vercel/turborepo/issues/14865">#14865</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/60480382670ce6ed882bb33ae59cc7be17f80279"><code>6048038</code></a>
fix: Keep task env vars in the hash when globalEnv excludes inferred
vars (<a
href="https://redirect.github.com/vercel/turborepo/issues/1">#1</a>...</li>
<li><a
href="https://github.com/vercel/turborepo/commit/57ecce3b0ebed41b43c288ed9082043f29f451ac"><code>57ecce3</code></a>
fix: Parse pnpm 8 single-project lockfiles (<a
href="https://redirect.github.com/vercel/turborepo/issues/14806">#14806</a>)</li>
<li><a
href="https://github.com/vercel/turborepo/commit/d926d6a3723956eff9d07228809097b307dc978a"><code>d926d6a</code></a>
fix: Match package configuration files and directory inputs in
task-level aff...</li>
<li>Additional commits viewable in <a
href="https://github.com/vercel/turborepo/compare/v2.11.5...v2.11.10">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.11.0 to 10.13.0
<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.13.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/48d5cc47e43478670ccf95a74f7a73e8408152b5"><code>48d5cc4</code></a>
feat: report references inside IIFEs in
<code>no-use-before-define</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21238">#21238</a>)
(Irfan fandi)</li>
<li><a
href="https://github.com/eslint/eslint/commit/a39d9fb1fdb5db546f6407580cac531ba3370450"><code>a39d9fb</code></a>
feat: add <code>globalThis</code> to <code>no-obj-calls</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21382">#21382</a>)
(synthex-byte)</li>
<li><a
href="https://github.com/eslint/eslint/commit/66ac58383e076940c11a5d7213ad079028d1140e"><code>66ac583</code></a>
feat: check <code>yield*</code> expressions in
<code>no-unsafe-optional-chaining</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21399">#21399</a>)
(xbinaryx)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/87bd2cef7b2309739c29598fb2be335f4bed71a3"><code>87bd2ce</code></a>
fix: avoid redeclared <code>let</code> when <code>no-var</code> and
<code>one-var</code> fix together (<a
href="https://redirect.github.com/eslint/eslint/issues/21389">#21389</a>)
(Daniel Pinto)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/4783a6806fb73ea4843ac46c6ae3d8285f56c288"><code>4783a68</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/43f0ac90e7858a834a0e68309e6397fb4cfa72dd"><code>43f0ac9</code></a>
docs: clarify parameter description in <code>getText</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21287">#21287</a>)
(lumir)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/b59d889c7c18d8c73144e6ba46e9e7f3e7da0196"><code>b59d889</code></a>
chore: update github/codeql-action action to v4.38.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/21412">#21412</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/95e51a0518b15015304eca9844b3f64c1fc167ca"><code>95e51a0</code></a>
ci: re-enable <code>@typescript-eslint/typescript-eslint</code> in
ecosystem tests (<a
href="https://redirect.github.com/eslint/eslint/issues/21410">#21410</a>)
(Francesco Trotta)</li>
<li><a
href="https://github.com/eslint/eslint/commit/317fcb365f19e912a60d171a9d5a8d1f16fe4660"><code>317fcb3</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21409">#21409</a>)
(ESLint Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/b92decf8ead7cc4082a18a4a0ab28f3c7c589887"><code>b92decf</code></a>
perf: skip no-redeclare scan for single-declaration variables (<a
href="https://redirect.github.com/eslint/eslint/issues/21365">#21365</a>)
(James Ross)</li>
<li><a
href="https://github.com/eslint/eslint/commit/47b7063b3f3f7bc7a5bcb87c082577e4e8b96e24"><code>47b7063</code></a>
chore: update dependency <code>@​eslint/json</code> to ^2.1.1 (<a
href="https://redirect.github.com/eslint/eslint/issues/21398">#21398</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/e5aea414cc00555ecd43f6e608bf80e0f9a6d4d6"><code>e5aea41</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21390">#21390</a>)
(ESLint Bot)</li>
</ul>
<h2>v10.12.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/4618052eed6bb3ef420cf0db0490bbda2fd835a5"><code>4618052</code></a>
feat: handle astral letters in <code>new-cap</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21357">#21357</a>)
(sary)</li>
<li><a
href="https://github.com/eslint/eslint/commit/4ec5168d6d6888e3f7b163eb82963d40e45a75fa"><code>4ec5168</code></a>
feat: allow <code>SourceCode#getText()</code> to accept tokens and
comments (<a
href="https://redirect.github.com/eslint/eslint/issues/21340">#21340</a>)
(electrohyun)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/bc51eee6e3e816622b2dd1bc08acb9cfb2d3a6a7"><code>bc51eee</code></a>
fix: <code>prefer-arrow-callback</code> false positive in conditional
test (<a
href="https://redirect.github.com/eslint/eslint/issues/21373">#21373</a>)
(Daniel Pinto)</li>
<li><a
href="https://github.com/eslint/eslint/commit/bbff86c41521cc435e20754e26007f657a843038"><code>bbff86c</code></a>
fix: skip lines with multiple comments in
<code>max-lines-per-function</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21332">#21332</a>)
(xbinaryx)</li>
<li><a
href="https://github.com/eslint/eslint/commit/efc4d6bdc4c4d1dfba8bdab6a8f44f27a1b1d00a"><code>efc4d6b</code></a>
fix: astral letters in <code>consistent-return</code>,
<code>no-eval</code>, <code>no-invalid-this</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21360">#21360</a>)
(lumir)</li>
<li><a
href="https://github.com/eslint/eslint/commit/93de066d4125f8df013d40305e8009f4634a5cba"><code>93de066</code></a>
fix: prefer-exponentiation-operator autofix for async function base (<a
href="https://redirect.github.com/eslint/eslint/issues/21322">#21322</a>)
(Vladimir Babin)</li>
<li><a
href="https://github.com/eslint/eslint/commit/02e34ffba6adbed72365177c7caee71d950834e5"><code>02e34ff</code></a>
fix: add missing space after <code>else</code> in <code>curly</code>
autofix (<a
href="https://redirect.github.com/eslint/eslint/issues/21355">#21355</a>)
(Pixel)</li>
<li><a
href="https://github.com/eslint/eslint/commit/b14b8bc213ccfc19d3edcfa9ce6af62622661160"><code>b14b8bc</code></a>
fix: correct <code>id-length</code> message for long private names (<a
href="https://redirect.github.com/eslint/eslint/issues/21348">#21348</a>)
(Pixel)</li>
<li><a
href="https://github.com/eslint/eslint/commit/69aac01d898837a934c9e735e0b7881cc1df09e8"><code>69aac01</code></a>
fix: support <code>TSFunctionType</code> in
<code>getFunctionHeadLoc</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21335">#21335</a>)
(xbinaryx)</li>
<li><a
href="https://github.com/eslint/eslint/commit/686630eb34ec1cddf399a4e6eaeebb080116a532"><code>686630e</code></a>
fix: <code>no-loss-of-precision</code> false positive with
<code>0.e5</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21337">#21337</a>)
(sethamus)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/67eb586e2eda290813b329d4f40e605ad696bc4f"><code>67eb586</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/5370d7ee35a6523873f798e87f3ccc73779d5d21"><code>5370d7e</code></a>
docs: clarify <code>one-var</code> <code>separateRequires</code> matches
any <code>require()</code> call (<a
href="https://redirect.github.com/eslint/eslint/issues/21192">#21192</a>)
(sethamus)</li>
<li><a
href="https://github.com/eslint/eslint/commit/8816c1ddfcfa31318ad17cddcfb68acffa30940f"><code>8816c1d</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/3d2e7cedb7409d8a2c5f2c2fafb14fa22790e40e"><code>3d2e7ce</code></a>
docs: fix typo in no-unused-expressions documentation (<a
href="https://redirect.github.com/eslint/eslint/issues/21346">#21346</a>)
(bytedoe)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/152067f0f81f08b6cce9cf8eeaddb8558731b4f6"><code>152067f</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21362">#21362</a>)
(ESLint Bot)</li>
<li><a
href="https://github.com/eslint/eslint/commit/b56d58e0b1f3c919a6b7661254af760cdd909d6e"><code>b56d58e</code></a>
chore: update github/codeql-action action to v4.38.2 (<a
href="https://redirect.github.com/eslint/eslint/issues/21376">#21376</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/bfaea12ddc30b458fe4cfbb6c306a62b5299c177"><code>bfaea12</code></a>
perf: cache normalized config globals per languageOptions (<a
href="https://redirect.github.com/eslint/eslint/issues/21364">#21364</a>)
(James Ross)</li>
<li><a
href="https://github.com/eslint/eslint/commit/322209ef164e8a00a25ec7ca166969ddc853355c"><code>322209e</code></a>
ci: avoid Nx cache in ecosystem tests and disable failing test (<a
href="https://redirect.github.com/eslint/eslint/issues/21369">#21369</a>)
(Francesco Trotta)</li>
<li><a
href="https://github.com/eslint/eslint/commit/d166567901e09940c1de4ad0e95d572eeab5c927"><code>d166567</code></a>
chore: update dependency prettier to v3.9.9 (<a
href="https://redirect.github.com/eslint/eslint/issues/21371">#21371</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/29585ceba7ad35368d1b763bd1e80d899f28842c"><code>29585ce</code></a>
chore: update dependency eslint-plugin-expect-type to ^0.7.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/21359">#21359</a>)
(renovate[bot])</li>
<li><a
href="https://github.com/eslint/eslint/commit/39d79ba9650542ac66d585d1899304abd630c07d"><code>39d79ba</code></a>
chore: update github/codeql-action action to v4.38.1 (<a
href="https://redirect.github.com/eslint/eslint/issues/21354">#21354</a>)
(renovate[bot])</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/4637f9827a1ba137498eaee70957850621082815"><code>4637f98</code></a>
10.13.0</li>
<li><a
href="https://github.com/eslint/eslint/commit/96df05ceb7000813859acf16a690f8a8bbbea904"><code>96df05c</code></a>
Build: changelog update for 10.13.0</li>
<li><a
href="https://github.com/eslint/eslint/commit/4783a6806fb73ea4843ac46c6ae3d8285f56c288"><code>4783a68</code></a>
docs: Update README</li>
<li><a
href="https://github.com/eslint/eslint/commit/b59d889c7c18d8c73144e6ba46e9e7f3e7da0196"><code>b59d889</code></a>
chore: update github/codeql-action action to v4.38.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/21412">#21412</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/95e51a0518b15015304eca9844b3f64c1fc167ca"><code>95e51a0</code></a>
ci: re-enable <code>@typescript-eslint/typescript-eslint</code> in
ecosystem tests (<a
href="https://redirect.github.com/eslint/eslint/issues/21410">#21410</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/317fcb365f19e912a60d171a9d5a8d1f16fe4660"><code>317fcb3</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21409">#21409</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/48d5cc47e43478670ccf95a74f7a73e8408152b5"><code>48d5cc4</code></a>
feat: report references inside IIFEs in
<code>no-use-before-define</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21238">#21238</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/43f0ac90e7858a834a0e68309e6397fb4cfa72dd"><code>43f0ac9</code></a>
docs: clarify parameter description in <code>getText</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21287">#21287</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/a39d9fb1fdb5db546f6407580cac531ba3370450"><code>a39d9fb</code></a>
feat: add <code>globalThis</code> to <code>no-obj-calls</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21382">#21382</a>)</li>
<li><a
href="https://github.com/eslint/eslint/commit/b92decf8ead7cc4082a18a4a0ab28f3c7c589887"><code>b92decf</code></a>
perf: skip no-redeclare scan for single-declaration variables (<a
href="https://redirect.github.com/eslint/eslint/issues/21365">#21365</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.11.0...v10.13.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint-plugin-prettier` from 5.5.6 to 5.5.7
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/eslint-plugin-prettier/releases">eslint-plugin-prettier's
releases</a>.</em></p>
<blockquote>
<h2>v5.5.7</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/796">#796</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7e53ecd882fe6f0f74150cb0a539776b64182b3e"><code>7e53ecd</code></a>
Thanks <a
href="https://github.com/OskarEichler"><code>@​OskarEichler</code></a>!
- Fix fallback diagnostic locations for all supported line endings.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/805">#805</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/1b7c43322ce0eb3ee7495d91481613d39d97af52"><code>1b7c433</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- Report a clear error when the sync worker fails to load, so a missing
<code>prettier</code> dependency now fails fast with <code>Cannot find
package 'prettier'</code> instead of hanging ESLint indefinitely.</p>
</li>
</ul>
<h3>New Contributors</h3>
<ul>
<li><a
href="https://github.com/truongbo17"><code>@​truongbo17</code></a> made
their first contribution in <a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/798">prettier/eslint-plugin-prettier#798</a></li>
<li><a
href="https://github.com/OskarEichler"><code>@​OskarEichler</code></a>
made their first contribution in <a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/796">prettier/eslint-plugin-prettier#796</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.6...v5.5.7">https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.6...v5.5.7</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md">eslint-plugin-prettier's
changelog</a>.</em></p>
<blockquote>
<h2>5.5.7</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/796">#796</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7e53ecd882fe6f0f74150cb0a539776b64182b3e"><code>7e53ecd</code></a>
Thanks <a
href="https://github.com/OskarEichler"><code>@​OskarEichler</code></a>!
- Fix fallback diagnostic locations for all supported line endings.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/805">#805</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/1b7c43322ce0eb3ee7495d91481613d39d97af52"><code>1b7c433</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- Report a clear error when the sync worker fails to load, so a missing
<code>prettier</code> dependency now fails fast with <code>Cannot find
package 'prettier'</code> instead of hanging ESLint indefinitely.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/e6dd27a4e4015748b2e87c646be2c1a530789343"><code>e6dd27a</code></a>
chore: keep packageManager when cleaning package.json for release (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/808">#808</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7544956b642d4e653c235d178432e4d60a312b6b"><code>7544956</code></a>
chore: release eslint-plugin-prettier (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/799">#799</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/e3e52386bd8ab1604d4d41ecd961de5e6749eed8"><code>e3e5238</code></a>
ci: enable npm publish provenance for changesets v3 (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/807">#807</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7c923c41069c939d6c6c8b7cfcad8f6a266f5276"><code>7c923c4</code></a>
build(deps): Bump http-cache-semantics from 4.2.0 to 4.3.0 (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/806">#806</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/1b7c43322ce0eb3ee7495d91481613d39d97af52"><code>1b7c433</code></a>
fix: bump synckit to surface worker failures instead of hanging (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/805">#805</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/a6b3feb3ce5d0bf785ddf4a83d24cf6a5c4bb902"><code>a6b3feb</code></a>
docs: explain line endings and <code>endOfLine</code> behavior (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/804">#804</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/2f6ba1dca350a40857884103a7bf0e74b64901f7"><code>2f6ba1d</code></a>
ci: exercise the getLocFromIndex ponyfill on ESLint 9 (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/803">#803</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/cf1d2c5cffb7520d28fe680603cc4906ada0816f"><code>cf1d2c5</code></a>
chore(deps): update (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/802">#802</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/9f14a554f9737b1b985e2b26501d3bb2defac920"><code>9f14a55</code></a>
chore(deps): update all dependencies (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/788">#788</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7e53ecd882fe6f0f74150cb0a539776b64182b3e"><code>7e53ecd</code></a>
fix: map fallback source locations to actual line starts (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/796">#796</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.6...v5.5.7">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.70.1 to 8.72.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/eslint-plugin's
releases</a>.</em></p>
<blockquote>
<h2>v8.72.1</h2>
<h2>8.72.1 (2026-10-26)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] false
positives on unresolved conditional types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13062">#13062</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
bound recursive type traversal instead of catching stack overflows (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13039">#13039</a>)</li>
<li><strong>typescript-estree:</strong> use one Program per tsconfig for
the project service in single-run mode (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12988">#12988</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Ghoulberg 👻</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.72.0</h2>
<h2>8.72.0 (2026-10-19)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> lazy load rules (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13010">#13010</a>)</li>
<li><strong>eslint-plugin:</strong> [await-thenable] add
<code>allowMixedPromiseArrays</code> option (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12992">#12992</a>)</li>
<li><strong>typescript-estree:</strong> error on abstract keyword that
precedes export (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13019">#13019</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>ast-spec:</strong> add <code>JSXFragment</code> to
<code>JSXAttribute['value']</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13051">#13051</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-readonly-parameter-types]
check where allowed type aliases are declared (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13016">#13016</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
properties in object literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12704">#12704</a>)</li>
<li><strong>eslint-plugin:</strong> use isTypeLiteralType instead of
type.isLiteral() to include bigint literals on TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13053">#13053</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] check
tuples (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13058">#13058</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] ignore
extra this type argument on TS &lt; 5.1 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13055">#13055</a>)</li>
<li><strong>type-utils:</strong> discriminateAnyType crashes when
calling getAwaitedType on awaited type in TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13054">#13054</a>)</li>
<li><strong>typescript-estree:</strong> include leading and trailing
comments &amp; whitespace in Program range (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12991">#12991</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Junichiro Kasuya <a
href="https://github.com/jksy"><code>@​jksy</code></a></li>
<li>Mitch Fultz <a
href="https://github.com/fitchmultz"><code>@​fitchmultz</code></a></li>
<li>Shaan GS <a
href="https://github.com/ShaanGS"><code>@​ShaanGS</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.71.2</h2>
<!-- 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">@​typescript-eslint/eslint-plugin's
changelog</a>.</em></p>
<blockquote>
<h2>8.72.1 (2026-10-26)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
bound recursive type traversal instead of catching stack overflows (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13039">#13039</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] false
positives on unresolved conditional types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13062">#13062</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Ghoulberg 👻</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.72.0 (2026-10-19)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [await-thenable] add
<code>allowMixedPromiseArrays</code> option (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12992">#12992</a>)</li>
<li><strong>eslint-plugin:</strong> lazy load rules (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13010">#13010</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>type-utils:</strong> discriminateAnyType crashes when
calling getAwaitedType on awaited type in TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13054">#13054</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] ignore
extra this type argument on TS &lt; 5.1 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13055">#13055</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] check
tuples (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13058">#13058</a>)</li>
<li><strong>eslint-plugin:</strong> use isTypeLiteralType instead of
type.isLiteral() to include bigint literals on TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13053">#13053</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
properties in object literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12704">#12704</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-readonly-parameter-types]
check where allowed type aliases are declared (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13016">#13016</a>)</li>
<li><strong>typescript-estree:</strong> include leading and trailing
comments &amp; whitespace in Program range (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12991">#12991</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Junichiro Kasuya <a
href="https://github.com/jksy"><code>@​jksy</code></a></li>
<li>Mitch Fultz <a
href="https://github.com/fitchmultz"><code>@​fitchmultz</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.71.2 (2026-10-12)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-optional-chain] safe fix
when the chain ends with a strict null comparison (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12805">#12805</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
parenthesize constraint in optional tuple elements (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12995">#12995</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unused-vars] ignore type-only
refs for after-used (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12977">#12977</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/4ad6b7553781fa3c93fc26642d7219d2e6624504"><code>4ad6b75</code></a>
chore(release): publish 8.72.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/77576945345c1537635e617ea0d4b34b003b15ff"><code>7757694</code></a>
chore: cleanup typecheck &amp; build and fix race condition (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/13071">#13071</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/1166ba20e9e484167ebfe216fdb38e605e3f7598"><code>1166ba2</code></a>
chore(type-utils): detect methods in isTypeReadonly without reading the
inter...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/2f0343a694bee1ba9815e4a0f946295452deb082"><code>2f0343a</code></a>
fix(eslint-plugin): [no-unnecessary-type-assertion] bound recursive type
trav...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/c49d287f046c794133b12fa94207e5ee3b846ddd"><code>c49d287</code></a>
chore: [ci] test on oldest supported TS version (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/13024">#13024</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/594fe286ec518c6899a9b89141cf3220bcfb9f72"><code>594fe28</code></a>
fix(eslint-plugin): [no-unnecessary-condition] false positives on
unresolved ...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a4c2603d3e4fd43b33bc25b82457716e9f7cef71"><code>a4c2603</code></a>
chore(release): publish 8.72.0</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/c6eb98c17cbeee540d3c48d1406098430b2f0660"><code>c6eb98c</code></a>
feat(eslint-plugin): [await-thenable] add
<code>allowMixedPromiseArrays</code> option (#...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/b382b74579e5514c247ed54d0dfc428f3a173f3a"><code>b382b74</code></a>
fix(type-utils): discriminateAnyType crashes when calling getAwaitedType
on a...</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/79edd896790b031457fc0ca6f81c732b29921c9d"><code>79edd89</code></a>
fix(eslint-plugin): [require-array-sort-compare] ignore extra this type
argum...</li>
<li>Additional commits viewable in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.72.1/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.70.1 to 8.72.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/parser's
releases</a>.</em></p>
<blockquote>
<h2>v8.72.1</h2>
<h2>8.72.1 (2026-10-26)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] false
positives on unresolved conditional types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13062">#13062</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
bound recursive type traversal instead of catching stack overflows (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13039">#13039</a>)</li>
<li><strong>typescript-estree:</strong> use one Program per tsconfig for
the project service in single-run mode (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12988">#12988</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Ghoulberg 👻</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.72.0</h2>
<h2>8.72.0 (2026-10-19)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> lazy load rules (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13010">#13010</a>)</li>
<li><strong>eslint-plugin:</strong> [await-thenable] add
<code>allowMixedPromiseArrays</code> option (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12992">#12992</a>)</li>
<li><strong>typescript-estree:</strong> error on abstract keyword that
precedes export (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13019">#13019</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>ast-spec:</strong> add <code>JSXFragment</code> to
<code>JSXAttribute['value']</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13051">#13051</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-readonly-parameter-types]
check where allowed type aliases are declared (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13016">#13016</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
properties in object literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12704">#12704</a>)</li>
<li><strong>eslint-plugin:</strong> use isTypeLiteralType instead of
type.isLiteral() to include bigint literals on TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13053">#13053</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] check
tuples (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13058">#13058</a>)</li>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] ignore
extra this type argument on TS &lt; 5.1 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13055">#13055</a>)</li>
<li><strong>type-utils:</strong> discriminateAnyType crashes when
calling getAwaitedType on awaited type in TS &lt; 5.0 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/13054">#13054</a>)</li>
<li><strong>typescript-estree:</strong> include leading and trailing
comments &amp; whitespace in Program range (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12991">#12991</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Junichiro Kasuya <a
href="https://github.com/jksy"><code>@​jksy</code></a></li>
<li>Mitch Fultz <a
href="https://github.com/fitchmultz"><code>@​fitchmultz</code></a></li>
<li>Shaan GS <a
href="https://github.com/ShaanGS"><code>@​ShaanGS</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.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.71.2</h2>
<!-- 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">@​typescript-eslint/parser's
changelog</a>.</em></p>
<blockquote>
<h2>8.72.1 (2026-10-26)</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.72.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.72.0 (2026-10-19)</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.72.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.71.2 (2026-10-12)</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.71.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.71.1 (2026-10-05)</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.71.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.71.0 (2026-09-28)</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.71.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>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/4ad6b7553781fa3c93fc26642d7219d2e6624504"><code>4ad6b75</code></a>
chore(release): publish 8.72.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/77576945345c1537635e617ea0d4b34b003b15ff"><code>7757694</code></a>
chore: cleanup typecheck &amp; build and fix race condition (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/13071">#13071</a>)</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/a4c2603d3e4fd43b33bc25b82457716e9f7cef71"><code>a4c2603</code></a>
chore(release): publish 8.72.0</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/7488d681f6e0e96c2aaa1eee9c3d1b9b1ef077cb"><code>7488d68</code></a>
chore(release): publish 8.71.2</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/d57b8da272375b6a45270639894a793933208a91"><code>d57b8da</code></a>
chore(release): publish 8.71.1</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/8695664c5ee3fbb8fd76907fc45e148579cc3162"><code>8695664</code></a>
chore(release): publish 8.71.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.72.1/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `prettier` from 3.9.9 to 3.9.10
<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.9.10</h2>
<ul>
<li>Markdown: Update <code>micromark-extension-gfm-table</code> to fix
the table parse issue on performance (<a
href="https://redirect.github.com/prettier/prettier/pull/20266">#20266</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.9.10/CHANGELOG.md#3910">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.9.10</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.9.9...3.9.10">diff</a></p>
<h4>Markdown: Update <code>micromark-extension-gfm-table</code> to fix
the table parse issue on performance (<a
href="https://redirect.github.com/prettier/prettier/pull/20266">#20266</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<p>See <a
href="https://github.com/micromark/micromark-extension-gfm-table/commit/4f43ead37b786bc531d924530d4ab4c714886267">https://github.com/micromark/micromark-extension-gfm-table/commit/4f43ead37b786bc531d924530d4ab4c714886267</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/prettier/commit/1172b82c295c4f889fd787dc6c78c3e194ebf353"><code>1172b82</code></a>
Release 3.9.10</li>
<li><a
href="https://github.com/prettier/prettier/commit/d0142273aff2c76f8a69785d42ee2cedc4b0aaa3"><code>d014227</code></a>
Update <code>micromark-extension-gfm-table</code> to fix the table parse
issue on perfor...</li>
<li><a
href="https://github.com/prettier/prettier/commit/953b9084329dcd06ed1072a04b333f1e6e9371e8"><code>953b908</code></a>
Bump Prettier dependency to 3.9.9</li>
<li><a
href="https://github.com/prettier/prettier/commit/ad8a6bf4fa23c42fd114683fec43b28a11bd24af"><code>ad8a6bf</code></a>
Clean changelog_unreleased</li>
<li>See full diff in <a
href="https://github.com/prettier/prettier/compare/3.9.9...3.9.10">compare
view</a></li>
</ul>
</details>
<br />

Updates `@langchain/core` from 1.2.13 to 1.3.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchainjs/releases">@​langchain/core's
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.3.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/12026">#12026</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/178af31ee41b7e36fa2971af61ade6a5460b6d2e"><code>178af31</code></a>
Thanks <a href="https://github.com/eliornl"><code>@​eliornl</code></a>!
- fix(core): deliver callbacks once to a handler registered twice by
merged configs</p>
<p>Merging two runnable configs that both carry the same callback
handler, as happens when an agent's <code>streamEvents()</code> runs
inside a LangGraph node with tracing on, registered that handler twice,
so it received every callback twice: every streamed chunk arrived twice.
The configured callback manager now keeps each handler once, in order,
including a handler passed as a plain object of methods that merged
configs wrapped twice.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/12042">#12042</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/50343ba94c2388b3d3a7d4d6cd0977fa60a90294"><code>50343ba</code></a>
Thanks <a href="https://github.com/sera-wi"><code>@​sera-wi</code></a>!
- fix(core): include node <code>metadata</code> in
<code>Graph.toJSON()</code> output when set, matching Python</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/12048">#12048</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/304d190a30343ecb8ae5f06081a2b5a6d93d9968"><code>304d190</code></a>
Thanks <a href="https://github.com/sera-wi"><code>@​sera-wi</code></a>!
- fix(core): escape <code>drawMermaid</code> node metadata labels and
leave out object values, so metadata with brackets or objects no longer
breaks Mermaid parsing</p>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.3.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11986">#11986</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/2caf64da3f02730f3cd2aab0c15de6eab41a01da"><code>2caf64d</code></a>
Thanks <a
href="https://github.com/richardscarrott"><code>@​richardscarrott</code></a>!
- feat: stream tool-call args as <code>tool-call-delta</code> slices</p>
<p>Tool-call and server-tool-call args now stream as
<code>tool-call-delta</code> events, which carry only the new slice of
the args, instead of <code>block-delta</code> snapshots of the args so
far. A large tool call now costs about the same bytes on the wire as the
same length of text.</p>
<ul>
<li>This covers the compat bridge (any model that only implements
<code>_streamResponseChunks</code>), the OpenAI-compatible converter
(OpenAI Completions, Groq, Mistral, IBM, OpenRouter, Perplexity,
DeepSeek, Fireworks, Together AI and xAI Completions), OpenAI and xAI
Responses, Anthropic (client and server tool calls) and Bedrock
Converse. Google and Ollama still send one complete snapshot per
call.</li>
<li>A tool call's <code>id</code>, <code>name</code> and
<code>index</code> go on <code>content-block-start</code>, which carries
no args. An <code>id</code> or <code>name</code> that arrives later
reaches consumers on <code>content-block-finish</code>.</li>
<li>If you read raw events and their <code>block-delta</code>
<code>fields.args</code> mid-stream, or write agent stream transformers
that do, read <code>tool-call-delta</code> instead. <code>await
stream</code>, <code>.toolCalls</code> and the assembled message are
unchanged.</li>
<li>Upgrade your clients first. Older clients mishandle
<code>tool-call-delta</code>. You need
<code>@langchain/langgraph-sdk</code> 1.12.6 or later;
<code>@langchain/react</code>, <code>@langchain/vue</code>,
<code>@langchain/svelte</code> or <code>@langchain/angular</code> 1.2.6
or later; and <code>langchain-core</code> 1.6.13 or later for Python
clients.</li>
<li>To hold back, pin <code>@langchain/core</code> below 1.3.0. That
also keeps you on <code>@langchain/openai</code>,
<code>@langchain/anthropic</code>, <code>@langchain/aws</code> and
<code>@langchain/xai</code> versions that send snapshots.</li>
</ul>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.20</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11922">#11922</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d71c542fb1d9cd1ceb310a4759591c024cf0d57d"><code>d71c542</code></a>
Thanks <a
href="https://github.com/richardscarrott"><code>@​richardscarrott</code></a>!
- feat(core): add <code>midConversationSystemMessages</code> and
<code>midConversationTools</code> to <code>ModelProfile</code>. They say
whether a chat model sends a system message after the first turn in
place, and whether it can add a tool through a message rather than the
request's tool list. Either way the prompt prefix before it stays
unchanged.</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.19</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11982">#11982</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/5af4684bd19a16bb59950801da92245733482fb3"><code>5af4684</code></a>
Thanks <a
href="https://github.com/richardscarrott"><code>@​richardscarrott</code></a>!
- feat(core): read <code>tool-call-delta</code> in
<code>ChatModelStream</code></p>
<ul>
<li><code>ChatModelStream</code> appends <code>tool-call-delta</code>
slices to a tool call's args, and still replaces them on
<code>block-delta</code></li>
<li>It ignores delta types it doesn't recognize instead of throwing
<code>Unknown delta type</code></li>
</ul>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.18</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11956">#11956</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/93d62b008e13e10f28724a5ab09ab6eda584f1cb"><code>93d62b0</code></a>
Thanks <a href="https://github.com/hntrl"><code>@​hntrl</code></a>! -
Require langsmith &gt;=0.11.0 to include the restored NullableHeaders
type declarations.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11822">#11822</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/d3e6fe66a5f902fa615f076ca71d9541f1291166"><code>d3e6fe6</code></a>
Thanks <a href="https://github.com/dqbd"><code>@​dqbd</code></a>! - Add
explicit agent address support to <code>LangChainTracer</code>, inherit
project and address destinations together, and delegate default
destination resolution to the LangSmith SDK.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langchainjs/pull/11150">#11150</a>
<a
href="https://github.com/langchain-ai/langchainjs/commit/cea15cf9ef5216125b24f23bb5ad06b7c1a91b33"><code>cea15cf</code></a>
Thanks <a
href="https://github.com/mdnanocom"><code>@​mdnanocom</code></a>! -
fix(core): defer tool call chunk collapsing in AIMessageChunk to fix
O(n²) streaming aggregation</p>
<p><code>AIMessageChunk</code> eagerly re-ran
<code>collapseToolCallChunks</code> in its constructor on every streamed
delta <code>concat</code>, re-parsing all accumulated tool args each
time — O(n²) over a stream, which can block the event loop for seconds
on long tool-call streams. Collapsing now runs lazily, once, on first
read of <code>tool_calls</code> / <code>invalid_tool_calls</code>.</p>
</li>
</ul>
<h2><code>@​langchain/core</code><a
href="https://github.com/1"><code>@​1</code></a>.2.17</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/e8c5dc82ba52cff3bc08cd71f963a69596d7ea4d"><code>e8c5dc8</code></a>
chore: version packages (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12047">#12047</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/b5135cc189adbf313f4e92c1527f96a40a546977"><code>b5135cc</code></a>
feat(openai,langchain): support Decisions API and agent middleware (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/11930">#11930</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/52d2ba8ea50149f2314df63b79582f8508034a3b"><code>52d2ba8</code></a>
fix(aws): use this.caller in ChatBedrockConverse to support retries (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/11463">#11463</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/178af31ee41b7e36fa2971af61ade6a5460b6d2e"><code>178af31</code></a>
fix(core): deliver callbacks once to a handler registered twice by
merged con...</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/304d190a30343ecb8ae5f06081a2b5a6d93d9968"><code>304d190</code></a>
fix(core): make drawMermaid node metadata labels safe (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12048">#12048</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/50343ba94c2388b3d3a7d4d6cd0977fa60a90294"><code>50343ba</code></a>
fix(core): include node metadata in Graph.toJSON() (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12042">#12042</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/d1d0692d8498d7e1521c854d90af0395535df4d8"><code>d1d0692</code></a>
chore: version packages (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12044">#12044</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/95b191b1928ee8f4e20e51aa821f5c5fd4b8b3da"><code>95b191b</code></a>
feat(openai): refresh model profiles from models.dev (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12046">#12046</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/81577b3e84c3f38581e76e8335faa89511e99d74"><code>81577b3</code></a>
fix(langchain): re-throw a drained tool from the agent's ToolNode (<a
href="https://redirect.github.com/langchain-ai/langchainjs/issues/12043">#12043</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchainjs/commit/4cbf0f5938710ce1669c679febdbea41b733a100"><code>4cbf0f5</code></a>
chore(deps): bump the npm_and_yarn group across 2 directories with 1
update (...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchainjs/compare/@langchain/core@1.2.13...@langchain/core@1.3.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@langchain/langgraph` from 1.4.18 to 1.5.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraphjs/releases">@​langchain/langgraph's
releases</a>.</em></p>
<blockquote>
<h2><code>@​langchain/langgraph-api</code><a
href="https://github.com/1"><code>@​1</code></a>.5.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/langchain-ai/langgraphjs/pull/2961">#2961</a>
<a
href="https://github.com/langchain-ai/langgraphjs/commit/1631fd895723c4553345ceb5d9fc425b68bda959"><code>1631fd8</code></a>
Thanks <a href="https://gith...

_Description has been truncated_

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-01 16:50:41 -08:00
dependabot[bot] d05c887163 chore(deps): bump the uv group across 2 directories with 1 update (#9513)
Bumps the uv group with 1 update in the /libs/cli/uv-examples/monorepo
directory: [langgraph-sdk](https://github.com/langchain-ai/langgraph).
Bumps the uv group with 1 update in the /libs/cli/uv-examples/simple
directory: [langgraph-sdk](https://github.com/langchain-ai/langgraph).

Updates `langgraph-sdk` from 0.4.4 to 0.4.11
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraph/releases">langgraph-sdk's
releases</a>.</em></p>
<blockquote>
<h2>langgraph-sdk==0.4.11</h2>
<p>Changes since sdk==0.4.10</p>
<ul>
<li>release(langgraph): 1.2.19 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9504">#9504</a>)</li>
<li>release(sdk-py): 0.4.11 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9503">#9503</a>)</li>
<li>test(sdk-py): check that a reconnect loop resumes after the newest
event (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9483">#9483</a>)</li>
<li>fix(sdk-py): retry runs.wait, join and cancel(wait=True) reconnects
with backoff (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9481">#9481</a>)</li>
<li>fix(sdk-py): warn when a stream reconnects without a Last-Event-ID
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9482">#9482</a>)</li>
<li>fix(sdk-py): preserve SSE data and cursor state across chunks (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9477">#9477</a>)</li>
<li>fix(sdk-py): retry failed stream reconnects with backoff (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9462">#9462</a>)</li>
<li>fix(sdk-py): match Auth.on overload to optional resources (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9450">#9450</a>)</li>
<li>fix(sdk-py): reset the reconnect cursor on every empty id (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9440">#9440</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.10</h2>
<p>Changes since sdk==0.4.9</p>
<ul>
<li>release(checkpoint): 4.3.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9447">#9447</a>)</li>
<li>release(langgraph): 1.2.18 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9445">#9445</a>)</li>
<li>release(sdk-py): 0.4.10 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9446">#9446</a>)</li>
<li>fix(sdk-py): retry runs.wait with a bodyless GET and raise run
errors in sync wait (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9399">#9399</a>)</li>
<li>fix(sdk-py): follow the SSE spec for multi-line data, empty ids and
a leading BOM (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9402">#9402</a>)</li>
<li>fix(sdk-py): accept durability, stream_resumable and context in the
runs overloads (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9403">#9403</a>)</li>
<li>release(langgraph): 1.2.17 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9418">#9418</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.9</h2>
<p>Changes since sdk==0.4.8</p>
<ul>
<li>release(sdk-py): 0.4.9 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9398">#9398</a>)</li>
<li>feat(sdk-py): add Priority for thread priority settings (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9392">#9392</a>)</li>
<li>chore(deps): remove precautionary third-party upper bounds (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9390">#9390</a>)</li>
<li>chore(deps): bump certifi from 2026.2.25 to 2026.10.19 in
/libs/sdk-py (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9370">#9370</a>)</li>
<li>chore(deps-dev): bump dotenv from 17.4.2 to 18.0.4 in
/libs/cli/js-examples in the major group (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9154">#9154</a>)</li>
<li>chore(deps-dev): bump the minor-and-patch group in /libs/prebuilt
with 6 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9150">#9150</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.8</h2>
<p>Changes since sdk==0.4.7</p>
<ul>
<li>release(langgraph): 1.2.16 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9334">#9334</a>)</li>
<li>release(sdk-py): 0.4.8 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9335">#9335</a>)</li>
<li>fix(sdk-py): surface thread stream errors and stop retrying a
rejected stream (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9317">#9317</a>)</li>
<li>release(prebuilt): 1.1.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9316">#9316</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.7</h2>
<p>Changes since sdk==0.4.6</p>
<ul>
<li>release(sdk-py): 0.4.7 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9289">#9289</a>)</li>
<li>release(langgraph): 1.2.15 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9283">#9283</a>)</li>
<li>fix(sdk-py): end a thread stream's run only on a root lifecycle
event (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9228">#9228</a>)</li>
<li>fix: order delta channel replay by task path (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8544">#8544</a>)</li>
<li>release(langgraph): 1.2.14 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9215">#9215</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/05e4efe712ae6b9dd9560007395c9ee248f45b1f"><code>05e4efe</code></a>
release(cli): relax api bounds (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6606">#6606</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/c80d93c78a6e6223c3df1581f0e70466110fd4b8"><code>c80d93c</code></a>
fix(cli): Escape variable substitution in compose codegen (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6594">#6594</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/87cb5095285ba809b547584d31bae26046fc844a"><code>87cb509</code></a>
feat(checkpoint): Validate checkpointer type at compile time (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6586">#6586</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/84023451a2bd5987b1d4df530f4145d503d75ccb"><code>8402345</code></a>
release(langgraph): bump to 1.0.5 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6582">#6582</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/efe78447b3cdd424702274ffda918961af07e560"><code>efe7844</code></a>
feat(sdk-py): emit id as part of stream events (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6581">#6581</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/df191731918482c5eaf2934a2df0c9cb3571db0c"><code>df19173</code></a>
fix: update readme (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6570">#6570</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/4d01e69b823c4126f375cbed72fed7c5797d1b91"><code>4d01e69</code></a>
release(checkpoint-postgres): 3.0.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6568">#6568</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/e86b5f4da21f0fa00bec19fe0e86d031dba0b004"><code>e86b5f4</code></a>
chore: pgqs (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6567">#6567</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/b70d5aac0ee330e9dde22ca9c7e8f0774e44da54"><code>b70d5aa</code></a>
release(checkpoint-sqlite): 3.0.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6566">#6566</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/297242913f8ad2143ee3e2f72e67db0911d48e2a"><code>2972429</code></a>
fix(checkpoint-sqlite): harden (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6565">#6565</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langgraph/compare/0.4.4...cli==0.4.11">compare
view</a></li>
</ul>
</details>
<br />

Updates `langgraph-sdk` from 0.4.4 to 0.4.11
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraph/releases">langgraph-sdk's
releases</a>.</em></p>
<blockquote>
<h2>langgraph-sdk==0.4.11</h2>
<p>Changes since sdk==0.4.10</p>
<ul>
<li>release(langgraph): 1.2.19 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9504">#9504</a>)</li>
<li>release(sdk-py): 0.4.11 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9503">#9503</a>)</li>
<li>test(sdk-py): check that a reconnect loop resumes after the newest
event (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9483">#9483</a>)</li>
<li>fix(sdk-py): retry runs.wait, join and cancel(wait=True) reconnects
with backoff (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9481">#9481</a>)</li>
<li>fix(sdk-py): warn when a stream reconnects without a Last-Event-ID
(<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9482">#9482</a>)</li>
<li>fix(sdk-py): preserve SSE data and cursor state across chunks (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9477">#9477</a>)</li>
<li>fix(sdk-py): retry failed stream reconnects with backoff (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9462">#9462</a>)</li>
<li>fix(sdk-py): match Auth.on overload to optional resources (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9450">#9450</a>)</li>
<li>fix(sdk-py): reset the reconnect cursor on every empty id (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9440">#9440</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.10</h2>
<p>Changes since sdk==0.4.9</p>
<ul>
<li>release(checkpoint): 4.3.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9447">#9447</a>)</li>
<li>release(langgraph): 1.2.18 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9445">#9445</a>)</li>
<li>release(sdk-py): 0.4.10 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9446">#9446</a>)</li>
<li>fix(sdk-py): retry runs.wait with a bodyless GET and raise run
errors in sync wait (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9399">#9399</a>)</li>
<li>fix(sdk-py): follow the SSE spec for multi-line data, empty ids and
a leading BOM (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9402">#9402</a>)</li>
<li>fix(sdk-py): accept durability, stream_resumable and context in the
runs overloads (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9403">#9403</a>)</li>
<li>release(langgraph): 1.2.17 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9418">#9418</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.9</h2>
<p>Changes since sdk==0.4.8</p>
<ul>
<li>release(sdk-py): 0.4.9 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9398">#9398</a>)</li>
<li>feat(sdk-py): add Priority for thread priority settings (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9392">#9392</a>)</li>
<li>chore(deps): remove precautionary third-party upper bounds (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9390">#9390</a>)</li>
<li>chore(deps): bump certifi from 2026.2.25 to 2026.10.19 in
/libs/sdk-py (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9370">#9370</a>)</li>
<li>chore(deps-dev): bump dotenv from 17.4.2 to 18.0.4 in
/libs/cli/js-examples in the major group (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9154">#9154</a>)</li>
<li>chore(deps-dev): bump the minor-and-patch group in /libs/prebuilt
with 6 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9150">#9150</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.8</h2>
<p>Changes since sdk==0.4.7</p>
<ul>
<li>release(langgraph): 1.2.16 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9334">#9334</a>)</li>
<li>release(sdk-py): 0.4.8 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9335">#9335</a>)</li>
<li>fix(sdk-py): surface thread stream errors and stop retrying a
rejected stream (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9317">#9317</a>)</li>
<li>release(prebuilt): 1.1.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9316">#9316</a>)</li>
</ul>
<h2>langgraph-sdk==0.4.7</h2>
<p>Changes since sdk==0.4.6</p>
<ul>
<li>release(sdk-py): 0.4.7 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9289">#9289</a>)</li>
<li>release(langgraph): 1.2.15 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9283">#9283</a>)</li>
<li>fix(sdk-py): end a thread stream's run only on a root lifecycle
event (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9228">#9228</a>)</li>
<li>fix: order delta channel replay by task path (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8544">#8544</a>)</li>
<li>release(langgraph): 1.2.14 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9215">#9215</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/05e4efe712ae6b9dd9560007395c9ee248f45b1f"><code>05e4efe</code></a>
release(cli): relax api bounds (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6606">#6606</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/c80d93c78a6e6223c3df1581f0e70466110fd4b8"><code>c80d93c</code></a>
fix(cli): Escape variable substitution in compose codegen (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6594">#6594</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/87cb5095285ba809b547584d31bae26046fc844a"><code>87cb509</code></a>
feat(checkpoint): Validate checkpointer type at compile time (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6586">#6586</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/84023451a2bd5987b1d4df530f4145d503d75ccb"><code>8402345</code></a>
release(langgraph): bump to 1.0.5 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6582">#6582</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/efe78447b3cdd424702274ffda918961af07e560"><code>efe7844</code></a>
feat(sdk-py): emit id as part of stream events (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6581">#6581</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/df191731918482c5eaf2934a2df0c9cb3571db0c"><code>df19173</code></a>
fix: update readme (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6570">#6570</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/4d01e69b823c4126f375cbed72fed7c5797d1b91"><code>4d01e69</code></a>
release(checkpoint-postgres): 3.0.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6568">#6568</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/e86b5f4da21f0fa00bec19fe0e86d031dba0b004"><code>e86b5f4</code></a>
chore: pgqs (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6567">#6567</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/b70d5aac0ee330e9dde22ca9c7e8f0774e44da54"><code>b70d5aa</code></a>
release(checkpoint-sqlite): 3.0.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6566">#6566</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/297242913f8ad2143ee3e2f72e67db0911d48e2a"><code>2972429</code></a>
fix(checkpoint-sqlite): harden (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/6565">#6565</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langgraph/compare/0.4.4...cli==0.4.11">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-11-01 16:50:37 -08:00
dependabot[bot] 21ab811f91 chore(deps-dev): bump the minor-and-patch group in /libs/checkpoint-conformance with 4 updates (#9523)
Bumps the minor-and-patch group in /libs/checkpoint-conformance with 4
updates: [pytest](https://github.com/pytest-dev/pytest),
[pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio),
[ruff](https://github.com/astral-sh/ruff) and
[ty](https://github.com/astral-sh/ty).

Updates `pytest` from 9.1.1 to 9.2.0
<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.2.0</h2>
<h1>pytest 9.2.0 (2026-10-27)</h1>
<h2>Removals and backward incompatible breaking changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14716">#14716</a>:
<code>-c</code> now raises a usage error for a path that does not exist,
is a directory, or is in a format pytest has no loader for. Such paths
were previously either ignored or reported as an unhandled
<code>FileNotFoundError</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/8593">#8593</a>:
The <code>indirect</code>, <code>ids</code> and <code>scope</code>
arguments of <code>Metafunc.parametrize()
&lt;pytest.Metafunc.parametrize&gt;</code> -- and therefore of
<code>@pytest.mark.parametrize &lt;pytest.mark.parametrize
ref&gt;</code> -- are now keyword-only.</p>
<p>Passing them positionally was a common source of confusing errors,
e.g. <code>parametrize(&quot;arg1&quot;, &quot;arg2&quot;, [(1,
2)])</code> made the argvalues land in <code>indirect</code> and failed
with <code>indirect fixture '(1, 2)' doesn't exist</code>; such mistakes
now fail with an understandable <code>TypeError</code>. The type
annotations have always declared these arguments as keyword-only.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/993">#993</a>:
Plugin import failures at startup are now reported and classified
instead of escaping as a raw traceback with the accidental exit code
<code>1</code>:</p>
<ul>
<li>a plugin that cannot be found -- via <code>-p</code>,
<code>pytest_plugins</code> or <code>PYTEST_PLUGINS</code> -- exits with
<code>pytest.ExitCode</code> <code>USAGE_ERROR</code> (<code>4</code>),
like a <code>conftest.py</code> that fails to import;</li>
<li>a plugin that is found but raises while importing -- including a
broken <code>pytest11</code> entry point or a missing plugin dependency
-- exits with <code>pytest.ExitCode</code> <code>INTERNAL_ERROR</code>
(<code>3</code>), with the traceback preserved.</li>
</ul>
<p><code>pytest.main</code> now returns these codes instead of raising
<code>ImportError</code>. Also fixed an <code>IndexError</code> from
pytest's own internals when the plugin raised an exception with no
arguments.</p>
</li>
</ul>
<h2>Deprecations (removal in next major release)</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14742">#14742</a>:
The private <code>_pytest.python.CallSpec2</code> name has been renamed
to <code>CallSpec</code>.
The old <code>CallSpec2</code> alias remains available for now and will
be removed in pytest 10 or 11.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14755">#14755</a>:
Schedule <em>yield_fixture</em> decorator for removal in pytest 10 or
11.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14774">#14774</a>:
Similarly to the deprecation of class-scoped fixtures defined as
instance methods in pytest 9.1,
<em>all</em> higher-scoped fixtures (<code>module</code>,
<code>package</code>, and <code>session</code>) defined
as instance methods are now deprecated.</p>
<p>They suffer from the same issue the original deprecation only fixed
for
class-scoped fixtures, see <code>10819</code> and
<code>14011</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14808">#14808</a>:
Passing a non-string value to a <code>string</code>-typed ini option now
emits a <code>pytest.PytestRemovedIn10Warning</code>. This affects
<code>[tool.pytest.ini_options]</code> in <code>pyproject.toml</code>.
In pytest 10 this will raise a <code>TypeError</code>, matching the
behavior of the corresponding TOML config path.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/7323">#7323</a>:
Passing <code>bytes</code> to <code>TerminalReporter.write_line()</code>
is now deprecated, pass a <code>str</code> instead.</p>
</li>
</ul>
<h2>New features</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14743">#14743</a>:
<!-- raw HTML omitted -->Full diff:<!-- raw HTML omitted --> now
contains a legend explaining the <!-- raw HTML omitted -->+<!-- raw HTML
omitted --> &amp; <!-- raw HTML omitted -->-<!-- raw HTML omitted -->
symbols in diff output.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/2781">#2781</a>:
When <code>strict_markers</code> is enabled, marker names used in
<code>-m</code> expressions are now validated against registered
markers.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/6881">#6881</a>:
Added the <code>parametrize_long_str_id_strategy</code> configuration
option to control how long <code>str</code>/<code>bytes</code> parameter
values are handled in auto-generated test IDs. Explicit IDs set via
<code>ids=[...]</code> or <code>pytest.param(..., id=...)</code> are not
affected. Available strategies: <code>short</code> (default, values over
100 chars fall back to <code>argname+index</code>), <code>sha256</code>
(SHA-256 hex digest), <code>legacy</code> (keep full value), and
<code>disallow</code> (error requesting explicit IDs).</li>
</ul>
<h2>Improvements in existing functionality</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/10745">#10745</a>:
A <code>RecursionError</code> whose recursion origin cannot be located
now shows only the
first and last 10 stack frames, matching what pytest already did when
locating
the origin failed outright. Previously the full traceback -- often
around a
thousand near-identical frames -- was rendered, which was both slow and
unreadable. Pass <code>--full-trace</code> to see every frame.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ae1d0ff57b8bd4252da9ade7353523ac0ac73281"><code>ae1d0ff</code></a>
Prepare release version 9.2.0</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/143ce31dea9b12ff2ab6add04361ee01ab114931"><code>143ce31</code></a>
pre-commit: exclude codespell on generated release announcements</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/532dc5fc78f57de5115c5ecc9c6c29257787ada7"><code>532dc5f</code></a>
tox: add attrs dependency to regen, needed in reportingdemo.rst</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7b2446ee41a7298d3a0bc22c52f3b8bacae1566"><code>a7b2446</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15245">#15245</a>
from pytest-dev/claude/project-thread-9jcf91</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/679d09c6945156746e17e9ec0b1c8309bbde3ad3"><code>679d09c</code></a>
[pre-commit.ci] pre-commit autoupdate (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/15266">#15266</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/41d12cdbb4a35a7877768d93d8dc50a9117a8d8c"><code>41d12cd</code></a>
docs: clarify typing for yield fixtures</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/b9022ba04a33bfc76b5c9aefb8974755a80bf5ce"><code>b9022ba</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15182">#15182</a>
from 1871698/docs/improve-getting-started-description</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/16f87a7f364a7362fbfbb204c79a47ad567b4fbd"><code>16f87a7</code></a>
docs: improve getting-started.rst test execution description</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/082ed70e314ee8e1ab117763d17fe801648f7508"><code>082ed70</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15255">#15255</a>
from pytest-dev/claude/project-thread-eoi3d6</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ae37a013763e9c4a143245eda1acdc3c27961685"><code>ae37a01</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15227">#15227</a>
from pytest-dev/claude/project-thread-4etye4</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.1.1...9.2.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `pytest-asyncio` from 1.4.0 to 1.4.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest-asyncio/releases">pytest-asyncio's
releases</a>.</em></p>
<blockquote>
<h2>pytest-asyncio v1.4.1</h2>
<h1><a
href="https://github.com/pytest-dev/pytest-asyncio/tree/1.4.1">1.4.1</a>
- 2026-10-25</h1>
<h2>Added</h2>
<ul>
<li>Declared support for Python 3.15 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1588">#1588</a>)</li>
</ul>
<h2>Fixed</h2>
<ul>
<li>Allow <em>AsyncIterable</em> as a return type for asynchronous
generator fixtures decorated with <em>pytest_asyncio.fixture</em> (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1090">#1090</a>)</li>
</ul>
<h2>Notes for Downstream Packagers</h2>
<ul>
<li>Source-tree development dependencies now use PEP 735 dependency
groups and <code>uv.lock</code>. (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1518">#1518</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/6af7f5c9a8c77fe5040b9d368b9ed01837c25652"><code>6af7f5c</code></a>
chore: Prepare release of v1.4.1.</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/32ccf64ec115e42543bddb8cd142b2026b0cab77"><code>32ccf64</code></a>
Support 3.15 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1588">#1588</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/983007120dcc53b14f8f7e9df492cbd1d20a242c"><code>9830071</code></a>
Build(deps): Bump certifi from 2026.6.17 to 2026.10.19 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1590">#1590</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/0238023e439113044a9ed095307884c774ec9e46"><code>0238023</code></a>
Build(deps): Bump virtualenv from 21.7.13 to 21.14.13 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1589">#1589</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/93893ecd9447d105ebd56419bc5162aa2420587e"><code>93893ec</code></a>
Build(deps): Bump astral-sh/setup-uv from 10.2.0 to 10.3.0 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1583">#1583</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/f287e5e0f5425380b24c32b64037dfb2ed3cb937"><code>f287e5e</code></a>
Build(deps): Bump actions/download-artifact from 8.0.1 to 8.0.2 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1581">#1581</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/b71da4fe09265e1ad86d4cbfc452b590ebfc4daa"><code>b71da4f</code></a>
Build(deps-dev): Bump hypothesis from 6.168.3 to 6.168.5 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1582">#1582</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/d8c51d8990a4bbdf8a19ff0cf80d68b95b11dea8"><code>d8c51d8</code></a>
Build(deps): Bump actions/upload-artifact from 7.0.1 to 7.0.2 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1580">#1580</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/b12808bd8d061b583ccdddc53ad6c85198509eed"><code>b12808b</code></a>
Build(deps): Bump virtualenv from 21.7.12 to 21.7.13 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1578">#1578</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/e0601f0ce9532de0b5453a5cfd08f25b258a93f5"><code>e0601f0</code></a>
Build(deps-dev): Bump coverage from 7.16.1 to 7.16.2 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1577">#1577</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest-asyncio/compare/v1.4.0...v1.4.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.16.9 to 0.17.3
<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.17.3</h2>
<h2>Release Notes</h2>
<p>Released on 2026-10-26.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>suspicious</code>] Add <code>shared-clock-mock</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29074">#29074</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>pyupgrade</code>] Avoid incorrect conversions to f-strings
(<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25485">#25485</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Buffer stdout in <code>ruff format</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29631">#29631</a>)</li>
<li>Specialize ASCII identifier lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29503">#29503</a>)</li>
<li>Speed up identifier and keyword lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29463">#29463</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>flake8-bandit</code>] Describe what the rule actually checks
for <code>httpx</code> (<code>S113</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/27936">#27936</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/itsvedantkumar"><code>@​itsvedantkumar</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
</ul>
<h2>Install ruff 0.17.3</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.17.3/ruff-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/ruff/releases/download/0.17.3/ruff-installer.ps1
| iex&quot;
</code></pre>
<h2>Download ruff 0.17.3</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/ruff/releases/download/0.17.3/ruff-aarch64-apple-darwin.tar.gz">ruff-aarch64-apple-darwin.tar.gz</a></td>
<td>Apple Silicon macOS</td>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-aarch64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-x86_64-apple-darwin.tar.gz">ruff-x86_64-apple-darwin.tar.gz</a></td>
<td>Intel macOS</td>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-x86_64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-aarch64-pc-windows-msvc.zip">ruff-aarch64-pc-windows-msvc.zip</a></td>
<td>ARM64 Windows</td>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-aarch64-pc-windows-msvc.zip.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/ruff/blob/main/CHANGELOG.md">ruff's
changelog</a>.</em></p>
<blockquote>
<h2>0.17.3</h2>
<p>Released on 2026-10-26.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>suspicious</code>] Add <code>shared-clock-mock</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29074">#29074</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>pyupgrade</code>] Avoid incorrect conversions to f-strings
(<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25485">#25485</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Buffer stdout in <code>ruff format</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29631">#29631</a>)</li>
<li>Specialize ASCII identifier lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29503">#29503</a>)</li>
<li>Speed up identifier and keyword lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29463">#29463</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>flake8-bandit</code>] Describe what the rule actually checks
for <code>httpx</code> (<code>S113</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/27936">#27936</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/itsvedantkumar"><code>@​itsvedantkumar</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
</ul>
<h2>0.17.2</h2>
<p>Released on 2026-10-22.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>complexity</code>] Add
<code>unnecessary-regular-expression-compile</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25533">#25533</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Avoid syntax errors for type variables with starred constraints
(<code>UP040</code>, <code>UP046</code>, <code>UP047</code>,
<code>RUF053</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/28362">#28362</a>)</li>
<li>[<code>pydocstyle</code>] Treat backslash-escaped <code>*args</code>
and <code>**kwargs</code> as documented (<code>D417</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29536">#29536</a>)</li>
<li>[<code>pylint</code>] Avoid false positive on starred elements
(<code>PLE0643</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29483">#29483</a>)</li>
<li>[<code>pylint</code>] Mark fixes unsafe when deleting comments
(<code>PLR5501</code>, <code>PLW0120</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29414">#29414</a>)</li>
<li>[<code>pyupgrade</code>] Mark fix unsafe when deleting comments
(<code>UP035</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29413">#29413</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Avoid intermediate names in lint name resolution (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29506">#29506</a>)</li>
<li>Prefilter bidirectional Unicode checks by UTF-8 leading bytes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29561">#29561</a>)</li>
<li>Share postfix parsing and reduce expression copies (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29480">#29480</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/ba65abf8ba7bfbcc059f7fb9712be5ba9cab6abc"><code>ba65abf</code></a>
Bump version to 0.17.3 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29745">#29745</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/fb2f9218793e3b0e979175d43d88dcac791e92d6"><code>fb2f921</code></a>
[ty] Fix narrowing of strings and integers compared with enum members
(<a
href="https://redirect.github.com/astral-sh/ruff/issues/29743">#29743</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/d1812878d5f81af6f5810d193ebc44413d1b21cc"><code>d181287</code></a>
[ty] Index nominal union alternatives (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29196">#29196</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/07872adbafa7c00f42f1174d8aa3c3e324033009"><code>07872ad</code></a>
[ty] Preserve string literals in comparisons with
<code>LiteralString</code> (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29741">#29741</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/95888b2edfaa34d0159732856b1466cf53b33e64"><code>95888b2</code></a>
[ty] Preserve narrowing after guarded match subject captures (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29558">#29558</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/3c8166b683805aa89557b0739f5b9918ad58ea6d"><code>3c8166b</code></a>
[ty] Fix hang when negating recursive unions of instance types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29652">#29652</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/3fd20e08598ac345b8afd524d3f7086456e4e758"><code>3fd20e0</code></a>
[ty] Preserve generic callback specializations during return refinement
(<a
href="https://redirect.github.com/astral-sh/ruff/issues/29667">#29667</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/2f0a29ece1f1a539c65c0671c04c76bb886219fe"><code>2f0a29e</code></a>
[ty] Record predicate truthiness alongside expression inference (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29706">#29706</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/60f104a1a2ec4e980198ff54c38ba6fe7045b29f"><code>60f104a</code></a>
[ty] Honor <a
href="https://github.com/override"><code>@​override</code></a> on
overload implementations in stubs (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29470">#29470</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/207ec3affeb118fce03a65f9333959157e228312"><code>207ec3a</code></a>
[ty] Add diagnostic baseline support (<a
href="https://redirect.github.com/astral-sh/ruff/issues/27636">#27636</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.16.9...0.17.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `ty` from 0.0.84 to 0.0.94
<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.94</h2>
<h2>Release Notes</h2>
<p>Released on 2026-10-28.</p>
<h3>Bug fixes</h3>
<ul>
<li>Correctly handle line continuation when fixing unused ignore
comments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29781">#29781</a>)</li>
<li>Fix Linux x86-64 startup on older kernels (<a
href="https://redirect.github.com/astral-sh/ty/pull/4853">#4853</a>)</li>
<li>Fix a panic when a comprehension filter depends on a later binding
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/29786">#29786</a>)</li>
<li>Fix a panic when sorting diagnostics without a source location (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29780">#29780</a>)</li>
<li>Fix panic on a chained assignment with a missing value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29787">#29787</a>)</li>
<li>Reject defaulted TypeVars after a TypeVarTuple in legacy generic
classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29753">#29753</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Django: suppress invalid-override errors for <code>Meta</code>
configurations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29769">#29769</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/sharkdp"><code>@​sharkdp</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
</ul>
<h2>Install ty 0.0.94</h2>
<h3>Install prebuilt binaries via shell script</h3>
<pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf
https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-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/ty/releases/download/0.0.94/ty-installer.ps1
| iex&quot;
</code></pre>
<h2>Download ty 0.0.94</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/ty/releases/download/0.0.94/ty-aarch64-apple-darwin.tar.gz">ty-aarch64-apple-darwin.tar.gz</a></td>
<td>Apple Silicon macOS</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-apple-darwin.tar.gz">ty-x86_64-apple-darwin.tar.gz</a></td>
<td>Intel macOS</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-pc-windows-msvc.zip">ty-aarch64-pc-windows-msvc.zip</a></td>
<td>ARM64 Windows</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-pc-windows-msvc.zip">ty-i686-pc-windows-msvc.zip</a></td>
<td>x86 Windows</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-pc-windows-msvc.zip">ty-x86_64-pc-windows-msvc.zip</a></td>
<td>x64 Windows</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-unknown-linux-gnu.tar.gz">ty-aarch64-unknown-linux-gnu.tar.gz</a></td>
<td>ARM64 Linux</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-unknown-linux-gnu.tar.gz">ty-i686-unknown-linux-gnu.tar.gz</a></td>
<td>x86 Linux</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-powerpc64le-unknown-linux-gnu.tar.gz">ty-powerpc64le-unknown-linux-gnu.tar.gz</a></td>
<td>PPC64LE Linux</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-powerpc64le-unknown-linux-gnu.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/ty/blob/main/CHANGELOG.md">ty's
changelog</a>.</em></p>
<blockquote>
<h2>0.0.94</h2>
<p>Released on 2026-10-28.</p>
<h3>Bug fixes</h3>
<ul>
<li>Correctly handle line continuation when fixing unused ignore
comments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29781">#29781</a>)</li>
<li>Fix Linux x86-64 startup on older kernels (<a
href="https://redirect.github.com/astral-sh/ty/pull/4853">#4853</a>)</li>
<li>Fix a panic when a comprehension filter depends on a later binding
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/29786">#29786</a>)</li>
<li>Fix a panic when sorting diagnostics without a source location (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29780">#29780</a>)</li>
<li>Fix panic on a chained assignment with a missing value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29787">#29787</a>)</li>
<li>Reject defaulted TypeVars after a TypeVarTuple in legacy generic
classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29753">#29753</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Django: suppress invalid-override errors for <code>Meta</code>
configurations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29769">#29769</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/sharkdp"><code>@​sharkdp</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
</ul>
<h2>0.0.93</h2>
<p>Released on 2026-10-28.</p>
<h3>Preview features</h3>
<ul>
<li>
<p>Add diagnostic baselines to help adopt ty in projects with existing
errors (<a
href="https://redirect.github.com/astral-sh/ruff/pull/27636">#27636</a>).
Configure a baseline in <code>pyproject.toml</code>:</p>
<pre lang="toml"><code>[tool.ty]
baseline = &quot;ty-baseline.json&quot;
</code></pre>
<p>Run <code>ty check --update-baseline</code> to record existing
diagnostics. Subsequent checks filter matching diagnostics from CLI
output and show them as hints in the language server. The baseline
format may change while the feature is in preview.</p>
</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Fix a panic when checking truthiness in a loop (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29759">#29759</a>)</li>
<li>Fix hang when negating recursive unions of instance types (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29652">#29652</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Avoid synthesizing primary keys for abstract Django models (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29744">#29744</a>)</li>
<li>Combine constraints from repeated <code>ParamSpec</code>s (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29172">#29172</a>)</li>
<li>Fix narrowing of strings and integers compared with enum members (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29743">#29743</a>)</li>
<li>Honor <code>@override</code> on overload implementations in stubs
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/29470">#29470</a>)</li>
<li>Preserve caller type variables in nested union inference (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29611">#29611</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/f8ea270e0b125116ce52868b16384819183056af"><code>f8ea270</code></a>
Bump version to 0.0.94 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4857">#4857</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/7a36fc91318b07a58d0aa27753db4db1d212032a"><code>7a36fc9</code></a>
Fix Linux x86-64 startup on older kernels (<a
href="https://redirect.github.com/astral-sh/ty/issues/4853">#4853</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/3db5e4df7becd3ca55fd06c78e98bf7c8a38e469"><code>3db5e4d</code></a>
Bump version to 0.0.93 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4842">#4842</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/f8fd0e8b5ce954b5a7f4e38431d805dd58e50e2d"><code>f8fd0e8</code></a>
Update astral-sh/setup-uv action to v10.3.2 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4843">#4843</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/c1ca420006c7e5ef62ddfa16267fd7531a0a1000"><code>c1ca420</code></a>
Update uv to v0.13.6 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4845">#4845</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/4c5c9079ac053fa3f879ee7625ec5c28a0817de4"><code>4c5c907</code></a>
Update prek dependencies (<a
href="https://redirect.github.com/astral-sh/ty/issues/4844">#4844</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/0445d6497ae22a0c480b23807cd71f47889053d8"><code>0445d64</code></a>
Optimize Linux x86-64 ty releases with LLVM BOLT (<a
href="https://redirect.github.com/astral-sh/ty/issues/4833">#4833</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/3933e6918b731d86b3f3a4f1bc47f2ce21195e8e"><code>3933e69</code></a>
Bump version to 0.0.92 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4821">#4821</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/9aaec9ba5896c759e8e93000d0a1dec51f2bae57"><code>9aaec9b</code></a>
Handle AI policy labels through Actions (<a
href="https://redirect.github.com/astral-sh/ty/issues/4812">#4812</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/e1b8efaafd842d3e33c1a21ae7e897434452975b"><code>e1b8efa</code></a>
Update uv to 0.13.5 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4809">#4809</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ty/compare/0.0.84...0.0.94">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-11-01 16:50:19 -08:00
dependabot[bot] d42015a707 chore(deps-dev): bump types-requests from 2.33.0.20260906 to 2.33.0.20261006 in /libs/langgraph (#9535)
Bumps [types-requests](https://github.com/python/typeshed) from
2.33.0.20260906 to 2.33.0.20261006.
<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 />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=types-requests&package-manager=uv&previous-version=2.33.0.20260906&new-version=2.33.0.20261006)](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-11-01 16:48:53 -08:00
dependabot[bot] 93aaa4632c chore(deps): bump the minor-and-patch group in /libs/checkpoint with 6 updates (#9529)
Bumps the minor-and-patch group in /libs/checkpoint with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [langchain-core](https://github.com/langchain-ai/langchain) | `1.6.5`
| `1.7.1` |
| [pytest](https://github.com/pytest-dev/pytest) | `9.1.1` | `9.2.0` |
| [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) |
`1.4.0` | `1.4.1` |
| [pytest-mock](https://github.com/pytest-dev/pytest-mock) | `3.16.0` |
`3.17.0` |
| [ruff](https://github.com/astral-sh/ruff) | `0.16.9` | `0.17.3` |
| [ty](https://github.com/astral-sh/ty) | `0.0.84` | `0.0.94` |

Updates `langchain-core` from 1.6.5 to 1.7.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchain/releases">langchain-core's
releases</a>.</em></p>
<blockquote>
<h2>langchain-core==1.7.1</h2>
<p>Changes since langchain-core==1.7.0</p>
<p>release(core): version 1.7.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41548">#41548</a>)
fix(core): preserve gateway tracing metadata in v3 streams (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41504">#41504</a>)
fix(core): escape node metadata in Mermaid labels (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41512">#41512</a>)
chore(deps): bump the minor-and-patch group across 3 directories with 32
updates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41465">#41465</a>)
chore(deps-dev): bump setuptools from 83.0.0 to 84.1.0 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41442">#41442</a>)
chore(deps): bump nbconvert from 7.17.1 to 7.17.2 in /libs/core (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41425">#41425</a>)
chore(infra): relax third-party dependency upper bounds (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41407">#41407</a>)</p>
<h2>langchain-core==1.7.0</h2>
<p>Changes since langchain-core==1.6.13</p>
<p>release(core): 1.7.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41418">#41418</a>)
feat(core): emit <code>tool-call-delta</code> for streamed tool-call
args (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41257">#41257</a>)</p>
<h2>Notes</h2>
<ul>
<li>Tool-call args now stream as <code>tool-call-delta</code> slices
instead of <code>block-delta</code> snapshots of the args so far. If you
read raw stream events and their <code>block-delta</code> args
mid-stream, read <code>tool-call-delta</code> instead.
<code>.tool_calls.get()</code> and <code>.output</code> are unchanged.
To keep snapshots, pin <code>langchain-core&lt;1.7</code>.</li>
<li>Since 1.6.13, iterating <code>ChatModelStream.tool_calls</code>
yields each call's new args slice, not a snapshot of the args so
far.</li>
</ul>
<h2>langchain-core==1.6.13</h2>
<p>Changes since langchain-core==1.6.12</p>
<p>release(core): 1.6.13 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41397">#41397</a>)
chore(deps): bump certifi from 2025.11.12 to 2026.10.19 in /libs/core
(<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41373">#41373</a>)
feat(core,langchain,standard-tests): read <code>tool-call-delta</code>
in stream consumers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41246">#41246</a>)</p>
<h2>langchain-core==1.6.12</h2>
<p>Changes since langchain-core==1.6.11</p>
<p>release(core): 1.6.12 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41277">#41277</a>)
fix(core): propagate extras on content-block-start events (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41272">#41272</a>)</p>
<h2>langchain-core==1.6.11</h2>
<p>Changes since langchain-core==1.6.10</p>
<p>release(core): 1.6.11 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41260">#41260</a>)
feat(core): accept address inside LangChainTracer (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41006">#41006</a>)</p>
<h2>langchain-core==1.6.10</h2>
<p>Changes since langchain-core==1.6.9</p>
<p>release(core): 1.6.10 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41220">#41220</a>)
feat(core,anthropic,openai): declare mid-conversation support in model
profiles (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41180">#41180</a>)</p>
<h2>langchain-core==1.6.9</h2>
<p>Changes since langchain-core==1.6.8</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/6419339aa163a49f32d9b80ff20d6adc3b446d49"><code>6419339</code></a>
release(core): version 1.7.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41548">#41548</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3bc70b9daa0d17f1438ab408df5e9f2a8ba1e56e"><code>3bc70b9</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41543">#41543</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/facf2788ae1f60c62f2397d15698237c405a5f05"><code>facf278</code></a>
fix(core): preserve gateway tracing metadata in v3 streams (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41504">#41504</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/6eb169b56cea0b535b9af0ca504bb367f6407455"><code>6eb169b</code></a>
release(anthropic): 1.7.7 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41538">#41538</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/0808e17e29b59ee1cc45a5ce2aa4cb4006671c1c"><code>0808e17</code></a>
fix(anthropic): trace resolved reasoning effort (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41536">#41536</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/4b80abeadd6faf7f321ac5d690602812f82a3baf"><code>4b80abe</code></a>
fix(core): escape node metadata in Mermaid labels (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41512">#41512</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/abbc0c30b9a3396a748add260284c27c8f5fb09f"><code>abbc0c3</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41520">#41520</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/575db57ab3658e38ba1d8c40c2bf62110c8a0c27"><code>575db57</code></a>
test(openai): use current lightweight model in integration tests (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41509">#41509</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/0d29456babaa707357523ea20b27bd8858fb4766"><code>0d29456</code></a>
release(openai): 1.7.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41506">#41506</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/8635846e620acc59ca0273944eaef2f4889452a3"><code>8635846</code></a>
chore(model-profiles): refresh openai model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/41505">#41505</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-core==1.6.5...langchain-core==1.7.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `pytest` from 9.1.1 to 9.2.0
<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.2.0</h2>
<h1>pytest 9.2.0 (2026-10-27)</h1>
<h2>Removals and backward incompatible breaking changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14716">#14716</a>:
<code>-c</code> now raises a usage error for a path that does not exist,
is a directory, or is in a format pytest has no loader for. Such paths
were previously either ignored or reported as an unhandled
<code>FileNotFoundError</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/8593">#8593</a>:
The <code>indirect</code>, <code>ids</code> and <code>scope</code>
arguments of <code>Metafunc.parametrize()
&lt;pytest.Metafunc.parametrize&gt;</code> -- and therefore of
<code>@pytest.mark.parametrize &lt;pytest.mark.parametrize
ref&gt;</code> -- are now keyword-only.</p>
<p>Passing them positionally was a common source of confusing errors,
e.g. <code>parametrize(&quot;arg1&quot;, &quot;arg2&quot;, [(1,
2)])</code> made the argvalues land in <code>indirect</code> and failed
with <code>indirect fixture '(1, 2)' doesn't exist</code>; such mistakes
now fail with an understandable <code>TypeError</code>. The type
annotations have always declared these arguments as keyword-only.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/993">#993</a>:
Plugin import failures at startup are now reported and classified
instead of escaping as a raw traceback with the accidental exit code
<code>1</code>:</p>
<ul>
<li>a plugin that cannot be found -- via <code>-p</code>,
<code>pytest_plugins</code> or <code>PYTEST_PLUGINS</code> -- exits with
<code>pytest.ExitCode</code> <code>USAGE_ERROR</code> (<code>4</code>),
like a <code>conftest.py</code> that fails to import;</li>
<li>a plugin that is found but raises while importing -- including a
broken <code>pytest11</code> entry point or a missing plugin dependency
-- exits with <code>pytest.ExitCode</code> <code>INTERNAL_ERROR</code>
(<code>3</code>), with the traceback preserved.</li>
</ul>
<p><code>pytest.main</code> now returns these codes instead of raising
<code>ImportError</code>. Also fixed an <code>IndexError</code> from
pytest's own internals when the plugin raised an exception with no
arguments.</p>
</li>
</ul>
<h2>Deprecations (removal in next major release)</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14742">#14742</a>:
The private <code>_pytest.python.CallSpec2</code> name has been renamed
to <code>CallSpec</code>.
The old <code>CallSpec2</code> alias remains available for now and will
be removed in pytest 10 or 11.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14755">#14755</a>:
Schedule <em>yield_fixture</em> decorator for removal in pytest 10 or
11.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14774">#14774</a>:
Similarly to the deprecation of class-scoped fixtures defined as
instance methods in pytest 9.1,
<em>all</em> higher-scoped fixtures (<code>module</code>,
<code>package</code>, and <code>session</code>) defined
as instance methods are now deprecated.</p>
<p>They suffer from the same issue the original deprecation only fixed
for
class-scoped fixtures, see <code>10819</code> and
<code>14011</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14808">#14808</a>:
Passing a non-string value to a <code>string</code>-typed ini option now
emits a <code>pytest.PytestRemovedIn10Warning</code>. This affects
<code>[tool.pytest.ini_options]</code> in <code>pyproject.toml</code>.
In pytest 10 this will raise a <code>TypeError</code>, matching the
behavior of the corresponding TOML config path.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/7323">#7323</a>:
Passing <code>bytes</code> to <code>TerminalReporter.write_line()</code>
is now deprecated, pass a <code>str</code> instead.</p>
</li>
</ul>
<h2>New features</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14743">#14743</a>:
<!-- raw HTML omitted -->Full diff:<!-- raw HTML omitted --> now
contains a legend explaining the <!-- raw HTML omitted -->+<!-- raw HTML
omitted --> &amp; <!-- raw HTML omitted -->-<!-- raw HTML omitted -->
symbols in diff output.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/2781">#2781</a>:
When <code>strict_markers</code> is enabled, marker names used in
<code>-m</code> expressions are now validated against registered
markers.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/6881">#6881</a>:
Added the <code>parametrize_long_str_id_strategy</code> configuration
option to control how long <code>str</code>/<code>bytes</code> parameter
values are handled in auto-generated test IDs. Explicit IDs set via
<code>ids=[...]</code> or <code>pytest.param(..., id=...)</code> are not
affected. Available strategies: <code>short</code> (default, values over
100 chars fall back to <code>argname+index</code>), <code>sha256</code>
(SHA-256 hex digest), <code>legacy</code> (keep full value), and
<code>disallow</code> (error requesting explicit IDs).</li>
</ul>
<h2>Improvements in existing functionality</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/10745">#10745</a>:
A <code>RecursionError</code> whose recursion origin cannot be located
now shows only the
first and last 10 stack frames, matching what pytest already did when
locating
the origin failed outright. Previously the full traceback -- often
around a
thousand near-identical frames -- was rendered, which was both slow and
unreadable. Pass <code>--full-trace</code> to see every frame.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ae1d0ff57b8bd4252da9ade7353523ac0ac73281"><code>ae1d0ff</code></a>
Prepare release version 9.2.0</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/143ce31dea9b12ff2ab6add04361ee01ab114931"><code>143ce31</code></a>
pre-commit: exclude codespell on generated release announcements</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/532dc5fc78f57de5115c5ecc9c6c29257787ada7"><code>532dc5f</code></a>
tox: add attrs dependency to regen, needed in reportingdemo.rst</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7b2446ee41a7298d3a0bc22c52f3b8bacae1566"><code>a7b2446</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15245">#15245</a>
from pytest-dev/claude/project-thread-9jcf91</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/679d09c6945156746e17e9ec0b1c8309bbde3ad3"><code>679d09c</code></a>
[pre-commit.ci] pre-commit autoupdate (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/15266">#15266</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/41d12cdbb4a35a7877768d93d8dc50a9117a8d8c"><code>41d12cd</code></a>
docs: clarify typing for yield fixtures</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/b9022ba04a33bfc76b5c9aefb8974755a80bf5ce"><code>b9022ba</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15182">#15182</a>
from 1871698/docs/improve-getting-started-description</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/16f87a7f364a7362fbfbb204c79a47ad567b4fbd"><code>16f87a7</code></a>
docs: improve getting-started.rst test execution description</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/082ed70e314ee8e1ab117763d17fe801648f7508"><code>082ed70</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15255">#15255</a>
from pytest-dev/claude/project-thread-eoi3d6</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ae37a013763e9c4a143245eda1acdc3c27961685"><code>ae37a01</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/15227">#15227</a>
from pytest-dev/claude/project-thread-4etye4</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/9.1.1...9.2.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `pytest-asyncio` from 1.4.0 to 1.4.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest-asyncio/releases">pytest-asyncio's
releases</a>.</em></p>
<blockquote>
<h2>pytest-asyncio v1.4.1</h2>
<h1><a
href="https://github.com/pytest-dev/pytest-asyncio/tree/1.4.1">1.4.1</a>
- 2026-10-25</h1>
<h2>Added</h2>
<ul>
<li>Declared support for Python 3.15 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1588">#1588</a>)</li>
</ul>
<h2>Fixed</h2>
<ul>
<li>Allow <em>AsyncIterable</em> as a return type for asynchronous
generator fixtures decorated with <em>pytest_asyncio.fixture</em> (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1090">#1090</a>)</li>
</ul>
<h2>Notes for Downstream Packagers</h2>
<ul>
<li>Source-tree development dependencies now use PEP 735 dependency
groups and <code>uv.lock</code>. (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1518">#1518</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/6af7f5c9a8c77fe5040b9d368b9ed01837c25652"><code>6af7f5c</code></a>
chore: Prepare release of v1.4.1.</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/32ccf64ec115e42543bddb8cd142b2026b0cab77"><code>32ccf64</code></a>
Support 3.15 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1588">#1588</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/983007120dcc53b14f8f7e9df492cbd1d20a242c"><code>9830071</code></a>
Build(deps): Bump certifi from 2026.6.17 to 2026.10.19 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1590">#1590</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/0238023e439113044a9ed095307884c774ec9e46"><code>0238023</code></a>
Build(deps): Bump virtualenv from 21.7.13 to 21.14.13 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1589">#1589</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/93893ecd9447d105ebd56419bc5162aa2420587e"><code>93893ec</code></a>
Build(deps): Bump astral-sh/setup-uv from 10.2.0 to 10.3.0 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1583">#1583</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/f287e5e0f5425380b24c32b64037dfb2ed3cb937"><code>f287e5e</code></a>
Build(deps): Bump actions/download-artifact from 8.0.1 to 8.0.2 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1581">#1581</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/b71da4fe09265e1ad86d4cbfc452b590ebfc4daa"><code>b71da4f</code></a>
Build(deps-dev): Bump hypothesis from 6.168.3 to 6.168.5 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1582">#1582</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/d8c51d8990a4bbdf8a19ff0cf80d68b95b11dea8"><code>d8c51d8</code></a>
Build(deps): Bump actions/upload-artifact from 7.0.1 to 7.0.2 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1580">#1580</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/b12808bd8d061b583ccdddc53ad6c85198509eed"><code>b12808b</code></a>
Build(deps): Bump virtualenv from 21.7.12 to 21.7.13 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1578">#1578</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-asyncio/commit/e0601f0ce9532de0b5453a5cfd08f25b258a93f5"><code>e0601f0</code></a>
Build(deps-dev): Bump coverage from 7.16.1 to 7.16.2 (<a
href="https://redirect.github.com/pytest-dev/pytest-asyncio/issues/1577">#1577</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest-asyncio/compare/v1.4.0...v1.4.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `pytest-mock` from 3.16.0 to 3.17.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest-mock/releases">pytest-mock's
releases</a>.</em></p>
<blockquote>
<h2>v3.17.0</h2>
<p><em>2026-10-17</em></p>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/641">#641</a>:
Failures of mock assertions such as <code>assert_called_with</code> are
again reported only once, instead of two or three times with the
original error from the <code>mock</code> module chained before
them.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/642">#642</a>:
Mock assertion failures no longer show misleading introspection when
they are not about the arguments of the last call:
<code>assert_called</code>, <code>assert_called_once</code>,
<code>assert_not_called</code>, <code>assert_any_call</code>, their
<code>await</code> counterparts, and
<code>assert_called_once_with</code>/<code>assert_awaited_once_with</code>
when the mock was not called (awaited) exactly once. The introspection
also no longer reports arguments passed by position versus by keyword as
different for autospecced mocks, and no longer shows empty
<code>Args:</code>/<code>Kwargs:</code> sections when assertion
rewriting is not available.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/643">#643</a>:
<code>assert_has_calls</code> and <code>assert_has_awaits</code>
failures no longer show pytest introspection, which compared the
expected and actual calls position by position and so reported
differences between unrelated calls. The mock's own message, listing the
expected and actual calls, is still shown.</li>
</ul>
<h2>v3.16.1</h2>
<p><em>2026-10-17</em></p>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/617">#617</a>:
Fixed <code>assert_has_calls</code> raising <code>ValueError</code>
instead of <code>AssertionError</code> (or showing misleading
introspection) when the expected calls are plain tuples rather than
<code>call(...)</code> objects, such as <code>(args, kwargs)</code>
entries of another mock's <code>call_args_list</code>,
<code>(args,)</code>, <code>(kwargs,)</code>, <code>(name, args)</code>
or <code>(name, kwargs)</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/621">#621</a>:
Fixed <code>mocker.stop()</code> sometimes stopping the wrong mock, for
example with mocks from <code>mocker.create_autospec</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/621">#621</a>:
<code>mocker.stop()</code> now undoes the most recent patch first when
the same object is used in several patches.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/623">#623</a>:
Fixed misleading introspection in <code>assert_has_awaits</code>
failures, and
<code>assert_has_calls</code>/<code>assert_has_awaits</code> raising
<code>IndexError</code> instead of <code>AssertionError</code> when
<code>calls</code> is passed by keyword.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/624">#624</a>:
Clear <code>spy_return_iter</code> after a spied synchronous or
asynchronous function raises, matching <code>spy_return</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/627">#627</a>:
Document <code>async_stub</code> await assertions and the distinction
between call and await tracking.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/629">#629</a>:
Fixed <code>mocker.resetall()</code> skipping the mocks patched with
<code>mocker.patch.multiple()</code>, both the ones it creates and the
ones passed explicitly.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/630">#630</a>:
<code>mocker.resetall()</code> now also resets <code>spy_return</code>
and <code>spy_exception</code> of spies, like it already did for
<code>spy_return_list</code> and <code>spy_return_iter</code>, and no
longer overwrites attributes with these names on mocks that are not
spies.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest-mock/pull/633">#633</a>:
Fixed spies on <code>classmethod</code>, <code>staticmethod</code>,
builtins and classes returning a <code>MagicMock</code> instead of
calling the spied code after
<code>mocker.resetall(side_effect=True)</code>. Resetting a spy's
<code>side_effect</code> now goes back to calling the spied code, also
clearing any <code>side_effect</code> set on the spy.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst">pytest-mock's
changelog</a>.</em></p>
<blockquote>
<h2>3.17.0</h2>
<p><em>2026-10-17</em></p>
<ul>
<li><code>[#641](https://github.com/pytest-dev/pytest-mock/issues/641)
&lt;https://github.com/pytest-dev/pytest-mock/pull/641&gt;</code>_:
Failures of mock assertions such as <code>assert_called_with</code> are
again reported only once, instead of two or three times with the
original error from the <code>mock</code> module chained before
them.</li>
<li><code>[#642](https://github.com/pytest-dev/pytest-mock/issues/642)
&lt;https://github.com/pytest-dev/pytest-mock/pull/642&gt;</code>_: Mock
assertion failures no longer show misleading introspection when they are
not about the arguments of the last call: <code>assert_called</code>,
<code>assert_called_once</code>, <code>assert_not_called</code>,
<code>assert_any_call</code>, their <code>await</code> counterparts, and
<code>assert_called_once_with</code>/<code>assert_awaited_once_with</code>
when the mock was not called (awaited) exactly once. The introspection
also no longer reports arguments passed by position versus by keyword as
different for autospecced mocks, and no longer shows empty
<code>Args:</code>/<code>Kwargs:</code> sections when assertion
rewriting is not available.</li>
<li><code>[#643](https://github.com/pytest-dev/pytest-mock/issues/643)
&lt;https://github.com/pytest-dev/pytest-mock/pull/643&gt;</code>_:
<code>assert_has_calls</code> and <code>assert_has_awaits</code>
failures no longer show pytest introspection, which compared the
expected and actual calls position by position and so reported
differences between unrelated calls. The mock's own message, listing the
expected and actual calls, is still shown.</li>
</ul>
<h2>3.16.1</h2>
<p><em>2026-10-17</em></p>
<ul>
<li><code>[#617](https://github.com/pytest-dev/pytest-mock/issues/617)
&lt;https://github.com/pytest-dev/pytest-mock/pull/617&gt;</code>_:
Fixed <code>assert_has_calls</code> raising <code>ValueError</code>
instead of <code>AssertionError</code> (or showing misleading
introspection) when the expected calls are plain tuples rather than
<code>call(...)</code> objects, such as <code>(args, kwargs)</code>
entries of another mock's <code>call_args_list</code>,
<code>(args,)</code>, <code>(kwargs,)</code>, <code>(name, args)</code>
or <code>(name, kwargs)</code>.</li>
<li><code>[#621](https://github.com/pytest-dev/pytest-mock/issues/621)
&lt;https://github.com/pytest-dev/pytest-mock/pull/621&gt;</code>_:
Fixed <code>mocker.stop()</code> sometimes stopping the wrong mock, for
example with mocks from <code>mocker.create_autospec</code>.</li>
<li><code>[#621](https://github.com/pytest-dev/pytest-mock/issues/621)
&lt;https://github.com/pytest-dev/pytest-mock/pull/621&gt;</code>_:
<code>mocker.stop()</code> now undoes the most recent patch first when
the same object is used in several patches.</li>
<li><code>[#623](https://github.com/pytest-dev/pytest-mock/issues/623)
&lt;https://github.com/pytest-dev/pytest-mock/pull/623&gt;</code>_:
Fixed misleading introspection in <code>assert_has_awaits</code>
failures, and
<code>assert_has_calls</code>/<code>assert_has_awaits</code> raising
<code>IndexError</code> instead of <code>AssertionError</code> when
<code>calls</code> is passed by keyword.</li>
<li><code>[#624](https://github.com/pytest-dev/pytest-mock/issues/624)
&lt;https://github.com/pytest-dev/pytest-mock/pull/624&gt;</code>_:
Clear <code>spy_return_iter</code> after a spied synchronous or
asynchronous function raises, matching <code>spy_return</code>.</li>
<li><code>[#627](https://github.com/pytest-dev/pytest-mock/issues/627)
&lt;https://github.com/pytest-dev/pytest-mock/pull/627&gt;</code>_:
Document <code>async_stub</code> await assertions and the distinction
between call and await tracking.</li>
<li><code>[#629](https://github.com/pytest-dev/pytest-mock/issues/629)
&lt;https://github.com/pytest-dev/pytest-mock/pull/629&gt;</code>_:
Fixed <code>mocker.resetall()</code> skipping the mocks patched with
<code>mocker.patch.multiple()</code>, both the ones it creates and the
ones passed explicitly.</li>
<li><code>[#630](https://github.com/pytest-dev/pytest-mock/issues/630)
&lt;https://github.com/pytest-dev/pytest-mock/pull/630&gt;</code>_:
<code>mocker.resetall()</code> now also resets <code>spy_return</code>
and <code>spy_exception</code> of spies, like it already did for
<code>spy_return_list</code> and <code>spy_return_iter</code>, and no
longer overwrites attributes with these names on mocks that are not
spies.</li>
<li><code>[#633](https://github.com/pytest-dev/pytest-mock/issues/633)
&lt;https://github.com/pytest-dev/pytest-mock/pull/633&gt;</code>_:
Fixed spies on <code>classmethod</code>, <code>staticmethod</code>,
builtins and classes returning a <code>MagicMock</code> instead of
calling the spied code after
<code>mocker.resetall(side_effect=True)</code>. Resetting a spy's
<code>side_effect</code> now goes back to calling the spied code, also
clearing any <code>side_effect</code> set on the spy.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/5c2f207ca7098dde996f9bedd06e596e39b6246b"><code>5c2f207</code></a>
Release 3.17.0</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/96252715fad4a0dfec1997103cc231bc1f133616"><code>9625271</code></a>
Bump the minor version for releases that change existing behavior</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/34b87aeeef1d2a85be5ce4fadb0c6bc4a033c40a"><code>34b87ae</code></a>
Drop introspection from assert_has_calls and assert_has_awaits (<a
href="https://redirect.github.com/pytest-dev/pytest-mock/issues/643">#643</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/866520f2d339c7649af56ac227ca67ed36b1e92e"><code>866520f</code></a>
Only show introspection for failures about the call arguments (<a
href="https://redirect.github.com/pytest-dev/pytest-mock/issues/642">#642</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/5ab41c143c57842456caad0029082e919fa6a95d"><code>5ab41c1</code></a>
Do not chain the original mock error to wrapped assertion failures (<a
href="https://redirect.github.com/pytest-dev/pytest-mock/issues/641">#641</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/1916b14db224b893edf18251f26a38487eb0d6a6"><code>1916b14</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest-mock/issues/635">#635</a>
from nicoddemus/docs-contributing-resetall</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/212fcd903aeb685a3b39875be82fc84ff58ebc56"><code>212fcd9</code></a>
Document resetall, the changelog format and running tests with tox</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/9374b2c3e38e3bbc2e04328f7026ae3680f0476f"><code>9374b2c</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest-mock/issues/634">#634</a>
from pytest-dev/release-3.16.1</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/fab730daf4a4cfc260d8656b0467503131535a00"><code>fab730d</code></a>
Release 3.16.1</li>
<li><a
href="https://github.com/pytest-dev/pytest-mock/commit/d26d41bb01e99de5c0b59156c667d46985b03470"><code>d26d41b</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest-mock/issues/627">#627</a>
from xiehuanyi/docs/async-stub-awaits-20261012-r2</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest-mock/compare/v3.16.0...v3.17.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `ruff` from 0.16.9 to 0.17.3
<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.17.3</h2>
<h2>Release Notes</h2>
<p>Released on 2026-10-26.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>suspicious</code>] Add <code>shared-clock-mock</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29074">#29074</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>pyupgrade</code>] Avoid incorrect conversions to f-strings
(<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25485">#25485</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Buffer stdout in <code>ruff format</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29631">#29631</a>)</li>
<li>Specialize ASCII identifier lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29503">#29503</a>)</li>
<li>Speed up identifier and keyword lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29463">#29463</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>flake8-bandit</code>] Describe what the rule actually checks
for <code>httpx</code> (<code>S113</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/27936">#27936</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/itsvedantkumar"><code>@​itsvedantkumar</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
</ul>
<h2>Install ruff 0.17.3</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.17.3/ruff-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/ruff/releases/download/0.17.3/ruff-installer.ps1
| iex&quot;
</code></pre>
<h2>Download ruff 0.17.3</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/ruff/releases/download/0.17.3/ruff-aarch64-apple-darwin.tar.gz">ruff-aarch64-apple-darwin.tar.gz</a></td>
<td>Apple Silicon macOS</td>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-aarch64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-x86_64-apple-darwin.tar.gz">ruff-x86_64-apple-darwin.tar.gz</a></td>
<td>Intel macOS</td>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-x86_64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-aarch64-pc-windows-msvc.zip">ruff-aarch64-pc-windows-msvc.zip</a></td>
<td>ARM64 Windows</td>
<td><a
href="https://releases.astral.sh/github/ruff/releases/download/0.17.3/ruff-aarch64-pc-windows-msvc.zip.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/ruff/blob/main/CHANGELOG.md">ruff's
changelog</a>.</em></p>
<blockquote>
<h2>0.17.3</h2>
<p>Released on 2026-10-26.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>suspicious</code>] Add <code>shared-clock-mock</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29074">#29074</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>pyupgrade</code>] Avoid incorrect conversions to f-strings
(<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25485">#25485</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Buffer stdout in <code>ruff format</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29631">#29631</a>)</li>
<li>Specialize ASCII identifier lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29503">#29503</a>)</li>
<li>Speed up identifier and keyword lexing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29463">#29463</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>flake8-bandit</code>] Describe what the rule actually checks
for <code>httpx</code> (<code>S113</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/27936">#27936</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/itsvedantkumar"><code>@​itsvedantkumar</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
</ul>
<h2>0.17.2</h2>
<p>Released on 2026-10-22.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>complexity</code>] Add
<code>unnecessary-regular-expression-compile</code> (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25533">#25533</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Avoid syntax errors for type variables with starred constraints
(<code>UP040</code>, <code>UP046</code>, <code>UP047</code>,
<code>RUF053</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/28362">#28362</a>)</li>
<li>[<code>pydocstyle</code>] Treat backslash-escaped <code>*args</code>
and <code>**kwargs</code> as documented (<code>D417</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29536">#29536</a>)</li>
<li>[<code>pylint</code>] Avoid false positive on starred elements
(<code>PLE0643</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29483">#29483</a>)</li>
<li>[<code>pylint</code>] Mark fixes unsafe when deleting comments
(<code>PLR5501</code>, <code>PLW0120</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29414">#29414</a>)</li>
<li>[<code>pyupgrade</code>] Mark fix unsafe when deleting comments
(<code>UP035</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29413">#29413</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Avoid intermediate names in lint name resolution (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29506">#29506</a>)</li>
<li>Prefilter bidirectional Unicode checks by UTF-8 leading bytes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29561">#29561</a>)</li>
<li>Share postfix parsing and reduce expression copies (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29480">#29480</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/ba65abf8ba7bfbcc059f7fb9712be5ba9cab6abc"><code>ba65abf</code></a>
Bump version to 0.17.3 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29745">#29745</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/fb2f9218793e3b0e979175d43d88dcac791e92d6"><code>fb2f921</code></a>
[ty] Fix narrowing of strings and integers compared with enum members
(<a
href="https://redirect.github.com/astral-sh/ruff/issues/29743">#29743</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/d1812878d5f81af6f5810d193ebc44413d1b21cc"><code>d181287</code></a>
[ty] Index nominal union alternatives (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29196">#29196</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/07872adbafa7c00f42f1174d8aa3c3e324033009"><code>07872ad</code></a>
[ty] Preserve string literals in comparisons with
<code>LiteralString</code> (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29741">#29741</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/95888b2edfaa34d0159732856b1466cf53b33e64"><code>95888b2</code></a>
[ty] Preserve narrowing after guarded match subject captures (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29558">#29558</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/3c8166b683805aa89557b0739f5b9918ad58ea6d"><code>3c8166b</code></a>
[ty] Fix hang when negating recursive unions of instance types (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29652">#29652</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/3fd20e08598ac345b8afd524d3f7086456e4e758"><code>3fd20e0</code></a>
[ty] Preserve generic callback specializations during return refinement
(<a
href="https://redirect.github.com/astral-sh/ruff/issues/29667">#29667</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/2f0a29ece1f1a539c65c0671c04c76bb886219fe"><code>2f0a29e</code></a>
[ty] Record predicate truthiness alongside expression inference (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29706">#29706</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/60f104a1a2ec4e980198ff54c38ba6fe7045b29f"><code>60f104a</code></a>
[ty] Honor <a
href="https://github.com/override"><code>@​override</code></a> on
overload implementations in stubs (<a
href="https://redirect.github.com/astral-sh/ruff/issues/29470">#29470</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/207ec3affeb118fce03a65f9333959157e228312"><code>207ec3a</code></a>
[ty] Add diagnostic baseline support (<a
href="https://redirect.github.com/astral-sh/ruff/issues/27636">#27636</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.16.9...0.17.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `ty` from 0.0.84 to 0.0.94
<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.94</h2>
<h2>Release Notes</h2>
<p>Released on 2026-10-28.</p>
<h3>Bug fixes</h3>
<ul>
<li>Correctly handle line continuation when fixing unused ignore
comments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29781">#29781</a>)</li>
<li>Fix Linux x86-64 startup on older kernels (<a
href="https://redirect.github.com/astral-sh/ty/pull/4853">#4853</a>)</li>
<li>Fix a panic when a comprehension filter depends on a later binding
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/29786">#29786</a>)</li>
<li>Fix a panic when sorting diagnostics without a source location (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29780">#29780</a>)</li>
<li>Fix panic on a chained assignment with a missing value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29787">#29787</a>)</li>
<li>Reject defaulted TypeVars after a TypeVarTuple in legacy generic
classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29753">#29753</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Django: suppress invalid-override errors for <code>Meta</code>
configurations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29769">#29769</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/sharkdp"><code>@​sharkdp</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
</ul>
<h2>Install ty 0.0.94</h2>
<h3>Install prebuilt binaries via shell script</h3>
<pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf
https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-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/ty/releases/download/0.0.94/ty-installer.ps1
| iex&quot;
</code></pre>
<h2>Download ty 0.0.94</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/ty/releases/download/0.0.94/ty-aarch64-apple-darwin.tar.gz">ty-aarch64-apple-darwin.tar.gz</a></td>
<td>Apple Silicon macOS</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-apple-darwin.tar.gz">ty-x86_64-apple-darwin.tar.gz</a></td>
<td>Intel macOS</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-pc-windows-msvc.zip">ty-aarch64-pc-windows-msvc.zip</a></td>
<td>ARM64 Windows</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-pc-windows-msvc.zip">ty-i686-pc-windows-msvc.zip</a></td>
<td>x86 Windows</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-pc-windows-msvc.zip">ty-x86_64-pc-windows-msvc.zip</a></td>
<td>x64 Windows</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-x86_64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-unknown-linux-gnu.tar.gz">ty-aarch64-unknown-linux-gnu.tar.gz</a></td>
<td>ARM64 Linux</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-aarch64-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-unknown-linux-gnu.tar.gz">ty-i686-unknown-linux-gnu.tar.gz</a></td>
<td>x86 Linux</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-i686-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-powerpc64le-unknown-linux-gnu.tar.gz">ty-powerpc64le-unknown-linux-gnu.tar.gz</a></td>
<td>PPC64LE Linux</td>
<td><a
href="https://releases.astral.sh/github/ty/releases/download/0.0.94/ty-powerpc64le-unknown-linux-gnu.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/ty/blob/main/CHANGELOG.md">ty's
changelog</a>.</em></p>
<blockquote>
<h2>0.0.94</h2>
<p>Released on 2026-10-28.</p>
<h3>Bug fixes</h3>
<ul>
<li>Correctly handle line continuation when fixing unused ignore
comments (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29781">#29781</a>)</li>
<li>Fix Linux x86-64 startup on older kernels (<a
href="https://redirect.github.com/astral-sh/ty/pull/4853">#4853</a>)</li>
<li>Fix a panic when a comprehension filter depends on a later binding
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/29786">#29786</a>)</li>
<li>Fix a panic when sorting diagnostics without a source location (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29780">#29780</a>)</li>
<li>Fix panic on a chained assignment with a missing value (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29787">#29787</a>)</li>
<li>Reject defaulted TypeVars after a TypeVarTuple in legacy generic
classes (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29753">#29753</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Django: suppress invalid-override errors for <code>Meta</code>
configurations (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29769">#29769</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a href="https://github.com/sharkdp"><code>@​sharkdp</code></a></li>
<li><a
href="https://github.com/charliermarsh"><code>@​charliermarsh</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
</ul>
<h2>0.0.93</h2>
<p>Released on 2026-10-28.</p>
<h3>Preview features</h3>
<ul>
<li>
<p>Add diagnostic baselines to help adopt ty in projects with existing
errors (<a
href="https://redirect.github.com/astral-sh/ruff/pull/27636">#27636</a>).
Configure a baseline in <code>pyproject.toml</code>:</p>
<pre lang="toml"><code>[tool.ty]
baseline = &quot;ty-baseline.json&quot;
</code></pre>
<p>Run <code>ty check --update-baseline</code> to record existing
diagnostics. Subsequent checks filter matching diagnostics from CLI
output and show them as hints in the language server. The baseline
format may change while the feature is in preview.</p>
</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Fix a panic when checking truthiness in a loop (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29759">#29759</a>)</li>
<li>Fix hang when negating recursive unions of instance types (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29652">#29652</a>)</li>
</ul>
<h3>Core type checking</h3>
<ul>
<li>Avoid synthesizing primary keys for abstract Django models (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29744">#29744</a>)</li>
<li>Combine constraints from repeated <code>ParamSpec</code>s (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29172">#29172</a>)</li>
<li>Fix narrowing of strings and integers compared with enum members (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29743">#29743</a>)</li>
<li>Honor <code>@override</code> on overload implementations in stubs
(<a
href="https://redirect.github.com/astral-sh/ruff/pull/29470">#29470</a>)</li>
<li>Preserve caller type variables in nested union inference (<a
href="https://redirect.github.com/astral-sh/ruff/pull/29611">#29611</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/f8ea270e0b125116ce52868b16384819183056af"><code>f8ea270</code></a>
Bump version to 0.0.94 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4857">#4857</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/7a36fc91318b07a58d0aa27753db4db1d212032a"><code>7a36fc9</code></a>
Fix Linux x86-64 startup on older kernels (<a
href="https://redirect.github.com/astral-sh/ty/issues/4853">#4853</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/3db5e4df7becd3ca55fd06c78e98bf7c8a38e469"><code>3db5e4d</code></a>
Bump version to 0.0.93 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4842">#4842</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/f8fd0e8b5ce954b5a7f4e38431d805dd58e50e2d"><code>f8fd0e8</code></a>
Update astral-sh/setup-uv action to v10.3.2 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4843">#4843</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/c1ca420006c7e5ef62ddfa16267fd7531a0a1000"><code>c1ca420</code></a>
Update uv to v0.13.6 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4845">#4845</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/4c5c9079ac053fa3f879ee7625ec5c28a0817de4"><code>4c5c907</code></a>
Update prek dependencies (<a
href="https://redirect.github.com/astral-sh/ty/issues/4844">#4844</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/0445d6497ae22a0c480b23807cd71f47889053d8"><code>0445d64</code></a>
Optimize Linux x86-64 ty releases with LLVM BOLT (<a
href="https://redirect.github.com/astral-sh/ty/issues/4833">#4833</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/3933e6918b731d86b3f3a4f1bc47f2ce21195e8e"><code>3933e69</code></a>
Bump version to 0.0.92 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4821">#4821</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/9aaec9ba5896c759e8e93000d0a1dec51f2bae57"><code>9aaec9b</code></a>
Handle AI policy labels through Actions (<a
href="https://redirect.github.com/astral-sh/ty/issues/4812">#4812</a>)</li>
<li><a
href="https://github.com/astral-sh/ty/commit/e1b8efaafd842d3e33c1a21ae7e897434452975b"><code>e1b8efa</code></a>
Update uv to 0.13.5 (<a
href="https://redirect.github.com/astral-sh/ty/issues/4809">#4809</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ty/compare/0.0.84...0.0.94">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-11-01 16:48:41 -08:00
dependabot[bot] f8812e50a5 chore(deps-dev): bump pycryptodome from 3.23.0 to 4.2.0 in /libs/checkpoint in the major group (#9531)
Bumps the major group in /libs/checkpoint with 1 update:
[pycryptodome](https://github.com/Legrandin/pycryptodome).

Updates `pycryptodome` from 3.23.0 to 4.2.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Legrandin/pycryptodome/releases">pycryptodome's
releases</a>.</em></p>
<blockquote>
<h2>v4.2.0 - Jura</h2>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLKEM</code>, for ML-KEM keys
(FIPS 203).
It is based on the portable C code of
<code>mlkem-native
&lt;https://github.com/pq-code-package/mlkem-native&gt;</code>_
(v2.0.0),
with the Keccak code of PyCryptodome, also compiled for AVX2 and BMI2
(with 4-way SHA-3).
The keys can be exported and imported as PKCS#8 DER or PEM files.</li>
<li>New module <code>Crypto.PublicKey.Hybrid</code>, to model hybrid
keys (post-quantum + traditional).
It supports the hybrid KEM MLKEM768-X25519 (X-Wing), which combines
ML-KEM-768 and X25519.
For now, hybrid keys can only be generated and used with HPKE: they
cannot be exported or imported.</li>
<li><code>Crypto.Protocol.HPKE</code> accepts ML-KEM keys and X-Wing
keys,
for post-quantum HPKE (draft-ietf-hpke-pq), in the Base and PSK
modes.</li>
</ul>
<h2>v4.1.0 - Issy-les-Moulineaux</h2>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLDSA</code>, for ML-DSA keys
(FIPS 204).
It is based on the portable C code of <code>mldsa-native
&lt;https://github.com/pq-code-package/mldsa-native&gt;</code>_
(v2.0.0), with the Keccak code of PyCryptodome. On CPUs with AVX2 and
BMI2, a version compiled for them and with 4-way SHA-3 is used: compared
to the portable version, key generation and verification are 30-45%
faster, signing 15-30%.</li>
<li>New module <code>Crypto.Signature.mldsa</code>, for ML-DSA and
HashML-DSA signatures (FIPS 204), hedged (default) or deterministic,
with an optional context. The new <code>MLDSAMuHash</code> computes the
message representative mu (external mu), to sign or verify a large
message in pieces.</li>
<li>New function <code>digest()</code> in the modules of the SHA-2 and
SHA-3 hashes, to hash a whole message with a single call, like
<code>Crypto.Hash.KangarooTwelve.digest()</code>. For messages up to 1
KiB, it is 1.5x to 3.4x faster than <code>new()</code> followed by
<code>digest()</code>.</li>
<li><code>Crypto.IO.PKCS8.wrap()</code> has a new parameter
<code>public_key</code>, to create a <code>OneAsymmetricKey</code>
version 2 (RFC 5958) with the field <code>publicKey</code>.
<code>Crypto.IO.PKCS8.unwrap()</code> returns an
<code>UnwrappedKey</code>: still a tuple <code>(oid, private_key,
params)</code>,
with the same items as attributes, plus <code>public_key</code> (the
field <code>publicKey</code>, if present).</li>
<li>The scalar Keccak permutation (used by SHA-3, SHAKE, cSHAKE, KMAC,
TupleHash, TurboSHAKE and KangarooTwelve) processes the state one plane
at a time, two rounds per iteration, which needs fewer registers: SHA-3
and SHAKE are 5-10% faster.</li>
</ul>
<h2>Resolved issues</h2>
<ul>
<li><code>Crypto.PublicKey.ECC.import_key()</code> checks that the field
<code>publicKey</code> of a PKCS#8 <code>OneAsymmetricKey</code> version
2 (RFC 5958), if present, matches the private key. Before, it was
ignored.</li>
</ul>
<h2>v4.0.1 - Le Havre</h2>
<h2>New features</h2>
<ul>
<li>The Windows wheels for x86-64 are built with <code>clang-cl</code>
(compatible with MSVC), so the
code for CPUs with AVX2, BMI2 and ADX is also used on Windows: SHA-3 and
the other
Keccak-based functions, KangarooTwelve, ChaCha20, Salsa20, and the big
integers of RSA,
DSA and the NIST curves. To build from source with
<code>clang-cl</code>, set the environment
variable <code>PYCRYPTODOME_CLANG_CL</code> (see the installation
instructions).</li>
</ul>
<h2>v4.0.0 - Grenoble</h2>
<p>Major release, with some breaking changes. See Changelog.</p>
<h2>v3.24.1 - Frejus</h2>
<h2>Resolved issues</h2>
<ul>
<li>Security: the time to create an ECDSA, EdDSA or DSA signature
depended
on the secret nonce, enough to recover the private key with a lattice
attack.
DSA signing is now about 40% slower with GMP. Thanks to Daniel
Bleichenbacher.</li>
</ul>
<h2>v3.24.0 - Evreux</h2>
<p>New features</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst">pycryptodome's
changelog</a>.</em></p>
<blockquote>
<p>4.2.0 (24 October 2026)
++++++++++++++++++++++++++</p>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLKEM</code>, for ML-KEM keys
(FIPS 203).
It is based on the portable C code of
<code>mlkem-native
&lt;https://github.com/pq-code-package/mlkem-native&gt;</code>_
(v2.0.0),
with the Keccak code of PyCryptodome, also compiled for AVX2 and BMI2
(with 4-way SHA-3).
The keys can be exported and imported as PKCS#8 DER or PEM files.</li>
<li>New module <code>Crypto.PublicKey.Hybrid</code>, to model hybrid
keys (post-quantum + traditional).
It supports the hybrid KEM MLKEM768-X25519 (X-Wing), which combines
ML-KEM-768 and X25519.
For now, hybrid keys can only be generated and used with HPKE: they
cannot be exported or imported.</li>
<li><code>Crypto.Protocol.HPKE</code> accepts ML-KEM keys and X-Wing
keys,
for post-quantum HPKE (draft-ietf-hpke-pq), in the Base and PSK
modes.</li>
</ul>
<p>4.1.0 (19 October 2026)
++++++++++++++++++++++++++</p>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLDSA</code>, for ML-DSA keys
(FIPS 204).
It is based on the portable C code of <code>mldsa-native
&lt;https://github.com/pq-code-package/mldsa-native&gt;</code>_
(v2.0.0), with the Keccak code of PyCryptodome. On CPUs with AVX2 and
BMI2, a version
compiled for them and with 4-way SHA-3 is used: compared to the portable
version,
key generation and verification are 30-45% faster, signing 15-30%.</li>
<li>New module <code>Crypto.Signature.mldsa</code>, for ML-DSA and
HashML-DSA signatures (FIPS 204),
hedged (default) or deterministic, with an optional context.
The new <code>MLDSAMuHash</code> computes the message representative mu
(external mu),
to sign or verify a large message in pieces.</li>
<li>New function <code>digest()</code> in the modules of the SHA-2 and
SHA-3 hashes,
to hash a whole message with a single call,
like <code>Crypto.Hash.KangarooTwelve.digest()</code>. For messages up
to 1 KiB,
it is 1.5x to 3.4x faster than <code>new()</code> followed by
<code>digest()</code>.</li>
<li><code>Crypto.IO.PKCS8.wrap()</code> has a new parameter
<code>public_key</code>, to create a <code>OneAsymmetricKey</code>
version 2 (RFC 5958) with the field <code>publicKey</code>.
<code>Crypto.IO.PKCS8.unwrap()</code> returns an
<code>UnwrappedKey</code>: still a tuple <code>(oid, private_key,
params)</code>,
with the same items as attributes, plus <code>public_key</code> (the
field <code>publicKey</code>, if present).</li>
<li>The scalar Keccak permutation (used by SHA-3, SHAKE, cSHAKE, KMAC,
TupleHash, TurboSHAKE
and KangarooTwelve) processes the state one plane at a time, two rounds
per iteration,
which needs fewer registers: SHA-3 and SHAKE are 5-10% faster.</li>
</ul>
<h2>Resolved issues</h2>
<ul>
<li><code>Crypto.PublicKey.ECC.import_key()</code> checks that the field
<code>publicKey</code> of a PKCS#8
<code>OneAsymmetricKey</code> version 2 (RFC 5958), if present, matches
the private key.
Before, it was ignored.</li>
</ul>
<p>4.0.1 (17 October 2026)
++++++++++++++++++++++++++</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/fba5867dc8f46e1100b2b9438e0ca7c09f0d5fa7"><code>fba5867</code></a>
Update Changelog</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/52a8ff62cff5c390091ef30834bc7ae8f1a4f7eb"><code>52a8ff6</code></a>
Fix PyPy wheels on Windows with setuptools 84.2</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/5a6f04770d72ed158b4157847de39b0a93728479"><code>5a6f047</code></a>
Bump version</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/9865be1158315cf7b4d077437b4a90d6c8086b11"><code>9865be1</code></a>
Don't run portable python with cffi</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/9c642f06a8220387a3381d745abdfb2d1e483e61"><code>9c642f0</code></a>
Update Changelog</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/c40f64ba91b54e225740f11de6e3014dacd25d49"><code>c40f64b</code></a>
Merge branch 'mlkem_import_export'</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/e9f74ecfca36e4b4b79ece354388b51d31f2f489"><code>e9f74ec</code></a>
Bump version</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/83f1406ad321158c6f3a7a85e95c21e38f99f68e"><code>83f1406</code></a>
Fix icons</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/00f2277e648334c1e42d41cc7c85cf7d17f14e0a"><code>00f2277</code></a>
Add logo</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/c4b4328c5218f1cf9c5de722b1cefad3db582a30"><code>c4b4328</code></a>
Fix docs</li>
<li>Additional commits viewable in <a
href="https://github.com/Legrandin/pycryptodome/compare/v3.23.0...v4.2.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pycryptodome&package-manager=uv&previous-version=3.23.0&new-version=4.2.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 <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-11-01 16:48:19 -08:00
dependabot[bot] 5608dfb71e chore(deps-dev): bump pycryptodome from 3.23.0 to 4.2.0 in /libs/langgraph in the major group (#9534)
Bumps the major group in /libs/langgraph with 1 update:
[pycryptodome](https://github.com/Legrandin/pycryptodome).

Updates `pycryptodome` from 3.23.0 to 4.2.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Legrandin/pycryptodome/releases">pycryptodome's
releases</a>.</em></p>
<blockquote>
<h2>v4.2.0 - Jura</h2>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLKEM</code>, for ML-KEM keys
(FIPS 203).
It is based on the portable C code of
<code>mlkem-native
&lt;https://github.com/pq-code-package/mlkem-native&gt;</code>_
(v2.0.0),
with the Keccak code of PyCryptodome, also compiled for AVX2 and BMI2
(with 4-way SHA-3).
The keys can be exported and imported as PKCS#8 DER or PEM files.</li>
<li>New module <code>Crypto.PublicKey.Hybrid</code>, to model hybrid
keys (post-quantum + traditional).
It supports the hybrid KEM MLKEM768-X25519 (X-Wing), which combines
ML-KEM-768 and X25519.
For now, hybrid keys can only be generated and used with HPKE: they
cannot be exported or imported.</li>
<li><code>Crypto.Protocol.HPKE</code> accepts ML-KEM keys and X-Wing
keys,
for post-quantum HPKE (draft-ietf-hpke-pq), in the Base and PSK
modes.</li>
</ul>
<h2>v4.1.0 - Issy-les-Moulineaux</h2>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLDSA</code>, for ML-DSA keys
(FIPS 204).
It is based on the portable C code of <code>mldsa-native
&lt;https://github.com/pq-code-package/mldsa-native&gt;</code>_
(v2.0.0), with the Keccak code of PyCryptodome. On CPUs with AVX2 and
BMI2, a version compiled for them and with 4-way SHA-3 is used: compared
to the portable version, key generation and verification are 30-45%
faster, signing 15-30%.</li>
<li>New module <code>Crypto.Signature.mldsa</code>, for ML-DSA and
HashML-DSA signatures (FIPS 204), hedged (default) or deterministic,
with an optional context. The new <code>MLDSAMuHash</code> computes the
message representative mu (external mu), to sign or verify a large
message in pieces.</li>
<li>New function <code>digest()</code> in the modules of the SHA-2 and
SHA-3 hashes, to hash a whole message with a single call, like
<code>Crypto.Hash.KangarooTwelve.digest()</code>. For messages up to 1
KiB, it is 1.5x to 3.4x faster than <code>new()</code> followed by
<code>digest()</code>.</li>
<li><code>Crypto.IO.PKCS8.wrap()</code> has a new parameter
<code>public_key</code>, to create a <code>OneAsymmetricKey</code>
version 2 (RFC 5958) with the field <code>publicKey</code>.
<code>Crypto.IO.PKCS8.unwrap()</code> returns an
<code>UnwrappedKey</code>: still a tuple <code>(oid, private_key,
params)</code>,
with the same items as attributes, plus <code>public_key</code> (the
field <code>publicKey</code>, if present).</li>
<li>The scalar Keccak permutation (used by SHA-3, SHAKE, cSHAKE, KMAC,
TupleHash, TurboSHAKE and KangarooTwelve) processes the state one plane
at a time, two rounds per iteration, which needs fewer registers: SHA-3
and SHAKE are 5-10% faster.</li>
</ul>
<h2>Resolved issues</h2>
<ul>
<li><code>Crypto.PublicKey.ECC.import_key()</code> checks that the field
<code>publicKey</code> of a PKCS#8 <code>OneAsymmetricKey</code> version
2 (RFC 5958), if present, matches the private key. Before, it was
ignored.</li>
</ul>
<h2>v4.0.1 - Le Havre</h2>
<h2>New features</h2>
<ul>
<li>The Windows wheels for x86-64 are built with <code>clang-cl</code>
(compatible with MSVC), so the
code for CPUs with AVX2, BMI2 and ADX is also used on Windows: SHA-3 and
the other
Keccak-based functions, KangarooTwelve, ChaCha20, Salsa20, and the big
integers of RSA,
DSA and the NIST curves. To build from source with
<code>clang-cl</code>, set the environment
variable <code>PYCRYPTODOME_CLANG_CL</code> (see the installation
instructions).</li>
</ul>
<h2>v4.0.0 - Grenoble</h2>
<p>Major release, with some breaking changes. See Changelog.</p>
<h2>v3.24.1 - Frejus</h2>
<h2>Resolved issues</h2>
<ul>
<li>Security: the time to create an ECDSA, EdDSA or DSA signature
depended
on the secret nonce, enough to recover the private key with a lattice
attack.
DSA signing is now about 40% slower with GMP. Thanks to Daniel
Bleichenbacher.</li>
</ul>
<h2>v3.24.0 - Evreux</h2>
<p>New features</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst">pycryptodome's
changelog</a>.</em></p>
<blockquote>
<p>4.2.0 (24 October 2026)
++++++++++++++++++++++++++</p>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLKEM</code>, for ML-KEM keys
(FIPS 203).
It is based on the portable C code of
<code>mlkem-native
&lt;https://github.com/pq-code-package/mlkem-native&gt;</code>_
(v2.0.0),
with the Keccak code of PyCryptodome, also compiled for AVX2 and BMI2
(with 4-way SHA-3).
The keys can be exported and imported as PKCS#8 DER or PEM files.</li>
<li>New module <code>Crypto.PublicKey.Hybrid</code>, to model hybrid
keys (post-quantum + traditional).
It supports the hybrid KEM MLKEM768-X25519 (X-Wing), which combines
ML-KEM-768 and X25519.
For now, hybrid keys can only be generated and used with HPKE: they
cannot be exported or imported.</li>
<li><code>Crypto.Protocol.HPKE</code> accepts ML-KEM keys and X-Wing
keys,
for post-quantum HPKE (draft-ietf-hpke-pq), in the Base and PSK
modes.</li>
</ul>
<p>4.1.0 (19 October 2026)
++++++++++++++++++++++++++</p>
<h2>New features</h2>
<ul>
<li>New module <code>Crypto.PublicKey.MLDSA</code>, for ML-DSA keys
(FIPS 204).
It is based on the portable C code of <code>mldsa-native
&lt;https://github.com/pq-code-package/mldsa-native&gt;</code>_
(v2.0.0), with the Keccak code of PyCryptodome. On CPUs with AVX2 and
BMI2, a version
compiled for them and with 4-way SHA-3 is used: compared to the portable
version,
key generation and verification are 30-45% faster, signing 15-30%.</li>
<li>New module <code>Crypto.Signature.mldsa</code>, for ML-DSA and
HashML-DSA signatures (FIPS 204),
hedged (default) or deterministic, with an optional context.
The new <code>MLDSAMuHash</code> computes the message representative mu
(external mu),
to sign or verify a large message in pieces.</li>
<li>New function <code>digest()</code> in the modules of the SHA-2 and
SHA-3 hashes,
to hash a whole message with a single call,
like <code>Crypto.Hash.KangarooTwelve.digest()</code>. For messages up
to 1 KiB,
it is 1.5x to 3.4x faster than <code>new()</code> followed by
<code>digest()</code>.</li>
<li><code>Crypto.IO.PKCS8.wrap()</code> has a new parameter
<code>public_key</code>, to create a <code>OneAsymmetricKey</code>
version 2 (RFC 5958) with the field <code>publicKey</code>.
<code>Crypto.IO.PKCS8.unwrap()</code> returns an
<code>UnwrappedKey</code>: still a tuple <code>(oid, private_key,
params)</code>,
with the same items as attributes, plus <code>public_key</code> (the
field <code>publicKey</code>, if present).</li>
<li>The scalar Keccak permutation (used by SHA-3, SHAKE, cSHAKE, KMAC,
TupleHash, TurboSHAKE
and KangarooTwelve) processes the state one plane at a time, two rounds
per iteration,
which needs fewer registers: SHA-3 and SHAKE are 5-10% faster.</li>
</ul>
<h2>Resolved issues</h2>
<ul>
<li><code>Crypto.PublicKey.ECC.import_key()</code> checks that the field
<code>publicKey</code> of a PKCS#8
<code>OneAsymmetricKey</code> version 2 (RFC 5958), if present, matches
the private key.
Before, it was ignored.</li>
</ul>
<p>4.0.1 (17 October 2026)
++++++++++++++++++++++++++</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/fba5867dc8f46e1100b2b9438e0ca7c09f0d5fa7"><code>fba5867</code></a>
Update Changelog</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/52a8ff62cff5c390091ef30834bc7ae8f1a4f7eb"><code>52a8ff6</code></a>
Fix PyPy wheels on Windows with setuptools 84.2</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/5a6f04770d72ed158b4157847de39b0a93728479"><code>5a6f047</code></a>
Bump version</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/9865be1158315cf7b4d077437b4a90d6c8086b11"><code>9865be1</code></a>
Don't run portable python with cffi</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/9c642f06a8220387a3381d745abdfb2d1e483e61"><code>9c642f0</code></a>
Update Changelog</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/c40f64ba91b54e225740f11de6e3014dacd25d49"><code>c40f64b</code></a>
Merge branch 'mlkem_import_export'</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/e9f74ecfca36e4b4b79ece354388b51d31f2f489"><code>e9f74ec</code></a>
Bump version</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/83f1406ad321158c6f3a7a85e95c21e38f99f68e"><code>83f1406</code></a>
Fix icons</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/00f2277e648334c1e42d41cc7c85cf7d17f14e0a"><code>00f2277</code></a>
Add logo</li>
<li><a
href="https://github.com/Legrandin/pycryptodome/commit/c4b4328c5218f1cf9c5de722b1cefad3db582a30"><code>c4b4328</code></a>
Fix docs</li>
<li>Additional commits viewable in <a
href="https://github.com/Legrandin/pycryptodome/compare/v3.23.0...v4.2.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pycryptodome&package-manager=uv&previous-version=3.23.0&new-version=4.2.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 <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-11-01 16:47:40 -08:00
dependabot[bot] ab350fd5a4 chore(deps): bump langgraph-sdk from 0.4.5 to 0.4.6 in /libs/cli (#9514)
Bumps [langgraph-sdk](https://github.com/langchain-ai/langgraph) from
0.4.5 to 0.4.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraph/releases">langgraph-sdk's
releases</a>.</em></p>
<blockquote>
<h2>langgraph-sdk==0.4.6</h2>
<p>Changes since sdk==0.4.5</p>
<ul>
<li>release(sdk-py): 0.4.6 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9214">#9214</a>)</li>
<li>fix(sdk-py): percent-encode thread_id and assistant_id in thread
stream requests (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9213">#9213</a>)</li>
<li>release(langgraph): 1.2.13 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9205">#9205</a>)</li>
<li>chore(deps): bump the minor-and-patch group in /libs/sdk-py with 5
updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9149">#9149</a>)</li>
<li>chore(deps): bump urllib3 from 2.7.0 to 2.8.0 in /libs/sdk-py (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9134">#9134</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/ade3f372a5be9e9e9b52db22f128fc501fb72e3d"><code>ade3f37</code></a>
0.4.6</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/bece43dc67024bcd0d4d7649323df3104f56949c"><code>bece43d</code></a>
Fix</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/913b8d5e9547d78523e75304d473555d7347546f"><code>913b8d5</code></a>
Lint</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/3c0d9346c278f628ad1b340f4579eef63e08dda6"><code>3c0d934</code></a>
Add sync test</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/0ebb78d9b202782d65da35e03619c9f65e19d532"><code>0ebb78d</code></a>
Fix</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/70153ceba22013d34b51317cd76c976a62af80cb"><code>70153ce</code></a>
Fix exception handling for imperative tasks</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/a9c87ed8b64efc6152b4b86a014d2f4cd327cae1"><code>a9c87ed</code></a>
prebuilt: release 0.2.1 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/4801">#4801</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/837fe59e243aa51a7ac361c3cb4cc53d8e542c68"><code>837fe59</code></a>
prebuilt: support provider builtin tools in
<code>create_react_agent</code> (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/4800">#4800</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/aff3be03dcd5b8ce0fdebde71997295e87b96f61"><code>aff3be0</code></a>
prebuilt: release 0.2.0 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/4793">#4793</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/10c6ed73205f23cf134833b6f4e56cd4e38de8f0"><code>10c6ed7</code></a>
Code review</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langgraph/compare/0.4.5...0.4.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langgraph-sdk&package-manager=uv&previous-version=0.4.5&new-version=0.4.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-11-01 16:46:09 -08:00
hari-dhanushkodi e5c961d7fb fix(cli): remove outdated deploy private-beta notice (#9506) 2026-10-30 20:26:39 -04:00
Elior Nataf Lackritz 5bd231cbfd release(langgraph): 1.2.19 (#9504)
Bumps langgraph from 1.2.18 to 1.2.19.

Includes:

- List a `ToolNode`'s tool names in its `get_graph()` node metadata
(#9449)
- Apply a node's `trace_policy` to tracers only, so it no longer changes
what streams see (#9486)
- Give `messages` and `tools` events the namespace of the graph they ran
in (#9458)
- Stream messages a node returns from a graph streamed inside another
node (#9463)
- Respect `subgraphs=False` in `RemoteGraph.stream` when the parent
streams subgraphs (#9453)
- Stop reporting a tool paused on `interrupt()` as failed on the `tools`
stream (#9406)
- Return an awaited `@task`'s value while streaming instead of `None`
(#9407)
2026-10-30 18:14:31 -04:00
Elior Nataf Lackritz 3eaf06c67c release(checkpoint-sqlite): 3.1.3 (#9505)
Bumps langgraph-checkpoint-sqlite from 3.1.2 to 3.1.3.

Includes:

- Require `langgraph-checkpoint>=4.3.1`, which rejects NUL bytes in
namespace labels (#9383)

`checkpoint==4.3.1` is already on PyPI.
2026-10-30 18:14:27 -04:00
Elior Nataf Lackritz 6fab323753 release(sdk-py): 0.4.11 (#9503)
Bumps the Python SDK version from 0.4.10 to 0.4.11.

Includes:

- Reset the reconnect cursor on every empty SSE `id`, including a
`Last-Event-ID` the caller passed in (#9440)
- Let `Auth.on()` type-check with no resources or `resources=None`
(#9450)
- Retry a stream reconnect that fails to open (429, 5xx, transport
error) with backoff (#9462)
- Keep SSE data and cursor state across chunks (#9477)
- Retry `runs.wait`, `runs.join` and `runs.cancel(wait=True)` reconnects
with backoff (#9481)
- Warn when a stream reconnects without a `Last-Event-ID` (#9482)
2026-10-30 18:14:22 -04:00
Elior Nataf Lackritz 8d5637b2dd fix(langgraph): keep trace_policy from rewriting streamed node payloads (#9486)
A node's `trace_policy` was applied to every callback handler on the
node's run, not only to tracers. `RunnableSeq` passed the processed
input and output to `on_chain_start` and `on_chain_end`, so LangGraph's
stream handlers received them too. With `process_outputs=omit_payload`,
messages a node returned never reached `stream_mode="messages"`. With
`process_inputs=omit_payload`, messages a node passed through were
emitted again, because the dedupe never saw them. `astream_events`
reported the node's input and output as `{}`.

`RunnableSeq` now takes the `TracePolicy` itself, in place of
`trace_outputs`, and `PregelNode` passes it there instead of through
`trace_inputs` and `trace_outputs`. When the policy changes the payload,
the run's start and end events go out in two dispatches under one run
id: tracers (`BaseTracer` handlers, `LangChainTracer` included) get the
processed payload, and every other handler gets the real one: the stream
handlers behind the `messages` and `tools` stream modes,
`astream_events` and `astream_log`, and any handler a user adds. Tracers
never receive the unprocessed payload, at start, where `LangChainTracer`
posts the run, or at end. Integrations whose handlers are not
`BaseTracer`s, such as Langfuse's or MLflow's, now get the real payload
too, since the policy exists to keep tracer ingest small. As before, a
processor that raises is logged and the raw payload is recorded. Child
runs still attach to the node's run, since both dispatches use the same
run id and children inherit every handler. Tracers get the end first;
langchain-core shields each `on_chain_end` dispatch once it starts, so a
cancellation can only skip the other handlers' end, never the tracers'.
Errors still reach every handler in one dispatch. With no policy, or a
processor that returns the same object it was given (like the
pass-through policy `create_agent` puts on middleware nodes), every
handler gets the payload in one dispatch, as before. `trace_inputs` is
unchanged; the functional API uses it so every handler, `astream_events`
included, sees `@task` arguments by name.

LangGraph now decides what a stream handler is in one place, by
inheritance from `_StreamingCallbackHandler`: the payload split, the
output taps in `RunnableSeq.stream` and `astream`, and Pregel's choice
to run nodes with `astream` all use it. A handler that only defines the
tap methods used to be tapped and saw the node's raw streamed output; it
no longer gets the taps.

Three new tests in `tests/test_trace_policy.py` run a node that calls a
chat model under a policy and a `FakeTracer`, through sync `stream` and
async `astream` with `stream_mode="messages"`, and through
`astream_events`. The two `messages` tests check that the node's own
message is streamed and the passed-through input is not re-emitted; the
`astream_events` test checks that the node's start and end events carry
the real input and output. All three check that the tracer's node run
holds exactly what the policy returned and that the model run is its
child (on Python 3.11+ for the async tests, since on 3.10 a model called
from a sync node on the async path is not traced at all). All three fail
on main. Two more tests check that a custom handler with the tap methods
gets the real payload and none of the node's streamed output while a
tracer next to it gets the processed one, through `ainvoke` and
`astream_events` (this fails on main too), and that a run cancelled as
the node ends still closes the tracer's node run. The existing trace
policy tests pass unchanged.

Thanks @digit50 for the report and the root-cause analysis.

Fixes #9090
2026-10-30 20:58:19 +00:00
Elior Nataf Lackritz 1f0cf816d5 fix(langgraph): give messages and tools events the namespace of their graph (#9458)
Fixes #6373
Fixes #5674

`messages` and `tools` events should stream under the namespace of the
graph they ran in, and a stream without `subgraphs=True` should get only
its own graph's events, plus those of a graph that one of its nodes
streams itself. langchain-ai/langgraphjs#3121 applies these rules in JS.
Python broke them in four places:

- **`@task` calls.** A task call nests its checkpoint namespace segment
under its caller's, although it runs in the caller's graph. The handlers
dropped only the last segment, so a model or tool called in a task
looked like a subgraph's: without `subgraphs=True` its events were
dropped, so a functional API agent streamed no tokens (#6373), and with
it they came under the calling node's namespace, one level deeper per
nested call. The handlers now also drop one segment per enclosing task
call, read from the task's `langgraph_path`. Send tasks and plain nodes
are unchanged.
- **A graph that runs outside the caller's task.** A graph invoked
inside a node with its own `thread_id`, `checkpoint_ns` or
`checkpoint_id`, or through a LangChain runnable called with its own
`configurable` (which replaces the caller's), starts its namespace at
the root, so a handler inherited from the caller streamed its events as
the caller's own, even with `subgraphs=False` (#5674). Such a graph now
drops the `messages` and `tools` handlers it inherits, whatever their
`subgraphs`, so its events stay out of the caller's stream like its
`updates` and `custom` output. A node that streams it still gets its
events.
- **A graph a node streams after running another.** The loop numbers the
namespace of every subgraph a task runs after its first (`node:<id>|1`),
but the handlers read their own namespace before that, so the node got
none of that graph's events. They now read it from the loop.
- **`push_message`.** It kept the pushing node's own segment, ignored
`subgraphs=False`, and reached only the first messages handler, so a
node that streamed a graph never got that graph's pushed messages. It
now follows the same rules, for every messages handler.

Reading the namespace from the loop changes one more case: a root run
given a subgraph's config, with its `checkpoint_ns` (as
`get_state(subgraphs=True)` returns it), took that namespace as its own
and streamed the subgraph's tokens with `subgraphs=False`. The loop runs
it at the root, so it now streams the subgraph node's returned messages,
like any root run.

`stream_events(version="v3")` reads the same namespaces, so a task's
tokens and tool calls and a root node's pushed messages now show in the
run's `messages` and `tool_calls` projections, where they were missing.

The new tests in `test_stream_namespaces.py` fail on `main`, except two
guards that invoke a child graph with no config. No existing test
changed.

Thanks @sdole and @Kannndev for the reports, @lxingy3 for the smaller
repro and diagnosis on #5674, and @roydonsequeira for the review that
caught the `subgraphs=True` case and the 3.10 failure.

## Release note

`messages` and `tools` events from a `@task` now stream without
`subgraphs=True`, as events of the graph that called the task, so a
functional API agent's `messages` stream carries its tokens. With
`subgraphs=True` they move from the calling node's namespace, such as
`("workflow:<id>",)`, to their graph's, `()` at the root. A graph
invoked inside a node with its own `thread_id`, `checkpoint_ns` or
`checkpoint_id`, or through a runnable called with its own
`configurable`, no longer streams into the caller's `messages` or
`tools` stream, even when the caller passes `subgraphs=True`; a node
that streams it still gets its events. A node that streams a graph after
running another one now gets that graph's events. Messages sent with
`push_message` stream under the namespace of their graph, stay out of a
stream without `subgraphs=True` when they come from a subgraph, and
reach a node that streams their graph. A root run given a subgraph's
`checkpoint_ns` with `subgraphs=False` now streams that subgraph node's
returned messages instead of its tokens. The `stream_mode` chunks that
`astream_events` carries change the same way; its own events do not.
2026-10-30 15:07:34 -04:00
Elior Nataf Lackritz 6f2df053f4 test(sdk-py): check that a reconnect loop resumes after the newest event (#9483)
Follow-up to the review on #9482: with `stream_resumable=True`, does a
loop of reconnects replay events? Each reconnect sends the newest event
id the stream got, and a failed reconnect keeps that id for the next
one, so the server replays only what the client missed. The existing
loop tests served the same events whatever the cursor, so nothing
checked this.

`test_*_reconnect_loop_resumes_after_the_newest_event` checks the
`Last-Event-ID` of every request in two loops, sync and async: progress
between drops, and a 502 between two reconnects. A stream that kept its
first cursor, or dropped it after a failed reconnect, fails it.
2026-10-29 15:08:20 -04:00
Elior Nataf Lackritz ffc3336367 fix(sdk-py): retry runs.wait, join and cancel(wait=True) reconnects with backoff (#9481)
`runs.wait`, `runs.join` and `runs.cancel(wait=True)` reconnect to the
run's `Location` when the response is cut off, but with no delay: a 429,
a 5xx or a refused connection on the reconnect raised at once, and a
dropped body reconnected again immediately, so they could not ride out a
server restart or a deploy. #9462 fixed the same for streams.

The reconnect now waits with the stream path's backoff and retries those
failures within the same five attempts; any other status and a failed
first request still raise. Each attempt is one GET with
`reconnect_limit=0`, so the loop owns every retry (the join `Location`
points back at itself, so nothing is lost by not following it).
`request_reconnect` raises plain `httpx.HTTPStatusError`, so it checks
429 and 5xx directly, the same set the stream path catches as
`RateLimitError` and `InternalServerError`.

`test_wait_retries_a_failed_reconnect_after_a_backoff` covers each
failure, sync and async, and fails on `main`; the other two new tests
check the budget and that a 404 still raises. `no_sleep` moves to
`conftest.py` so both test files can use it.
2026-10-28 18:55:31 -04:00
Elior Nataf Lackritz 5f4b28a9f7 fix(sdk-py): warn when a stream reconnects without a Last-Event-ID (#9482)
A dropped stream reconnects even when it has no event id to resume from
(a run created without `stream_resumable=True`, or one that dropped
before its first event). The reconnect can't replay what was sent while
it was disconnected, so those events were lost without a sign.

The SDK now warns when a reconnect request carries no `Last-Event-ID`,
whether the cursor would come from the stream's ids or from the caller's
own header. It still reconnects: everything sent after the reconnect
arrives, so refusing would turn a gap into losing the rest of the
stream.

The new table test covers a stream with no ids (warns; fails on `main`),
one with ids and one with a caller cursor (no warning), sync and async.
The empty-id tests now expect the warning, since they reconnect without
a cursor too.
2026-10-28 18:44:56 -04:00
dependabot[bot] 5c96bce8d3 chore(deps): bump setuptools from 83.0.0 to 84.1.0 in /libs/langgraph (#9433)
Bumps [setuptools](https://github.com/pypa/setuptools) from 83.0.0 to
84.1.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pypa/setuptools/blob/main/NEWS.rst">setuptools's
changelog</a>.</em></p>
<blockquote>
<h1>v84.1.0</h1>
<h2>Features</h2>
<ul>
<li><code>setuptools.archive_util</code> now raises the new
<code>UnsafeMember</code> exception when
an archive member would be extracted outside of the extraction
directory,
where previously such a member was silently skipped. Aborting makes a
malicious or malformed archive visible to the caller instead of yielding
a
quietly incomplete extraction, and matches the behavior of the standard
library's <code>tarfile</code> extraction filters.</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>
<p><code>Wheel.install_as_egg</code> no longer creates namespace package
directories
outside of the destination egg directory. Entries in a wheel's
<code>namespace_packages.txt</code> were split on <code>.</code> and
passed straight to
<code>os.path.join</code>, so an entry that was absolute,
drive-qualified, or UNC
discarded the destination and placed an <code>__init__.py</code> at a
path chosen by
the wheel. Namespace entries are now validated as dotted module names on
every platform -- see GHSA-xmj3-9gf7-h52w.</p>
<p>This code path is reached only by the deprecated
<code>setup_requires</code>
mechanism when <code>setup()</code> is invoked outside a PEP 517
frontend; the
setuptools build backend never reaches it.</p>
</li>
<li>
<p><code>setuptools.archive_util</code> no longer extracts archive
members outside of the
requested extraction directory. Both the tar and zip formats specify
<code>/</code> as
the only path separator, but the guard against <code>..</code>
components split member
names on <code>/</code> alone, so a name such as
<code>..\escaped.txt</code> was treated as a
single component and then resolved as a traversal by the filesystem on
Windows. Member names containing a backslash, a drive letter, or a UNC
prefix
are now rejected on every platform, as are members whose resolved
destination
falls outside the extraction directory -- see GHSA-grgh-hr87-3jpw.</p>
</li>
</ul>
<h1>v84.0.0</h1>
<h2>Features</h2>
<ul>
<li>Newline-separated <code>keywords</code> and
<code>platforms</code><code>pypa/setuptools#4887</code><code>old
specification &lt;https://peps.python.org/pep-0345/&gt;</code>_
separated items with spaces and the current one uses commas. (<a
href="https://redirect.github.com/pypa/setuptools/issues/4887">#4887</a>)</li>
<li><code>Extension</code><code>pypa/distutils#373</code><a
href="https://redirect.github.com/pypa/setuptools/issues/5022">#5022</a>)</li>
<li>The C compiler modules now emit log messages through their own
<code>compilers.C.*</code> loggers instead of the distutils root logger,
part of decoupling the compilers package from distutils. The logger
names are normalized to a stable <code>compilers.C.*</code> prefix so
they remain constant as the package migrates toward a standalone
<code>compilers.C</code> distribution. (<a
href="https://redirect.github.com/pypa/setuptools/issues/5266">#5266</a>)</li>
<li>The C compilers gained a <code>Compiler.call</code> method -- a thin
wrapper over <code>subprocess.check_call</code> (with macOS
deployment-target env injection) that is the modern replacement for
<code>Compiler.spawn</code>. The compilers no longer depend on
<code>distutils.spawn</code>, <code>distutils.dir_util</code>,
<code>distutils.file_util</code>, <code>distutils._modified</code>, or
<code>distutils.util.execute</code>/<code>split_quoted</code>: the
generic <code>newer</code>/<code>newer_group</code> and
<code>split_quoted</code> helpers are vendored into the
<code>compilers</code> package, and
<code>Compiler.mkpath</code>/<code>move_file</code>/<code>execute</code>
are implemented directly on the standard library
(<code>os.makedirs</code>/<code>shutil.move</code>). The methods are
retained for backward compatibility. (<a
href="https://redirect.github.com/pypa/setuptools/issues/5267">#5267</a>)</li>
<li>The compilers no longer depend on <code>distutils.util</code>,
<code>distutils.version</code>, <code>distutils.compat</code>, or
<code>distutils._macos_compat</code>. The platform-identification
helpers
(<code>get_platform</code>/<code>get_host_platform</code>/<code>is_mingw</code>)
now live in <code>distutils.compilers.platform.detect</code> and the
macOS deployment-target logic and <code>compiler_fixup</code> in
<code>distutils.compilers.platform.macos</code>;
<code>CygwinCCompiler.gcc_version</code> returns a
<code>packaging.version.Version</code>. <code>distutils.util</code>
re-exports the platform/macOS helpers from their new homes for backward
compatibility rather than keeping duplicate copies. (sysconfig lookups
still route through distutils pending its own decoupling.) (<a
href="https://redirect.github.com/pypa/setuptools/issues/5268">#5268</a>)</li>
<li>The compilers now read their build configuration from the standard
library's <code>sysconfig</code> instead of
<code>distutils.sysconfig</code>. Per-compiler customization --
previously <code>distutils.sysconfig.customize_compiler</code> -- has
moved into <code>Compiler.configure_system()</code>: a no-op on the base
class, with <code>UnixCCompiler</code> applying the
compiler/flag/archiver settings CPython recorded in
<code>sysconfig</code> (and the usual
<code>CC</code>/<code>CFLAGS</code>/<code>LDSHARED</code>/… environment
overrides). <code>distutils.sysconfig.customize_compiler</code> is
retained as a thin wrapper that calls
<code>compiler.configure_system()</code>. (<a
href="https://redirect.github.com/pypa/setuptools/issues/5269">#5269</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pypa/setuptools/commit/77b7ace0aaf660beeb9b3f9da4aabf21cbcf3fa9"><code>77b7ace</code></a>
Bump version: 84.0.0 → 84.1.0</li>
<li><a
href="https://github.com/pypa/setuptools/commit/e66004c64eaf6cf2f8c53ccdfac75cf7abce7ccf"><code>e66004c</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/setuptools/issues/5329">#5329</a>
from pypa/fix/wheel-namespace-path-escape</li>
<li><a
href="https://github.com/pypa/setuptools/commit/54b5dcadc1bd938a5a66919e8ab7bcd59805e7fd"><code>54b5dca</code></a>
Cover the containment check with a symlink escape test</li>
<li><a
href="https://github.com/pypa/setuptools/commit/6d07b8d17890dda58d944ac51e58f54af92d52af"><code>6d07b8d</code></a>
Validate namespace package names during legacy wheel installation</li>
<li><a
href="https://github.com/pypa/setuptools/commit/8419f006d4e34c4ecfc7a0bd1bc6993855adc3cc"><code>8419f00</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/setuptools/issues/5356">#5356</a>
from pypa/skeleton-ruff-0.16</li>
<li><a
href="https://github.com/pypa/setuptools/commit/6dadc86a3f7dbe1cf49c6301548ffde61d4e74e1"><code>6dadc86</code></a>
Remove DTZ011 suppressions that Ruff 0.17 reports as unused.</li>
<li><a
href="https://github.com/pypa/setuptools/commit/715ec38b46424533806195a96983e3dd70fdf3b9"><code>715ec38</code></a>
Merge <a
href="https://github.com/jaraco/skeleton">https://github.com/jaraco/skeleton</a></li>
<li><a
href="https://github.com/pypa/setuptools/commit/9695460ebfa879ada16545d62b26b9a489a6d5ed"><code>9695460</code></a><code>jaraco/skeleton#219</code></li>
<li><a
href="https://github.com/pypa/setuptools/commit/9e0812976b1154a7aece47188814385c887bc6c9"><code>9e08129</code></a><code>jaraco/skeleton#216</code></li>
<li><a
href="https://github.com/pypa/setuptools/commit/4e612f9e44ecfad130d0b2a27a2dce0740599b4b"><code>4e612f9</code></a>
Ignore PyPy's deprecated check_home warning in tests</li>
<li>Additional commits viewable in <a
href="https://github.com/pypa/setuptools/compare/v83.0.0...v84.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=uv&previous-version=83.0.0&new-version=84.1.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-10-29 03:03:53 +09:00
2f46bb3ca4 fix(sdk-py): preserve SSE data and cursor state across chunks (#9477)
Replaces https://github.com/langchain-ai/langgraph/pull/9451, which
GitHub would not reopen after the branch was rebased. Prior discussion
and review remain on that PR.

Preserve empty data lines and unnamed null events, emit CR-terminated
lines immediately, and retain decoded cursor IDs across reconnects.
Replace malformed UTF-8 consistently with JS, expose decoder state
through its constructor/read-only property, and suppress repeated
control placeholders.

Rebased onto the current reconnect/backoff fixes without changing
request header encoding, retry parsing, JSON-only payloads or tolerant
EOF behavior. Matching JS changes:
https://github.com/langchain-ai/langgraphjs/pull/3128.

### SSE references
- [MDN event stream format and
fields](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format):
UTF-8, comments, colonless fields and multiline data.
- [WHATWG
parsing](https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream):
CR/LF/CRLF framing and buffering.
- [WHATWG
interpretation](https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation):
BOM, data assembly and ID rules.
- [WHATWG
reconnection](https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model):
cursor propagation.

Native Python/JS JSON semantics remain language-specific; this does not
claim exhaustive conformance.

## References
-
[Plan](https://openswe.langchain.dev/agents/86db0ac8-b177-5c04-a1e5-e3226a37b42e/plan)
- [Slack
thread](https://langchain.slack.com/archives/C0C411URFR6/p1792789247110279)

Made by [Open SWE](https://github.com/langchain-ai/open-swe) · [view
thread](https://openswe.langchain.dev/agents/86db0ac8-b177-5c04-a1e5-e3226a37b42e)
· openai:gpt-6.1-sol (medium)

---------

Co-authored-by: Hunter Lovell <hntrl@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-10-28 10:53:22 -07:00
dependabot[bot] 4b7f2f5230 chore(deps): bump zod from 4.3.6 to 4.6.7 in /libs/cli/js-monorepo-example (#9431)
Bumps [zod](https://github.com/colinhacks/zod) from 4.3.6 to 4.6.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.6.7</h2>
<h2>Commits:</h2>
<ul>
<li>b500a2ee95134f81d74da7911d7378dd2853d042 4.6.7</li>
</ul>
<h2>v4.6.6</h2>
<blockquote>
<p>Six malicious commits pushed to <code>main</code> on September 21
with a compromised git credential were reverted the same day in <a
href="https://redirect.github.com/colinhacks/zod/issues/6620">#6620</a>
and have since been removed from the history of <code>main</code>. None
were ever published to npm. The npm provenance for 4.6.6 names 284243fb,
the pre-rewrite copy of 9c219833 with identical contents.</p>
</blockquote>
<h2>Commits:</h2>
<ul>
<li>0cd3af5e9f50ad11bbcd4407a46a21b8848ba574 docs: load the en locale
directly in Zod Mini</li>
<li>9d5e60f162997b764b40d45aa02481888db50dea docs: import locales from
zod/locales, not z.locales</li>
<li>34a626f307871ecbb0123947559328dc52ed5e3e docs: tighten the z.locales
tree-shaking note</li>
<li>74304ce5b5796b5edb9a88ceb8e170b6ea3e82df release: stage npm versions
instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>b2ef983e0711ff6517d6020b021071e2e5653efa docs: add Code for Japan to
bronze sponsors</li>
<li>8fb2faf0584828a6a027d8279db36f6f52784462 docs: crop the Code for
Japan logo and list it first in bronze</li>
<li>bacad518c41610d654658172d325874bf0ee6290 docs: square the Code for
Japan logo to match the other bronze tiles</li>
<li>c42596bd1c87e941d7241d10e012c5c196adcc10 release: hold the run on
the release-approval environment until the staged versions are approved
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6638">#6638</a>)</li>
<li>a2ba4a67986a3e6b2b131be30084a2f460d9ea67 fix(types): declare record
output and input as members, not base type arguments (<a
href="https://redirect.github.com/colinhacks/zod/issues/6645">#6645</a>)</li>
<li>a0ebf970f2d752e5f58527cd2942ff48be4f46bd fix(v4): check array, set
and map size bounds before parsing elements (<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li>529f78dd9431b07f49647fee3aa0819b3f8b66b7 4.6.6</li>
<li>9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1 ci: stage at verbose so a
failed OIDC token exchange says why</li>
</ul>
<h2>v4.6.5</h2>
<h2>Commits:</h2>
<ul>
<li>d2b135cfb7a3582b9eb515756b9166bcb9521f4a docs: add the 4.6.x patch
highlights to the 4.6 post</li>
<li>f1448f7cee00df9fe1e9ad84a000aa1828cc8bc1 docs: fold the 4.6.x patch
highlights into the 4.6 post's own sections</li>
<li>de65a5cb39ed22a507fac935788f718fa88d104f docs: lead the properties
section with the check and add a Zod Mini tab (<a
href="https://redirect.github.com/colinhacks/zod/issues/6598">#6598</a>)</li>
<li>56222cd1532c07bcb91b67df529cab4c0a215330 feat(instanceof): key the
.properties() shape off the instance type (<a
href="https://redirect.github.com/colinhacks/zod/issues/6600">#6600</a>)</li>
<li>ca0229a404818290e6cdcfefcd7eb2d04bcbb543 Revert &quot;feat: add
z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;</li>
<li>cc4cd4ee9c52fcaa10964e48cc144541e41a5ed9 Revert &quot;Revert
&quot;feat: add z.currencyCode() over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;&quot;</li>
<li>0f3f5ee3ca56c7574bf849e54f79e9a6e02562ee 4.6.5</li>
<li>59bbc03e10c636b9eb3c393dfeb552819774ec21 chore: re-pin the
integration peers to the workspace zod after the 4.6.5 bump</li>
</ul>
<h2>v4.6.4</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.3">4.6.3</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/d6bc1e30"><code>d6bc1e30</code></a>
feat: add <code>z.currencyCode()</code> over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/pull/6595">#6595</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/ad32d751"><code>ad32d751</code></a>
perf: <code>z.url()</code> rejects an invalid URL with
<code>URL.canParse()</code> instead of a throwing constructor, about 50x
faster; fewer allocations on the validation path (<a
href="https://redirect.github.com/colinhacks/zod/pull/6588">#6588</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/2bb08717"><code>2bb08717</code></a>
chore: re-pin the integration peers to the workspace zod after the 4.6.4
bump</li>
<li><a
href="https://github.com/colinhacks/zod/commit/f6e1701a"><code>f6e1701a</code></a>
chore(deps): bump next to 15.5.25 and vite to 7.3.6 (<a
href="https://redirect.github.com/colinhacks/zod/pull/6153">#6153</a>)</li>
</ul>
<h2>v4.6.3</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.2">4.6.2</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/413cce9a"><code>413cce9a</code></a>
fix(v4): make z.properties() a check again (<a
href="https://redirect.github.com/colinhacks/zod/pull/6594">#6594</a>) —
removes the standalone <code>z.properties()</code> schema from 4.6.0;
<code>z.instanceof().properties()</code> and
<code>.check(...z.properties())</code> are unchanged</li>
<li><a
href="https://github.com/colinhacks/zod/commit/75d63ee1"><code>75d63ee1</code></a>
docs: show only the <code>.properties()</code> method form in the 4.6
post</li>
<li><a
href="https://github.com/colinhacks/zod/commit/46da9572"><code>46da9572</code></a>
docs: match the error-message examples to what the parsers emit</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/b500a2ee95134f81d74da7911d7378dd2853d042"><code>b500a2e</code></a>
4.6.7</li>
<li><a
href="https://github.com/colinhacks/zod/commit/9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1"><code>9c21983</code></a>
ci: stage at verbose so a failed OIDC token exchange says why</li>
<li><a
href="https://github.com/colinhacks/zod/commit/529f78dd9431b07f49647fee3aa0819b3f8b66b7"><code>529f78d</code></a>
4.6.6</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a0ebf970f2d752e5f58527cd2942ff48be4f46bd"><code>a0ebf97</code></a>
fix(v4): check array, set and map size bounds before parsing elements
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a2ba4a67986a3e6b2b131be30084a2f460d9ea67"><code>a2ba4a6</code></a>
fix(types): declare record output and input as members, not base type
argumen...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/c42596bd1c87e941d7241d10e012c5c196adcc10"><code>c42596b</code></a>
release: hold the run on the release-approval environment until the
staged ve...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/bacad518c41610d654658172d325874bf0ee6290"><code>bacad51</code></a>
docs: square the Code for Japan logo to match the other bronze
tiles</li>
<li><a
href="https://github.com/colinhacks/zod/commit/8fb2faf0584828a6a027d8279db36f6f52784462"><code>8fb2faf</code></a>
docs: crop the Code for Japan logo and list it first in bronze</li>
<li><a
href="https://github.com/colinhacks/zod/commit/b2ef983e0711ff6517d6020b021071e2e5653efa"><code>b2ef983</code></a>
docs: add Code for Japan to bronze sponsors</li>
<li><a
href="https://github.com/colinhacks/zod/commit/74304ce5b5796b5edb9a88ceb8e170b6ea3e82df"><code>74304ce</code></a>
release: stage npm versions instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v4.3.6...v4.6.7">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 zod since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zod&package-manager=npm_and_yarn&previous-version=4.3.6&new-version=4.6.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-10-29 02:29:45 +09:00
Richard Scarrott 5a3e5dd739 fix(sdk-py): retry failed stream reconnects with backoff (#9462)
## Summary

A stream reconnect that fails to open (429, 5xx, transport error,
non-SSE response) is now retried with backoff instead of ending the
stream, e.g. while the server restarts or deploys.

```diff
 HttpClient.stream / SyncHttpClient.stream, on reconnect (GET to Location)
-  retry only a mid-read drop, with no delay, 5 times per stream
+  also retry a reconnect that fails to open: 429, 5xx, transport error, non-SSE
+  sleep 1, 2, 4, 5, 5s (+ up to 25% jitter) before each reconnect
+  reset the 5 attempts once a reconnect yields an event
+  give up with "Exceeded maximum…" raised from the last error
```

The first POST still raises on any error, since retrying it could start
a duplicate run. The backoff formula moves to `_shared/utilities.py`,
shared with the stream controllers.

The sleep also delays the first reconnect after a plain drop by about a
second, which matches the JS SDK. A run created without
`stream_resumable=True` can't replay what it sent while the client was
away, so it now loses about a second more of events. Resumable runs lose
nothing.

## Evidence

- **Before:** a reconnect hitting 429/502/503, a connect error or a
non-SSE response raised, a stream with 6 spaced-out drops gave up, and
reconnects never slept.
**After:** `test_client_stream.py` covers each (sync and async), plus
404 and first-POST failures staying fatal, and giving up after ~20s.

## Merge Danger

**Door:** two-way

**Blast Radius:** small (sdk-py stream reconnects only)
2026-10-28 11:46:39 +00:00
Hunter Lovell eb8ec111b3 fix(langgraph): emit node-output messages from a graph streamed inside a node (#9463)
When a node streams a graph with `stream_mode="messages"`, messages that
one of that graph's nodes *returns* (rather than streams from an LLM)
are dropped. The same graph streamed at the top level reports them:

```python
def respond(state: MessagesState) -dict:
    return {"messages": [AIMessage("hi there")]}

subgraph = StateGraph(MessagesState).add_node("respond", respond).add_edge(START, "respond").compile()

subgraph.stream(msgs, stream_mode="messages")          # -> "hi there"

def parent_node(state, config):
    for msg, _ in subgraph.stream(msgs, config, stream_mode="messages"):
        ...                                             # -> nothing
```

A graph streamed this way attaches its own messages handler with the
node's namespace as `parent_ns`, so it still reports that graph's own
messages with `subgraphs=False` (#5836). `on_chat_model_start` honors
`parent_ns`, but `on_chain_start` didn't, so node outputs were filtered
out as if they came from a subgraph. This applies the same check in
`on_chain_start`.

Split out of #9453 (see @eliornl's [review
comment](https://github.com/langchain-ai/langgraph/pull/9453#discussion_r…)).
2026-10-27 14:46:39 -07:00
Hunter Lovell 8c177f7388 fix(langgraph): respect subgraphs=False in RemoteGraph.stream under a streaming parent (#9453)
When a node streams a `RemoteGraph` with `subgraphs=False` while its
parent graph is itself streamed with `subgraphs=True`,
`RemoteGraph.stream`/`astream` yield the remote graph's subgraph events
to the caller. With `stream_mode="updates"`, the node gets `['model',
'child']` where an in-memory graph streamed the same way gives
`['child']`.

`RemoteGraph` requests `stream_subgraphs=True` whenever the config
carries a parent stream, so the parent sees the remote graph's subgraph
events. Those events are forwarded to the parent correctly, but the
values yielded to the caller were never filtered by the caller's own
`subgraphs` setting. Locally this falls out of how streams are wired:
with `subgraphs=False` the run never installs the caller's queue for its
subgraphs, so their events only reach the parent. This applies the same
split on the client: subgraph events still go to the parent, and only
reach the caller when it passed `subgraphs=True`.

`messages` events are left as they are. The server's messages handler
dedupes across namespaces, so once it has sent a message from inside a
subgraph it doesn't send the top-level copy a `subgraphs=False` caller
would see locally. Filtering would leave that caller with nothing, so it
keeps getting the subgraph tokens, as today. Matching local exactly
needs the server to produce the caller's view; I'll open an issue for
that.

The new tests fail on `main`.
2026-10-27 14:40:42 -07:00
55013146ae fix(sdk-py): match Auth.on overload to optional resources (#9450)
Match the `Auth.on` decorator-factory overload to its existing optional
`resources` argument so `auth.on()`, action-only calls, and
`resources=None` type-check. Add regression coverage for the overload
and wildcard registration; runtime authorization behavior is unchanged.

## References
- [Slack
thread](https://langchain.slack.com/archives/C0C411URFR6/p1792789013113819)

Made by [Open SWE](https://github.com/langchain-ai/open-swe) · [view
thread](https://openswe.langchain.dev/agents/094c284b-638a-5b04-95f0-623b47d0a52f)
· openai:gpt-6.1-sol (medium)

Co-authored-by: Hunter Lovell <hntrl@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-10-27 14:34:20 -07:00
Elior Nataf Lackritz 858891aaa1 fix(langgraph): don't report a paused tool as failed on the tools stream (#9406)
`interrupt()` inside a tool raises `GraphInterrupt`, and langchain-core
calls `on_tool_error` for every exception, so the `tools` stream
reported a tool that was only waiting for an answer as `tool-error`,
with the interrupt flattened into the message. With a subagent running
inside a tool, the outer tool showed as failed while the subagent waited
for approval. Fixes #8218.

## Fix

- An interrupt or a drain ends the tool's events without a `tool-error`.
On resume the tool runs again and emits `tool-started` and
`tool-finished`. A parent command doesn't rerun the tool, so it still
ends the call with `tool-error`, as before.
- `astream` no longer runs every node in streaming mode just because the
`tools` handler is attached. Like the `messages` handler, it doesn't
need it, and with it a `@task` awaited inside a subgraph node returned
`None` whenever the `tools` mode was on.

The new tests fail on `main`.

## Release note

The `tools` stream no longer sends `tool-error` for a tool that pauses
on `interrupt()`. Until the run resumes, that tool has a `tool-started`
event and no end event. In-process `stream_events(version="v3")` now
ends that tool's stream as completed with no output, instead of failed
with the interrupt.

Same idea as #8219 by @haseeb-m4235 and #8282 by @thsfranca.
2026-10-26 19:35:13 -04:00
Elior Nataf Lackritz 1526b0e82d fix(sdk-py): reset the reconnect cursor on every empty id (#9440)
#9402 made an empty `id:` field reset the reconnect cursor, so the next
reconnect sends no `Last-Event-ID`. Two cases still reconnected with the
old cursor.

- A `Last-Event-ID` header the caller passed in, as
`runs.join_stream(last_event_id=...)` does, stayed in the reconnect
headers. A stream joined at 5 that sent id 6 and then an empty id
reconnected from 5, so event 6 arrived twice. Once the stream has sent
an `id:` field, the caller's header is now dropped, in any letter case,
and the stream's cursor is sent if it is not empty. This also stops a
caller header spelled `last-event-id` from going out next to the SDK's
own `Last-Event-ID`.
- An empty `id:` on its own, with no event or data in its block, never
reached the stream loop, because the decoder skipped the block as empty.
The decoder now returns it as an empty part. The stream loop takes the
reset from it and, like every other caller in the SDK, drops the part.

The new test cases fail on `main`.

Both cases were reported by @1052326311 on #9014 and by @Tivonsico on
#9230, whose suggested approach this follows. Related: #9299, not fixed
here.

## Release note

Once a stream has sent an `id:` field, reconnects no longer send a
`Last-Event-ID` header the caller passed in. After an empty `id:`,
`SSEDecoder.decode` returns an empty part, with no event and no data,
for each block that has neither, as it already did after a non-empty id.
2026-10-26 20:31:20 +00:00
Elior Nataf Lackritz c2c73b4f91 fix(langgraph): return an awaited @task's value while streaming (#9407)
A `@task` awaited inside a node returned `None` whenever the node ran in
streaming mode, which `astream_events` (v1 and v2) turns on, and so does
everything built on it. With streaming, `arun_with_retry` drains the
task's `astream()` and returned nothing, so the awaiting node got
`None`. Fixes #9284.

It now returns the value the task wrote, on both stream paths (with and
without a timeout).

The new test fails on `main` for v1 and v2, including a task that
returns a falsy value.

Same idea as #9295 by @AaronG18, and the lookup @amishaguptaberk
suggested on the issue.
2026-10-26 20:29:22 +00:00
Sera 03e73ff51b feat(langgraph): list ToolNode tool names in graph node metadata (#9449)
A `ToolNode` appears in `get_graph()` output as a plain runnable named
`tools`. Graph viewers such as Studio can't show which tools it runs,
and with deep agents that's most of what the agent does.

## Changes
- `draw_graph` adds `metadata["__tools"]`, the node's tool names in
order, for any node with a non-empty `tools_by_name`.
- The check is duck-typed, so core doesn't import from
`langgraph-prebuilt`.

## Reviewer notes
- Names only. Tool descriptions are long for deep agents and would bloat
`/graph` payloads.
- This sits next to `__interrupt`. Like it, `__tools` shows up in node
labels from `draw_mermaid(with_styles=True)`, but not in the
`with_styles=False` output.

## Test Plan
- [x] The `to_json()` snapshots for `create_react_agent` and the
hand-built `ToolNode` graph now include `__tools`.
- [x] A local deep agent's `/assistants/{id}/graph` response lists all
11 tools on the `tools` node.
2026-10-26 16:11:08 -04:00
Elior Nataf Lackritz 3521f08e21 release(checkpoint): 4.3.1 (#9447)
Version bump only. No library code changes in this PR.

Unreleased since `checkpoint==4.3.0`:

- #9383 - reject NUL bytes in store namespace labels
2026-10-26 19:47:44 +00:00
Elior Nataf Lackritz 24294fad37 release(langgraph): 1.2.18 (#9445)
Bumps langgraph from 1.2.17 to 1.2.18.

Includes:

- Default `DeltaChannel` snapshots to every 100 updates instead of 1000
(#9420)
- Let `error_handler` recover on every path and stream failed task
results (#9408)
- Stream the last checkpoint of a run stopped by `interrupt_after` or
the recursion limit (#9409)
- Stop duplicating tags at every subgraph level (#9400)
2026-10-26 15:40:09 -04:00
Elior Nataf Lackritz f6317d5cb1 release(sdk-py): 0.4.10 (#9446)
Bumps the Python SDK version from 0.4.9 to 0.4.10.

Includes:

- Retry `runs.wait` with a bodyless GET and raise run errors in the sync
`wait` (#9399)
- Follow the SSE spec for multi-line data, empty ids and a leading BOM
(#9402)
- Accept `durability`, `stream_resumable` and `context` in the `runs`
overloads (#9403)
2026-10-26 15:40:04 -04:00
Elior Nataf Lackritz 7e56e6c80b fix(langgraph): let error_handler recover on every path and stream failed task results (#9408)
- A node's `error_handler` ran, but the original error was still raised
when the node ran on the executor path: when streaming `messages` or
`custom`, with `subgraphs=True`, or next to a parallel node. The runner
marked the handled task's future, but the executors re-raised every
failed future on exit without checking the mark. The mark now lives in
the executor module, and both executors skip those futures. Fixes #8608.
- With only `tasks` or `debug` streaming, a failing task's result (with
its `error`) was never streamed: the single-task fast path raised before
the caller drained the stream queue. It now yields once before
re-raising. Fixes #5764.

The new tests fail on `main`.

Ideas from #8278 by @UdayKiranJadi, #8611 by @SnowingFox, #8823 by
@sailikhithk, #6984 by @Br1an67 and #8400 by @cms19859230182-lang.
2026-10-26 14:15:25 -04:00
0fc4f35b66 feat(langgraph)!: default delta snapshots to 100 updates (#9420)
Change `DeltaChannel`'s default `snapshot_frequency` from 1000 to 100
updates, including channels used for messages, to bound replay latency
with more frequent snapshots. Explicit overrides remain unchanged.

Co-authored-by: Elior Nataf Lackritz <eliornl@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-10-26 13:28:01 -04:00
raghavverma-langchain 58be6911de release(cli): 0.4.40 (#9443)
## Summary

bumps the cli to 0.4.40 to cut a release with #9396, the only cli change
since 0.4.39

- `langgraph deploy --remote` now sends whatever headers the control
plane returns with the source upload url, so uploads work with storage
that needs extra headers (like azure blob storage)
- control planes that dont return any headers keep the old behavior, so
nothing changes for existing setups

## Test Plan
- [x] #9396 (send the upload headers the control plane returns) is the
only CLI change merged since 0.4.39
- [ ] After merge, run the `release` workflow with `working-directory:
libs/cli` (merging doesn't publish by itself)
- [ ] `pip install -U langgraph-cli` picks up 0.4.40

## Demo

<!-- Drag a screen recording or screenshot here -->
2026-10-26 16:25:10 +00:00
raghavverma-langchain 1adeafeaf3 feat(cli): send the upload headers the control plane returns (#9396)
## Summary

some storage needs extra headers on the signed upload PUT (azure blob
storage 400s without `x-ms-blob-type`), and `langgraph deploy --remote`
always sent the same gcs style headers no matter what. this lets the
control plane tell the cli what to send

- `request_upload_url` now sends `supports_upload_headers: true` so the
control plane knows this cli can handle it
- if the response has `upload_headers` the upload sends exactly those +
content-length. if not its the same defaults as before so older control
planes wont see a difference
- content-type stays as a fallback, otherwise urllib sneaks in a form
content type and that breaks signed urls

## Test Plan
- [x] `make lint` and `make test` pass in `libs/cli` (576 tests)
- [x] New tests: the upload-url request carries the flag; returned
headers are sent as-is plus the real `Content-Length` (overriding a
returned one); missing or empty `upload_headers` sends the old defaults;
returned headers without a `Content-Type` still send `application/gzip`.
Each fails without the change
- [x] Against a real Azure Blob Storage upload URL: 0.4.36 fails with
`MissingRequiredHeader` (`x-ms-blob-type`), and this branch's upload
with the returned headers succeeds (201)
- [ ] After release, `langgraph deploy --remote` still uploads against a
control plane that returns no `upload_headers`

## Demo

<!-- Drag a screen recording or screenshot here -->
2026-10-26 11:02:08 -04:00
Hugo DURAND dc1741ce96 release(cli): 0.4.39 (#9442)
Bumps the CLI to 0.4.39 to cut a release including #9395, the only CLI
change merged since 0.4.38:

- `langgraph deploy list --json` prints `[{"id": "...", "name":
"..."}]`, so a CI job can check whether a deployment exists before it
deploys.
- `deploy list` and `deploy listeners list` now read every page, instead
of stopping at 20 deployments or 100 listeners.

Merging doesn't publish by itself: after it merges, run the `release`
workflow with `working-directory: libs/cli`.
2026-10-26 10:14:16 -04:00
Igor Soarez 244ca657e4 docs: add "Direction: LangGraph is a runtime" section to AGENTS.md (#9439)
## What

Adds a "Direction" section to `AGENTS.md`, placed before the library
list so it frames how the rest of the file is read. It states the
project's strategic direction for contributors — human and coding-agent
alike:

- LangGraph is consolidating as the low-level **runtime** for agents and
workflows (graph execution, checkpointing, streaming, interrupt/resume),
and is moving away from being a friendly SDK for building agents
directly.
- Agent development belongs on a harness; Deep Agents (separate repo) is
the recommended one.
- Contributions are optimized for runtime correctness, reliability,
performance, and simplicity — not for the shortest agent example.

## Why

LangGraph today is both a runtime and an agent-building SDK. As agent
harnesses have consolidated, keeping both roles blurs the contribution
criteria: "should I add this convenience helper?" and "is this the right
place for this feature?" have no written answer. `AGENTS.md` is the
first thing a coding agent reads before touching this repo; giving it an
explicit direction makes that judgment mechanical and aligns
agent-generated contributions with where the project is going.

## Notes for reviewers

- **The ratchet is stated inline, not linked.** The section embeds the
stabilization rule ("one way to do each thing, and every change leaves
fewer ways than it found") directly, because that rule currently has no
home in this repo. If the full stabilization policy lands in
`CONTRIBUTING.md`, the bullet can shrink to a link and the "deprecation
process" reference can point at it.
- **Scope is explicit.** The consolidation mandate applies to the
runtime core (`langgraph`, `checkpoint*`); the client SDKs and CLI
remain ergonomic, as thin clients.
- **`prebuilt` is marked maintenance-only**, matching its state in code
(`create_react_agent` is already `@deprecated` in favour of
`langchain.agents`). The library list itself is unchanged in this PR;
rewording the `langgraph` and `prebuilt` descriptions could be a
follow-up.
- **Removal discipline is part of the section**: breaking changes must
be a PR's stated purpose and go through deprecation — never a side
effect of an unrelated change. The intent is to prevent agents from
over-reading the direction as license for opportunistic removals.
- **Companion PR:** a matching section adapted for
langchain-ai/langgraphjs follows.

Nothing here changes code or CI; this is a documentation-only change to
contributor guidance.
2026-10-26 15:56:56 +02:00
Hugo DURAND c696d4e79f feat(cli): add --json to deploy list (#9395)
This PR adds a `--json` flag to the `langgraph deploy list` CLI command.
It solves the issue of CI/CD pipelines having to parse complex ASCII
tables by providing a structured JSON output (e.g., `[{"id": "...",
"name": "..."}]`) to check for existing deployments. It also fixes a
limitation where the CLI previously only fetched the first 20
deployments by implementing proper pagination.

## Changes

* **Added `--json` flag:** Outputs a clean, allow-listed JSON array of
deployments.
* **Stripped secrets:** Explicitly excludes environment variables from
the JSON output to prevent leaking secrets in CI logs.
* **Full pagination:** Updates the backend to fetch all limit/offset
pages, ensuring all deployments (not just the first 20) are retrieved.
* **Cleaned standard output:** Moved beta warnings to `stderr` and
disabled interactive prompts when using `--json` to guarantee valid JSON
on `stdout`.
* **Centralized formatting:** Refactored three separate table rendering
functions into a single shared `Table` and `JsonOutput` logic.

## Test

* **Automated checks:** Passed `make format`, `make lint`, and `make
test` (580 tests total).
* **New unit tests:** Added extensive testing for JSON shape, secret
omission, empty lists, missing IDs, stderr routing, and multi-page
retrieval logic.
* **Manual verification:** Tested against a local mock control plane
with 101 deployments, verifying that `jq` can successfully parse the
output and check for specific deployment names.

Release Note: Added `--json` flag to `langgraph deploy list` to support
scriptable, structured deployment lookups in CI/CD environments.
2026-10-26 09:45:57 -04:00
Elior Nataf Lackritz 34c7f58655 fix(sdk-py): retry runs.wait with a bodyless GET and raise run errors in sync wait (#9399)
Two `runs.wait` bugs in the Python SDK.

- When the response of `runs.wait` broke off mid-read, the SDK retried
with a GET to the `Location` header, but that GET reused the POST's
`Content-Length` and `Content-Type`, so the only retry always failed
with `h11.LocalProtocolError`. The sync client also sent a JSON `null`
body on every body-less request made this way (`runs.join`,
`runs.cancel(wait=True)`). Fixes #7858.
- Sync `runs.wait(raise_error=True)` returned a failed run's
`{"__error__": ...}` as a normal result, and sent `raise_error` to the
server, which ignores it. The async client raises. Fixes #8383.

## Fix

The retry GET carries only the caller's headers, and the sync client
sends no body for `json=None`, as the async one already did. Sync
`runs.wait` checks `__error__` like the async client and no longer sends
`raise_error`.

The new tests fail on `main`.

## Release note

Sync `runs.wait` now raises when the run fails, as `raise_error=True`
(the default) always documented. Code that reads `__error__` from the
result should pass `raise_error=False`.

Ideas from #7869 by @pragnyanramtha, #8385 by @Solaris-star and #9377 by
@ShallAbby.
2026-10-26 09:25:00 -04:00
Elior Nataf Lackritz 1b1aee689d fix(langgraph): stream the last checkpoint of a run stopped by interrupt_after or the recursion limit (#9409)
When a run stopped at an `interrupt_after` pause or at the recursion
limit, the checkpoint it stopped on was never sent on the `checkpoints`
or `debug` stream. A checkpoint is streamed at the start of the next
tick, which works out its next tasks; `interrupt_after` raises right
after saving it, and the recursion limit stops the run at the start of
the next tick before that point. A client that resumes from the last
checkpoint it saw ran the last node again. Fixes #9180.

The block that prepares the next tasks and streams the checkpoint moves
into one method, called by `tick` and on both of those exits.

The new tests fail on `main` with every checkpointer in the suite.

## Release note

The `checkpoints` and `debug` streams send one more event when a run
stops at an `interrupt_after` pause or at the recursion limit: the
checkpoint it stopped on.

Same approach as @jayhemnani9910's branch on the issue.
2026-10-26 09:18:02 -04:00
Elior Nataf Lackritz 27aeb75efd fix(langgraph): stop duplicating tags at every subgraph level (#9400)
Tags passed at invoke time are repeated at every subgraph level: a node
one subgraph deep sees each tag twice, three levels deep 8 times. The
copies show up in the node's `config["tags"]`, in `messages` stream
metadata and in traces. Fixes #9084.

## Cause

`ensure_config` appends the tags of explicit configs to the ambient
config's tags, so tags bound with `with_config` survive. A subgraph is
called with its parent's config, which the ambient config also holds, so
the parent's tags are appended a second time at each level.

## Fix

`ensure_config` skips tags the ambient config already has. Tags repeated
across explicit configs are still concatenated, as before.

The new tests fail on `main`.

Same approach as #9110 by @mudscode.
2026-10-26 08:56:49 -04:00
Elior Nataf Lackritz c18a42fcba fix(sdk-py): follow the SSE spec for multi-line data, empty ids and a leading BOM (#9402)
The Python SDK's SSE decoder broke three rules of the SSE spec.

- Repeated `data:` lines were concatenated without a newline, so a value
split across lines could decode to a different one: `[1` then `0]`
became `[10]`. They are now joined with a newline. Fixes #7915.
- An empty `id:` field did not reset the reconnect cursor, so a
reconnect after it resumed from the previous id. It now clears the
cursor, and the reconnect sends no `Last-Event-ID`. Fixes #9009.
- A UTF-8 BOM at the start of a stream became part of the first field
name, so the first event lost its type. It is now stripped. Fixes #9225.

A server that writes each event's data on one line, with no BOM and no
empty ids, sees no change.

The new tests fail on `main`.

## Release note

Multi-line `data:` fields are joined with newlines, so data that only
parsed because its lines were glued together now raises a decode error.
`SSEDecoder.last_event_id` returns `""` after an empty `id:` field,
instead of `None`.

Ideas from #7936 by @NishchayMahor, #7947 by @tayyub-ai, #9014 by
@daleselaji-dev and #9236 by @Kayvan-Zahiri.
2026-10-26 08:56:37 -04:00
Elior Nataf Lackritz b54566b620 fix(sdk-py): accept durability, stream_resumable and context in the runs overloads (#9403)
The `@overload` stubs of `runs.stream`, `runs.create` and `runs.wait`
left out parameters their implementations accept, so type checkers
rejected valid calls, for example `client.runs.wait(thread_id,
assistant_id, durability="sync")` (mypy: "Unexpected keyword argument").
Fixes #8442.

Added `durability` to all 16 stubs, `stream_resumable` to the two sync
stateful `stream` stubs, and `context` to the two async stateless
`stream` stubs. A new test parses both runs clients and checks that
every overload accepts the implementation's keyword parameters, except
the ones a run kind leaves out on purpose (thread-only parameters on
stateless runs, `on_completion` on stateful ones). It fails on `main`,
and mypy accepts the calls above with this change.

Covers the gap #7101 by @KartikPawade fixed for `stream_resumable`.

Co-authored-by: Kartik Pawade
<81703361+KartikPawade@users.noreply.github.com>
Co-authored-by: Anurag Paul
<231525521+anuragpaul602-netizen@users.noreply.github.com>
2026-10-26 08:56:26 -04:00
dependabot[bot] a25bfafe48 chore(deps): bump zod from 4.3.6 to 4.6.7 in /libs/cli/js-examples (#9432)
Bumps [zod](https://github.com/colinhacks/zod) from 4.3.6 to 4.6.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.6.7</h2>
<h2>Commits:</h2>
<ul>
<li>b500a2ee95134f81d74da7911d7378dd2853d042 4.6.7</li>
</ul>
<h2>v4.6.6</h2>
<blockquote>
<p>Six malicious commits pushed to <code>main</code> on September 21
with a compromised git credential were reverted the same day in <a
href="https://redirect.github.com/colinhacks/zod/issues/6620">#6620</a>
and have since been removed from the history of <code>main</code>. None
were ever published to npm. The npm provenance for 4.6.6 names 284243fb,
the pre-rewrite copy of 9c219833 with identical contents.</p>
</blockquote>
<h2>Commits:</h2>
<ul>
<li>0cd3af5e9f50ad11bbcd4407a46a21b8848ba574 docs: load the en locale
directly in Zod Mini</li>
<li>9d5e60f162997b764b40d45aa02481888db50dea docs: import locales from
zod/locales, not z.locales</li>
<li>34a626f307871ecbb0123947559328dc52ed5e3e docs: tighten the z.locales
tree-shaking note</li>
<li>74304ce5b5796b5edb9a88ceb8e170b6ea3e82df release: stage npm versions
instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>b2ef983e0711ff6517d6020b021071e2e5653efa docs: add Code for Japan to
bronze sponsors</li>
<li>8fb2faf0584828a6a027d8279db36f6f52784462 docs: crop the Code for
Japan logo and list it first in bronze</li>
<li>bacad518c41610d654658172d325874bf0ee6290 docs: square the Code for
Japan logo to match the other bronze tiles</li>
<li>c42596bd1c87e941d7241d10e012c5c196adcc10 release: hold the run on
the release-approval environment until the staged versions are approved
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6638">#6638</a>)</li>
<li>a2ba4a67986a3e6b2b131be30084a2f460d9ea67 fix(types): declare record
output and input as members, not base type arguments (<a
href="https://redirect.github.com/colinhacks/zod/issues/6645">#6645</a>)</li>
<li>a0ebf970f2d752e5f58527cd2942ff48be4f46bd fix(v4): check array, set
and map size bounds before parsing elements (<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li>529f78dd9431b07f49647fee3aa0819b3f8b66b7 4.6.6</li>
<li>9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1 ci: stage at verbose so a
failed OIDC token exchange says why</li>
</ul>
<h2>v4.6.5</h2>
<h2>Commits:</h2>
<ul>
<li>d2b135cfb7a3582b9eb515756b9166bcb9521f4a docs: add the 4.6.x patch
highlights to the 4.6 post</li>
<li>f1448f7cee00df9fe1e9ad84a000aa1828cc8bc1 docs: fold the 4.6.x patch
highlights into the 4.6 post's own sections</li>
<li>de65a5cb39ed22a507fac935788f718fa88d104f docs: lead the properties
section with the check and add a Zod Mini tab (<a
href="https://redirect.github.com/colinhacks/zod/issues/6598">#6598</a>)</li>
<li>56222cd1532c07bcb91b67df529cab4c0a215330 feat(instanceof): key the
.properties() shape off the instance type (<a
href="https://redirect.github.com/colinhacks/zod/issues/6600">#6600</a>)</li>
<li>ca0229a404818290e6cdcfefcd7eb2d04bcbb543 Revert &quot;feat: add
z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;</li>
<li>cc4cd4ee9c52fcaa10964e48cc144541e41a5ed9 Revert &quot;Revert
&quot;feat: add z.currencyCode() over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;&quot;</li>
<li>0f3f5ee3ca56c7574bf849e54f79e9a6e02562ee 4.6.5</li>
<li>59bbc03e10c636b9eb3c393dfeb552819774ec21 chore: re-pin the
integration peers to the workspace zod after the 4.6.5 bump</li>
</ul>
<h2>v4.6.4</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.3">4.6.3</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/d6bc1e30"><code>d6bc1e30</code></a>
feat: add <code>z.currencyCode()</code> over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/pull/6595">#6595</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/ad32d751"><code>ad32d751</code></a>
perf: <code>z.url()</code> rejects an invalid URL with
<code>URL.canParse()</code> instead of a throwing constructor, about 50x
faster; fewer allocations on the validation path (<a
href="https://redirect.github.com/colinhacks/zod/pull/6588">#6588</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/2bb08717"><code>2bb08717</code></a>
chore: re-pin the integration peers to the workspace zod after the 4.6.4
bump</li>
<li><a
href="https://github.com/colinhacks/zod/commit/f6e1701a"><code>f6e1701a</code></a>
chore(deps): bump next to 15.5.25 and vite to 7.3.6 (<a
href="https://redirect.github.com/colinhacks/zod/pull/6153">#6153</a>)</li>
</ul>
<h2>v4.6.3</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.2">4.6.2</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/413cce9a"><code>413cce9a</code></a>
fix(v4): make z.properties() a check again (<a
href="https://redirect.github.com/colinhacks/zod/pull/6594">#6594</a>) —
removes the standalone <code>z.properties()</code> schema from 4.6.0;
<code>z.instanceof().properties()</code> and
<code>.check(...z.properties())</code> are unchanged</li>
<li><a
href="https://github.com/colinhacks/zod/commit/75d63ee1"><code>75d63ee1</code></a>
docs: show only the <code>.properties()</code> method form in the 4.6
post</li>
<li><a
href="https://github.com/colinhacks/zod/commit/46da9572"><code>46da9572</code></a>
docs: match the error-message examples to what the parsers emit</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/b500a2ee95134f81d74da7911d7378dd2853d042"><code>b500a2e</code></a>
4.6.7</li>
<li><a
href="https://github.com/colinhacks/zod/commit/9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1"><code>9c21983</code></a>
ci: stage at verbose so a failed OIDC token exchange says why</li>
<li><a
href="https://github.com/colinhacks/zod/commit/529f78dd9431b07f49647fee3aa0819b3f8b66b7"><code>529f78d</code></a>
4.6.6</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a0ebf970f2d752e5f58527cd2942ff48be4f46bd"><code>a0ebf97</code></a>
fix(v4): check array, set and map size bounds before parsing elements
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a2ba4a67986a3e6b2b131be30084a2f460d9ea67"><code>a2ba4a6</code></a>
fix(types): declare record output and input as members, not base type
argumen...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/c42596bd1c87e941d7241d10e012c5c196adcc10"><code>c42596b</code></a>
release: hold the run on the release-approval environment until the
staged ve...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/bacad518c41610d654658172d325874bf0ee6290"><code>bacad51</code></a>
docs: square the Code for Japan logo to match the other bronze
tiles</li>
<li><a
href="https://github.com/colinhacks/zod/commit/8fb2faf0584828a6a027d8279db36f6f52784462"><code>8fb2faf</code></a>
docs: crop the Code for Japan logo and list it first in bronze</li>
<li><a
href="https://github.com/colinhacks/zod/commit/b2ef983e0711ff6517d6020b021071e2e5653efa"><code>b2ef983</code></a>
docs: add Code for Japan to bronze sponsors</li>
<li><a
href="https://github.com/colinhacks/zod/commit/74304ce5b5796b5edb9a88ceb8e170b6ea3e82df"><code>74304ce</code></a>
release: stage npm versions instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v4.3.6...v4.6.7">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 zod since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zod&package-manager=npm_and_yarn&previous-version=4.3.6&new-version=4.6.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-10-25 15:27:13 +00:00
dependabot[bot] 471fee3dff chore(deps): bump langgraph-checkpoint-postgres from 3.1.1 to 3.1.3 in /libs/cli/uv-examples/monorepo in the uv group across 1 directory (#9422)
Bumps the uv group with 1 update in the /libs/cli/uv-examples/monorepo
directory:
[langgraph-checkpoint-postgres](https://github.com/langchain-ai/langgraph).

Updates `langgraph-checkpoint-postgres` from 3.1.1 to 3.1.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langgraph/releases">langgraph-checkpoint-postgres's
releases</a>.</em></p>
<blockquote>
<h2>langgraph-checkpoint-postgres==3.1.3</h2>
<p>Changes since checkpointpostgres==3.1.2</p>
<ul>
<li>release(checkpoint-postgres): 3.1.3 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9287">#9287</a>)</li>
<li>fix(checkpoint-postgres,checkpoint-sqlite): treat an empty
checkpoint_id as the latest in delta history (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9262">#9262</a>)</li>
<li>perf(checkpoint-postgres): start the delta walk at the target (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9248">#9248</a>)</li>
<li>fix(checkpoint,checkpoint-postgres,checkpoint-sqlite): validate
namespace labels before store dispatch (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9217">#9217</a>)</li>
<li>fix: order delta channel replay by task path (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8544">#8544</a>)</li>
<li>chore(deps): bump the minor-and-patch group in
/libs/checkpoint-postgres with 6 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9147">#9147</a>)</li>
<li>chore(deps): bump urllib3 from 2.7.0 to 2.8.0 in
/libs/checkpoint-postgres (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9132">#9132</a>)</li>
<li>fix(checkpoint-postgres): derive the delta walk cursor once the
target loads (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8556">#8556</a>)</li>
<li>chore(deps): bump the minor-and-patch group in
/libs/checkpoint-postgres with 3 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8773">#8773</a>)</li>
<li>fix(checkpoint): widen <code>Store</code> <code>put</code> value
type to <code>Mapping[str, Any]</code> (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8617">#8617</a>)</li>
</ul>
<h2>langgraph-checkpoint-postgres==3.1.2</h2>
<p>Changes since checkpointpostgres==3.1.1</p>
<ul>
<li>release(checkpoint-postgres): 3.1.2 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8565">#8565</a>)</li>
<li>release(checkpoint): 4.2.0 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8563">#8563</a>)</li>
<li>test(checkpoint-postgres,checkpoint-sqlite): run the conformance
suite (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8537">#8537</a>)</li>
<li>fix(checkpoint-postgres): find plain-value seeds when walking delta
history (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8535">#8535</a>)</li>
<li>chore: enable RUF100 and clear unused noqa directives (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8546">#8546</a>)</li>
<li>chore(checkpoint-postgres,checkpoint-sqlite): enable PLC0415 lint
rule (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8540">#8540</a>)</li>
<li>chore(deps): bump the minor-and-patch group in
/libs/checkpoint-postgres with 5 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8497">#8497</a>)</li>
</ul>
<h2>langgraph-checkpoint-sqlite==3.1.2</h2>
<p>Changes since checkpointsqlite==3.1.1</p>
<ul>
<li>release(checkpoint-sqlite): 3.1.2 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9288">#9288</a>)</li>
<li>fix(checkpoint-postgres,checkpoint-sqlite): treat an empty
checkpoint_id as the latest in delta history (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9262">#9262</a>)</li>
<li>fix(checkpoint,checkpoint-postgres,checkpoint-sqlite): validate
namespace labels before store dispatch (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9217">#9217</a>)</li>
<li>fix: order delta channel replay by task path (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8544">#8544</a>)</li>
<li>chore(deps-dev): bump the minor-and-patch group in
/libs/checkpoint-sqlite with 3 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9146">#9146</a>)</li>
<li>chore(deps): bump urllib3 from 2.7.0 to 2.8.0 in
/libs/checkpoint-sqlite (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9135">#9135</a>)</li>
<li>fix(checkpoint-sqlite): walk delta ancestors by parent pointer (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8557">#8557</a>)</li>
<li>chore(deps): bump anyio from 4.12.1 to 4.14.2 in
/libs/checkpoint-sqlite (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8993">#8993</a>)</li>
<li>chore(deps-dev): bump the minor-and-patch group in
/libs/checkpoint-sqlite with 2 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8772">#8772</a>)</li>
<li>fix(checkpoint): widen <code>Store</code> <code>put</code> value
type to <code>Mapping[str, Any]</code> (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8617">#8617</a>)</li>
<li>release(checkpoint): 4.2.0 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8563">#8563</a>)</li>
<li>test(checkpoint-postgres,checkpoint-sqlite): run the conformance
suite (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8537">#8537</a>)</li>
<li>chore: enable RUF100 and clear unused noqa directives (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8546">#8546</a>)</li>
<li>chore(checkpoint-postgres,checkpoint-sqlite): enable PLC0415 lint
rule (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8540">#8540</a>)</li>
<li>chore(deps): bump the minor-and-patch group in
/libs/checkpoint-sqlite with 4 updates (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/8494">#8494</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/afbe7e48b17f62e139b5c6337ab934549e6dc73b"><code>afbe7e4</code></a>
release(checkpoint-postgres): 3.1.3 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9287">#9287</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/1074714c260486900d9bec9b5dd48e13883dd5df"><code>1074714</code></a>
release(checkpoint-sqlite): 3.1.2 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9288">#9288</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/740461ed50f89f867b0c3a1e46fa6d2a4e339cf4"><code>740461e</code></a>
release(sdk-py): 0.4.7 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9289">#9289</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/08a345637fb2bd499f69c90056a22c478a468817"><code>08a3456</code></a>
release(langgraph): 1.2.15 (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9283">#9283</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/b7d053b256157ac6b8e8ff5a0783f8a446a9c89e"><code>b7d053b</code></a>
fix(langgraph): give every message written to a messages DeltaChannel an
id (...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/395c145c047aaf962a4f599582bbc8d717bd9fa1"><code>395c145</code></a>
fix(langgraph): save a cached node's writes like a node that ran (<a
href="https://redirect.github.com/langchain-ai/langgraph/issues/9259">#9259</a>)</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/10f1fabb3b2f2d550b8133fba1217391c27694c4"><code>10f1fab</code></a>
fix(langgraph): stop at an interrupt_before breakpoint when a drain is
reques...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/799c2f04fd3a56186e24e9fb8bbf3568e543cfbe"><code>799c2f0</code></a>
fix(langgraph): keep an exit run from a checkpoint the thread moved past
out ...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/b4991f1ba36a2449ab59c38ab71828ee865bff84"><code>b4991f1</code></a>
fix(langgraph): store an update_state input's writes on the checkpoint
it upd...</li>
<li><a
href="https://github.com/langchain-ai/langgraph/commit/6aa0afba682b0308fbcace31f66fb532978d4dca"><code>6aa0afb</code></a>
fix(checkpoint): default InMemorySaver delta history to the latest
checkpoint...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langgraph/compare/checkpointsqlite==3.1.1...checkpointpostgres==3.1.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langgraph-checkpoint-postgres&package-manager=uv&previous-version=3.1.1&new-version=3.1.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 <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-10-26 00:09:10 +09:00
dependabot[bot] 7bf29e9798 chore(deps): bump virtualenv from 21.7.13 to 21.14.13 in /libs/cli (#9427)
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.7.13 to
21.14.13.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pypa/virtualenv/releases">virtualenv's
releases</a>.</em></p>
<blockquote>
<h2>21.14.13</h2>
<h1>Bugfixes - 21.14.13</h1>
<ul>
<li>
<p>Stop bash running commands from <code>--prompt</code> or an
environment name containing <code>$(...)</code>, backticks or a
backslash
after <code>shopt -s promptvars</code>, <code>set -o posix</code>, or in
a child shell that inherits an exported <code>PS1</code>
(GHSA-cx6f-vjhg-g3cc); reported by rozzkml. zsh now draws the prompt
from <code>psvar[9]</code>, which is never parsed
as shell syntax.</p>
<p>Write the <code>.venv</code> redirect and the wheel
<code>RECORD</code> with exclusive create, so a link planted after the
existence
check cannot redirect the write. On POSIX, a read-only
<code>.venv</code> redirect that points at a virtualenv is replaced.
(<a
href="https://redirect.github.com/pypa/virtualenv/issues/3466">#3466</a>)</p>
</li>
</ul>
<p><a href="https://pypi.org/project/virtualenv/21.14.13/">PyPI
release</a></p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pypa/virtualenv/compare/21.14.12...21.14.13">https://github.com/pypa/virtualenv/compare/21.14.12...21.14.13</a></p>
<h2>21.14.12</h2>
<h1>Bugfixes - 21.14.12</h1>
<ul>
<li>
<p>Upgrade embedded wheels:</p>
<ul>
<li>setuptools to <code>84.2.1</code> from <code>84.0.0</code></li>
<li>pip to <code>26.2.2</code> from <code>26.2.1</code> (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3452">#3452</a>)</li>
</ul>
</li>
</ul>
<h1>Improved Documentation - 21.14.12</h1>
<ul>
<li>Require a pull request news fragment and a matching changelog
section before publishing. (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3445">#3445</a>)</li>
</ul>
<p><a href="https://pypi.org/project/virtualenv/21.14.12/">PyPI
release</a></p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/pypa/virtualenv/compare/21.14.11...21.14.12">https://github.com/pypa/virtualenv/compare/21.14.11...21.14.12</a></p>
<h2>21.14.11</h2>
<h1>Bugfixes - 21.14.11</h1>
<ul>
<li>Search all directories supplied by repeated
<code>--extra-search-dir</code> options instead of keeping only the last
group.
(<a
href="https://redirect.github.com/pypa/virtualenv/issues/3440">#3440</a>)</li>
<li>On Windows, the app data is now migrated into the nested cache
directory instead of the migration failing with a
warning on every run. (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3441">#3441</a>)</li>
</ul>
<p><a href="https://pypi.org/project/virtualenv/21.14.11/">PyPI
release</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst">virtualenv's
changelog</a>.</em></p>
<blockquote>
<h1>Bugfixes - 21.14.13</h1>
<ul>
<li>
<p>Stop bash running commands from <code>--prompt</code> or an
environment name containing <code>$(...)</code>, backticks or a
backslash
after <code>shopt -s promptvars</code>, <code>set -o posix</code>, or in
a child shell that inherits an exported <code>PS1</code>
(GHSA-cx6f-vjhg-g3cc); reported by :user:<code>rozzkml</code>. zsh now
draws the prompt from <code>psvar[9]</code>, which is never parsed
as shell syntax.</p>
<p>Write the <code>.venv</code> redirect and the wheel
<code>RECORD</code> with exclusive create, so a link planted after the
existence
check cannot redirect the write. On POSIX, a read-only
<code>.venv</code> redirect that points at a virtualenv is replaced.
(:issue:<code>3466</code>)</p>
</li>
</ul>
<hr />
<p>v21.14.12 (2026-10-21)</p>
<hr />
<h1>Bugfixes - 21.14.12</h1>
<ul>
<li>
<p>Upgrade embedded wheels:</p>
<ul>
<li>setuptools to <code>84.2.1</code> from <code>84.0.0</code></li>
<li>pip to <code>26.2.2</code> from <code>26.2.1</code>
(:issue:<code>3452</code>)</li>
</ul>
</li>
</ul>
<h1>Improved Documentation - 21.14.12</h1>
<ul>
<li>Require a pull request news fragment and a matching changelog
section before publishing. (:issue:<code>3445</code>)</li>
</ul>
<hr />
<p>v21.14.11 (2026-10-18)</p>
<hr />
<h1>Bugfixes - 21.14.11</h1>
<ul>
<li>Search all directories supplied by repeated
<code>--extra-search-dir</code> options instead of keeping only the last
group.
(:issue:<code>3440</code>)</li>
<li>On Windows, the app data is now migrated into the nested cache
directory instead of the migration failing with a
warning on every run. (:issue:<code>3441</code>)</li>
</ul>
<hr />
<p>v21.14.10 (2026-10-17)</p>
<hr />
<h1>Bugfixes - 21.14.10</h1>
<ul>
<li>Always copy seed packages when app-data is temporary, including when
symlink seeding is enabled through an environment
variable or configuration file. Seeded packages remain available after
temporary cache cleanup. (:issue:<code>3437</code>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pypa/virtualenv/commit/6accbd5ee857f6d7a5a59b1bd3db25cd5f04174a"><code>6accbd5</code></a>
release 21.14.13</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/3598e201f6f044e389136b2347a8a919ac03c5f4"><code>3598e20</code></a>
🐛 fix(bash): escape the prompt for every PS1 state (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3466">#3466</a>)</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/f6512a559dc5a4561b8364881aee0f8ea7341e9b"><code>f6512a5</code></a>
release 21.14.12</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/b5357267d81fcb95672fa2fe8f33108de4b04a33"><code>b535726</code></a>
[pre-commit.ci] pre-commit autoupdate (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3455">#3455</a>)</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/7e32366ab551514994a7d455ffb203208ecdc08b"><code>7e32366</code></a>
Fix broken pip and code review links in the docs (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3460">#3460</a>)</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/632e56fc92d8f64da72922f3e9da569491bc52c7"><code>632e56f</code></a>
Upgrade embedded pip/setuptools/wheel and CI test tools (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3452">#3452</a>)</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/a8bd1a916a5629859307c153bfc1924e56ba969c"><code>a8bd1a9</code></a>
chore(deps): bump certifi from 2026.7.22 to 2026.10.19 in /tasks (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3462">#3462</a>)</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/c82323bb7140ed678b2485e00c3785525f4e91af"><code>c82323b</code></a>
chore(deps): bump uv from 0.13.0 to 0.13.4 in /tasks (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3461">#3461</a>)</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/863ae6ccfe1878e77b6f00074f3fd91664c8c0aa"><code>863ae6c</code></a>
chore(deps): bump the runtime-dependencies group with 3 updates (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3456">#3456</a>)</li>
<li><a
href="https://github.com/pypa/virtualenv/commit/79537b3461bf150d3eb439347f2858b8ecd504f8"><code>79537b3</code></a>
👷 ci: pin App token action v3.3.0 (<a
href="https://redirect.github.com/pypa/virtualenv/issues/3449">#3449</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pypa/virtualenv/compare/21.7.13...21.14.13">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=virtualenv&package-manager=uv&previous-version=21.7.13&new-version=21.14.13)](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-10-25 10:03:59 +00:00
dependabot[bot]andJohn Kennedy 80133010ac chore(deps): bump opentelemetry-api from 1.39.1 to 1.45.1 in /libs/langgraph (#9424)
Bumps
[opentelemetry-api](https://github.com/open-telemetry/opentelemetry-python)
from 1.39.1 to 1.45.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-python/releases">opentelemetry-api's
releases</a>.</em></p>
<blockquote>
<h2>Version 1.45.1/0.66b1</h2>
<p>This is a patch release on the previous 1.45.0/0.66b0 release, fixing
the issue(s) below.</p>
<h3>Fixed</h3>
<ul>
<li><code>opentelemetry-api</code>: make <code>AnyValue</code> and
<code>AttributeValue</code> explicit type aliases (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5703">#5703</a>)</li>
<li><code>opentelemetry-sdk</code>: fix thread leak bug in resource
initialization logic for long running resource detectors (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5706">#5706</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: restore
<code>requests</code> as the default HTTP transport so proxy environment
variables such as <code>HTTPS_PROXY</code> are honored again (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5714">#5714</a>)</li>
<li><code>opentelemetry-api</code>: fix quadratic time parsing of the
<code>tracestate</code> header and discard <code>tracestate</code>
headers longer than 8192 characters (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5746">#5746</a>)</li>
</ul>
<h2>Version 1.45.0/0.66b0</h2>
<h3>Added</h3>
<ul>
<li><code>opentelemetry-exporter-prometheus</code>: add support to
configure Resource attributes as metric labels (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5122">#5122</a>)</li>
<li>infra: add renovate (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5202">#5202</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
support for extended attribute values everywhere. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5266">#5266</a>)</li>
<li><code>opentelemetry-sdk</code>: wire the top-level
<code>log_level</code> field in declarative configuration — when set,
maps the OTel <code>SeverityNumber</code> value to a Python logging
level and applies it to the <code>opentelemetry</code> logger so SDK
internal diagnostics respect the configured severity. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5351">#5351</a>)</li>
<li><code>opentelemetry-sdk</code>: add the new stable
<code>AlwaysRecordSampler</code> (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5354">#5354</a>)</li>
<li><code>opentelemetry-configuration</code>,
<code>opentelemetry-sdk</code>: wire top-level
<code>attribute_limits</code> into per-signal providers via declarative
config; add <code>log_record_limits</code> support to
<code>LoggerProvider</code> (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5365">#5365</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-http</code>: add OTLP JSON
HTTP exporter package (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5374">#5374</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
<code>enabled()</code> support to the Logger API, SDK, and
<code>LogRecordProcessor</code> to let instrumentation skip expensive
work when logging is disabled (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5380">#5380</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-file</code>: add OTLP JSON
file Docker tests (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5412">#5412</a>)</li>
<li><code>opentelemetry-configuration</code>: wire the experimental
<code>tracer_configurator/development</code>,
<code>meter_configurator/development</code> and
<code>logger_configurator/development</code> fields into
<code>create_tracer_provider</code>, <code>create_meter_provider</code>
and <code>create_logger_provider</code>, so per-instrumentation-scope
<code>enabled</code> overrides declared in the config file are applied
to the provider (previously these fields were parsed but silently
discarded). The logger
<code>minimum_severity</code>/<code>trace_based</code> fields are not
supported by the Python SDK and are ignored with a warning. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5418">#5418</a>)</li>
<li><code>docs/examples</code>: add example on how to manually setup the
SDK to get SDK metrics (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5449">#5449</a>)</li>
<li><code>opentelemetry-docker-tests</code>: add Prometheus exporter
docker tests (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5457">#5457</a>)</li>
<li><code>opentelemetry-sdk</code>: count records dropped after shutdown
on <code>otel.sdk.processor.{span,log}.processed</code> with
<code>error.type=already_shutdown</code> (batch span/log and simple log
processors), which the semantic conventions define as a valid value for
this metric. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5509">#5509</a>)</li>
<li><code>opentelemetry-semantic-conventions</code>: update semantic
conventions to v1.44.0 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5511">#5511</a>)</li>
<li><code>opentelemetry-sdk</code>: add <code>host.id</code> to the host
resource detector (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5653">#5653</a>)</li>
<li><code>opentelemetry-test-utils</code>: add
<code>CapturingSampler</code> to record what samplers receive in
instrumentation tests (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5681">#5681</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Enable <code>PIE</code> (flake8-pie) ruff rule and fix all
violations (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5150">#5150</a>)</li>
<li>The public <code>opentelemetry.util.types.AttributeValue</code> type
in package <code>opentelemetry-api</code> is being expanded to include
<code>None</code>, heterogeneous sequences of primitive types (and
nested sequences) as opposed to only homogeneous primitive sequences,
and Mappings of strings to any primitive types or sequences/mappings
(which themselves must only contain primitive types or
sequences/mappings validated the same way). If a <code>bytes</code> type
is set as an attribute value in the SDK, it will no longer be utf-8
decoded to a string, instead it will be passed along as is in accordance
with the OTEL spec, since <code>bytes</code> is a valid type in the OTLP
proto. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5266">#5266</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: add a
<code>max_request_size</code> argument to the OTLP HTTP exporters
(traces, logs, metrics); serialized requests larger than the limit are
dropped before sending, measured before compression. Defaults to 64 MiB
(enabled); set to 0 to disable. Mirrors opentelemetry-go#8157. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5369">#5369</a>)</li>
<li>[BREAKING] <code>opentelemetry-api</code>: subclasses of
<code>Logger</code> need to implement the <code>enabled</code> method
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5380">#5380</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: refactor to use
shared opentelemetry-exporter-otlp-common and
opentelemetry-exporter-http-transport packages and switch default HTTP
backend to urllib3 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5389">#5389</a>)</li>
<li><code>opentelemetry-sdk</code>: unify logging force_flush timeout
defaults to 30000ms (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5438">#5438</a>)</li>
<li><code>opentelemetry-python</code>: enable Ruff default ruleset and
fix auto-fixable lint issues (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5491">#5491</a>)</li>
<li><code>opentelemetry-sdk</code>: <code>SimpleSpanProcessor</code> now
drops spans ended after <code>shutdown()</code> instead of passing them
to the exporter, and counts them on
<code>otel.sdk.processor.span.processed</code> with
<code>error.type=already_shutdown</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5512">#5512</a>)</li>
<li>Bump pytest to 9.0.3 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5518">#5518</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: clarify that
the <code>endpoint=</code> kwarg requires the full signal path (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5633">#5633</a>)</li>
<li><code>opentelemetry-sdk</code>: fix typos in SpanLimits docstring
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5658">#5658</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>opentelemetry-configuration</code>: perform environment
variable substitution on scalar values after parsing the configuration
file, so <code>${VAR}</code> references inside comments and mapping keys
are no longer substituted and undefined references in comments no longer
abort loading (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5407">#5407</a>)</li>
<li><code>opentelemetry-configuration</code>: declarative config
environment variable substitution now replaces an unset variable that
has no default with an empty value instead of raising an error, per the
configuration spec. Resource attributes whose value resolves to null (an
unset <code>${VAR}</code> with no default) are skipped with a warning
instead of being inserted as a null value. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5408">#5408</a>)</li>
<li>'scripts/build.sh<code>: add
</code>opentelemetry-configuration<code>and</code>opentelemetry-proto-json`
to the package to release (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5425">#5425</a>)</li>
<li><code>opentelemetry-sdk</code>: fix <code>View</code>
instrument-name matching so a view configured with an instrument's real
(mixed-case) name is applied; matching is now case-insensitive and
platform-independent instead of relying on <code>fnmatch</code>'s
OS-dependent case handling (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5430">#5430</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md">opentelemetry-api's
changelog</a>.</em></p>
<blockquote>
<h2>Version 1.45.1/0.66b1 (2026-10-06)</h2>
<h3>Fixed</h3>
<ul>
<li><code>opentelemetry-api</code>: make <code>AnyValue</code> and
<code>AttributeValue</code> explicit type
aliases
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5703">#5703</a>)</li>
<li><code>opentelemetry-sdk</code>: fix thread leak bug in resource
initialization logic for
long running resource detectors
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5706">#5706</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: restore
<code>requests</code> as the default
HTTP transport so proxy environment variables such as
<code>HTTPS_PROXY</code> are
honored again
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5714">#5714</a>)</li>
<li><code>opentelemetry-api</code>: fix quadratic time parsing of the
<code>tracestate</code> header
and discard <code>tracestate</code> headers longer than 8192 characters
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5746">#5746</a>)</li>
</ul>
<h2>Version 1.45.0/0.66b0 (2026-09-25)</h2>
<h3>Added</h3>
<ul>
<li><code>opentelemetry-exporter-prometheus</code>: add support to
configure Resource
attributes as metric labels
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5122">#5122</a>)</li>
<li>infra: add renovate
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5202">#5202</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
support for extended attribute
values everywhere.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5266">#5266</a>)</li>
<li><code>opentelemetry-sdk</code>: wire the top-level
<code>log_level</code> field in declarative
configuration — when set, maps the OTel <code>SeverityNumber</code>
value to a Python
logging level and applies it to the <code>opentelemetry</code> logger so
SDK internal
diagnostics respect the configured severity.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5351">#5351</a>)</li>
<li><code>opentelemetry-sdk</code>: add the new stable
<code>AlwaysRecordSampler</code>
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5354">#5354</a>)</li>
<li><code>opentelemetry-configuration</code>,
<code>opentelemetry-sdk</code>: wire top-level
<code>attribute_limits</code> into per-signal providers via declarative
config; add
<code>log_record_limits</code> support to <code>LoggerProvider</code>
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5365">#5365</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-http</code>: add OTLP JSON
HTTP exporter package
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5374">#5374</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
<code>enabled()</code> support to the
Logger API, SDK, and <code>LogRecordProcessor</code> to let
instrumentation skip
expensive work when logging is disabled
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5380">#5380</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-file</code>: add OTLP JSON
file Docker tests
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5412">#5412</a>)</li>
<li><code>opentelemetry-configuration</code>: wire the experimental</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/14971cef9c84c2354cbaf6871090896fad0f9a1a"><code>14971ce</code></a>
Prepare release 1.45.1/0.66b1 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5752">#5752</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/44ac26edcee5cc99e03a65d6a1a9978775703e28"><code>44ac26e</code></a>
opentelemetry-sdk: fix thread leak bug in resource initialization logic
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/570">#570</a>...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/a2ec3f91f1c2b735ccba177eaa77d6019a4428ae"><code>a2ec3f9</code></a>
[release/v1.45.x-0.66bx] opentelemetry-exporter-otlp-proto-http: restore
requ...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/d5224bf4ffdf2aee1b592ad238dcc32ca957f26b"><code>d5224bf</code></a>
opentelemetry-api: improve performance of tracestate parsing (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5746">#5746</a>)
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5749">#5749</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/db61173a56475d4fa1276d16182e46b43db83b9e"><code>db61173</code></a>
Declare AnyValue and AttributeValue as explicit type aliases (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5703">#5703</a>)
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5750">#5750</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/4f0fcfa53e53a8c654a34df0d0a1fd74483ebca2"><code>4f0fcfa</code></a>
Prepare release 1.45.0/0.66b0 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5690">#5690</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/9406f34ca29d78ad760658ccb9f37b7b6c8e58e9"><code>9406f34</code></a>
opentelemetry-test-utils: add CapturingSampler for instrumentation tests
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5681">#5681</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/1fe31a9bd00c88c824060506aed49b0611fe6e9c"><code>1fe31a9</code></a>
fix: update W3CBaggagePropagator to properly handle whitespace (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5680">#5680</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/008b5b0a06c521203b43842c2604acfce9a2b534"><code>008b5b0</code></a>
feat(config): wire top-level attribute_limits into per-signal providers
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5365">#5365</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/edfad0cafec718ff0f635232390e7f37e2460ab1"><code>edfad0c</code></a>
[opentelemetry-sdk] Fix overwriting of the service.instance.id which has
been...</li>
<li>Additional commits viewable in <a
href="https://github.com/open-telemetry/opentelemetry-python/compare/v1.39.1...v1.45.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=opentelemetry-api&package-manager=uv&previous-version=1.39.1&new-version=1.45.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>
Co-authored-by: John Kennedy <65985482+jkennedyvz@users.noreply.github.com>
2026-10-25 10:03:11 +00:00
dependabot[bot] 4136a6b763 chore(deps): bump setuptools from 83.0.0 to 84.1.0 in /libs/cli (#9425)
Bumps [setuptools](https://github.com/pypa/setuptools) from 83.0.0 to
84.1.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pypa/setuptools/blob/main/NEWS.rst">setuptools's
changelog</a>.</em></p>
<blockquote>
<h1>v84.1.0</h1>
<h2>Features</h2>
<ul>
<li><code>setuptools.archive_util</code> now raises the new
<code>UnsafeMember</code> exception when
an archive member would be extracted outside of the extraction
directory,
where previously such a member was silently skipped. Aborting makes a
malicious or malformed archive visible to the caller instead of yielding
a
quietly incomplete extraction, and matches the behavior of the standard
library's <code>tarfile</code> extraction filters.</li>
</ul>
<h2>Bugfixes</h2>
<ul>
<li>
<p><code>Wheel.install_as_egg</code> no longer creates namespace package
directories
outside of the destination egg directory. Entries in a wheel's
<code>namespace_packages.txt</code> were split on <code>.</code> and
passed straight to
<code>os.path.join</code>, so an entry that was absolute,
drive-qualified, or UNC
discarded the destination and placed an <code>__init__.py</code> at a
path chosen by
the wheel. Namespace entries are now validated as dotted module names on
every platform -- see GHSA-xmj3-9gf7-h52w.</p>
<p>This code path is reached only by the deprecated
<code>setup_requires</code>
mechanism when <code>setup()</code> is invoked outside a PEP 517
frontend; the
setuptools build backend never reaches it.</p>
</li>
<li>
<p><code>setuptools.archive_util</code> no longer extracts archive
members outside of the
requested extraction directory. Both the tar and zip formats specify
<code>/</code> as
the only path separator, but the guard against <code>..</code>
components split member
names on <code>/</code> alone, so a name such as
<code>..\escaped.txt</code> was treated as a
single component and then resolved as a traversal by the filesystem on
Windows. Member names containing a backslash, a drive letter, or a UNC
prefix
are now rejected on every platform, as are members whose resolved
destination
falls outside the extraction directory -- see GHSA-grgh-hr87-3jpw.</p>
</li>
</ul>
<h1>v84.0.0</h1>
<h2>Features</h2>
<ul>
<li>Newline-separated <code>keywords</code> and
<code>platforms</code><code>pypa/setuptools#4887</code><code>old
specification &lt;https://peps.python.org/pep-0345/&gt;</code>_
separated items with spaces and the current one uses commas. (<a
href="https://redirect.github.com/pypa/setuptools/issues/4887">#4887</a>)</li>
<li><code>Extension</code><code>pypa/distutils#373</code><a
href="https://redirect.github.com/pypa/setuptools/issues/5022">#5022</a>)</li>
<li>The C compiler modules now emit log messages through their own
<code>compilers.C.*</code> loggers instead of the distutils root logger,
part of decoupling the compilers package from distutils. The logger
names are normalized to a stable <code>compilers.C.*</code> prefix so
they remain constant as the package migrates toward a standalone
<code>compilers.C</code> distribution. (<a
href="https://redirect.github.com/pypa/setuptools/issues/5266">#5266</a>)</li>
<li>The C compilers gained a <code>Compiler.call</code> method -- a thin
wrapper over <code>subprocess.check_call</code> (with macOS
deployment-target env injection) that is the modern replacement for
<code>Compiler.spawn</code>. The compilers no longer depend on
<code>distutils.spawn</code>, <code>distutils.dir_util</code>,
<code>distutils.file_util</code>, <code>distutils._modified</code>, or
<code>distutils.util.execute</code>/<code>split_quoted</code>: the
generic <code>newer</code>/<code>newer_group</code> and
<code>split_quoted</code> helpers are vendored into the
<code>compilers</code> package, and
<code>Compiler.mkpath</code>/<code>move_file</code>/<code>execute</code>
are implemented directly on the standard library
(<code>os.makedirs</code>/<code>shutil.move</code>). The methods are
retained for backward compatibility. (<a
href="https://redirect.github.com/pypa/setuptools/issues/5267">#5267</a>)</li>
<li>The compilers no longer depend on <code>distutils.util</code>,
<code>distutils.version</code>, <code>distutils.compat</code>, or
<code>distutils._macos_compat</code>. The platform-identification
helpers
(<code>get_platform</code>/<code>get_host_platform</code>/<code>is_mingw</code>)
now live in <code>distutils.compilers.platform.detect</code> and the
macOS deployment-target logic and <code>compiler_fixup</code> in
<code>distutils.compilers.platform.macos</code>;
<code>CygwinCCompiler.gcc_version</code> returns a
<code>packaging.version.Version</code>. <code>distutils.util</code>
re-exports the platform/macOS helpers from their new homes for backward
compatibility rather than keeping duplicate copies. (sysconfig lookups
still route through distutils pending its own decoupling.) (<a
href="https://redirect.github.com/pypa/setuptools/issues/5268">#5268</a>)</li>
<li>The compilers now read their build configuration from the standard
library's <code>sysconfig</code> instead of
<code>distutils.sysconfig</code>. Per-compiler customization --
previously <code>distutils.sysconfig.customize_compiler</code> -- has
moved into <code>Compiler.configure_system()</code>: a no-op on the base
class, with <code>UnixCCompiler</code> applying the
compiler/flag/archiver settings CPython recorded in
<code>sysconfig</code> (and the usual
<code>CC</code>/<code>CFLAGS</code>/<code>LDSHARED</code>/… environment
overrides). <code>distutils.sysconfig.customize_compiler</code> is
retained as a thin wrapper that calls
<code>compiler.configure_system()</code>. (<a
href="https://redirect.github.com/pypa/setuptools/issues/5269">#5269</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pypa/setuptools/commit/77b7ace0aaf660beeb9b3f9da4aabf21cbcf3fa9"><code>77b7ace</code></a>
Bump version: 84.0.0 → 84.1.0</li>
<li><a
href="https://github.com/pypa/setuptools/commit/e66004c64eaf6cf2f8c53ccdfac75cf7abce7ccf"><code>e66004c</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/setuptools/issues/5329">#5329</a>
from pypa/fix/wheel-namespace-path-escape</li>
<li><a
href="https://github.com/pypa/setuptools/commit/54b5dcadc1bd938a5a66919e8ab7bcd59805e7fd"><code>54b5dca</code></a>
Cover the containment check with a symlink escape test</li>
<li><a
href="https://github.com/pypa/setuptools/commit/6d07b8d17890dda58d944ac51e58f54af92d52af"><code>6d07b8d</code></a>
Validate namespace package names during legacy wheel installation</li>
<li><a
href="https://github.com/pypa/setuptools/commit/8419f006d4e34c4ecfc7a0bd1bc6993855adc3cc"><code>8419f00</code></a>
Merge pull request <a
href="https://redirect.github.com/pypa/setuptools/issues/5356">#5356</a>
from pypa/skeleton-ruff-0.16</li>
<li><a
href="https://github.com/pypa/setuptools/commit/6dadc86a3f7dbe1cf49c6301548ffde61d4e74e1"><code>6dadc86</code></a>
Remove DTZ011 suppressions that Ruff 0.17 reports as unused.</li>
<li><a
href="https://github.com/pypa/setuptools/commit/715ec38b46424533806195a96983e3dd70fdf3b9"><code>715ec38</code></a>
Merge <a
href="https://github.com/jaraco/skeleton">https://github.com/jaraco/skeleton</a></li>
<li><a
href="https://github.com/pypa/setuptools/commit/9695460ebfa879ada16545d62b26b9a489a6d5ed"><code>9695460</code></a><code>jaraco/skeleton#219</code></li>
<li><a
href="https://github.com/pypa/setuptools/commit/9e0812976b1154a7aece47188814385c887bc6c9"><code>9e08129</code></a><code>jaraco/skeleton#216</code></li>
<li><a
href="https://github.com/pypa/setuptools/commit/4e612f9e44ecfad130d0b2a27a2dce0740599b4b"><code>4e612f9</code></a>
Ignore PyPy's deprecated check_home warning in tests</li>
<li>Additional commits viewable in <a
href="https://github.com/pypa/setuptools/compare/v83.0.0...v84.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=uv&previous-version=83.0.0&new-version=84.1.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-10-25 02:56:36 -07:00
dependabot[bot] 4f5740e53e chore(deps): bump nbconvert from 7.17.1 to 7.17.2 in /libs/langgraph (#9426)
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.17.1 to
7.17.2.
<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.2</h2>
<h2>7.17.2</h2>
<p>(<a
href="https://github.com/jupyter/nbconvert/compare/v7.17.1...8685e15e2d924fa39be38aeafb6ac859fc254ccc">Full
Changelog</a>)</p>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Fix CI: fetch the classic CSS from PyPI, update dead links <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2327">#2327</a>
(<a href="https://github.com/Yann-P"><code>@​Yann-P</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/claude"><code>@​claude</code></a>)</li>
</ul>
<h3>Other merged PRs</h3>
<ul>
<li>Use org-level security policy (jupyter/.github/SECURITY.md) <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2318">#2318</a>
(<a href="https://github.com/Yann-P"><code>@​Yann-P</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</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-04-08&amp;to=2026-10-12&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/Carreau"><code>@​Carreau</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3ACarreau+updated%3A2026-04-08..2026-10-12&amp;type=Issues">activity</a>)
| <a href="https://github.com/claude"><code>@​claude</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Aclaude+updated%3A2026-04-08..2026-10-12&amp;type=Issues">activity</a>)
| <a
href="https://github.com/GruffElixir"><code>@​GruffElixir</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AGruffElixir+updated%3A2026-04-08..2026-10-12&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-04-08..2026-10-12&amp;type=Issues">activity</a>)
| <a href="https://github.com/Yann-P"><code>@​Yann-P</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AYann-P+updated%3A2026-04-08..2026-10-12&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.2</h2>
<p>(<a
href="https://github.com/jupyter/nbconvert/compare/v7.17.1...8685e15e2d924fa39be38aeafb6ac859fc254ccc">Full
Changelog</a>)</p>
<h3>Maintenance and upkeep improvements</h3>
<ul>
<li>Fix CI: fetch the classic CSS from PyPI, update dead links <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2327">#2327</a>
(<a href="https://github.com/Yann-P"><code>@​Yann-P</code></a>, <a
href="https://github.com/Carreau"><code>@​Carreau</code></a>, <a
href="https://github.com/claude"><code>@​claude</code></a>)</li>
</ul>
<h3>Other merged PRs</h3>
<ul>
<li>Use org-level security policy (jupyter/.github/SECURITY.md) <a
href="https://redirect.github.com/jupyter/nbconvert/pull/2318">#2318</a>
(<a href="https://github.com/Yann-P"><code>@​Yann-P</code></a>, <a
href="https://github.com/minrk"><code>@​minrk</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-04-08&amp;to=2026-10-12&amp;type=c">GitHub
contributors page for this release</a>)</p>
<p><a href="https://github.com/Carreau"><code>@​Carreau</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3ACarreau+updated%3A2026-04-08..2026-10-12&amp;type=Issues">activity</a>)
| <a href="https://github.com/claude"><code>@​claude</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3Aclaude+updated%3A2026-04-08..2026-10-12&amp;type=Issues">activity</a>)
| <a
href="https://github.com/GruffElixir"><code>@​GruffElixir</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AGruffElixir+updated%3A2026-04-08..2026-10-12&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-04-08..2026-10-12&amp;type=Issues">activity</a>)
| <a href="https://github.com/Yann-P"><code>@​Yann-P</code></a> (<a
href="https://github.com/search?q=repo%3Ajupyter%2Fnbconvert+involves%3AYann-P+updated%3A2026-04-08..2026-10-12&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/e681152f214b65387f875aad96a4726994533a0b"><code>e681152</code></a>
Publish 7.17.2</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/8685e15e2d924fa39be38aeafb6ac859fc254ccc"><code>8685e15</code></a>
Fix CI: fetch the classic CSS from PyPI, update dead links</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/ce4bd99aec31d81d759fa8eeb8643179392ec66f"><code>ce4bd99</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/jupyter/nbconvert/commit/1ff189e1b30f0820238e95427c035a9150a5e43f"><code>1ff189e</code></a>
Use org-level security policy (jupyter/.github/SECURITY.md) (<a
href="https://redirect.github.com/jupyter/nbconvert/issues/2318">#2318</a>)</li>
<li>See full diff in <a
href="https://github.com/jupyter/nbconvert/compare/v7.17.1...v7.17.2">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.1&new-version=7.17.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-10-25 02:55:35 -07:00
dependabot[bot] 225328f2f9 chore(deps): bump opentelemetry-api from 1.41.0 to 1.45.1 in /libs/cli (#9423)
Bumps
[opentelemetry-api](https://github.com/open-telemetry/opentelemetry-python)
from 1.41.0 to 1.45.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-python/releases">opentelemetry-api's
releases</a>.</em></p>
<blockquote>
<h2>Version 1.45.1/0.66b1</h2>
<p>This is a patch release on the previous 1.45.0/0.66b0 release, fixing
the issue(s) below.</p>
<h3>Fixed</h3>
<ul>
<li><code>opentelemetry-api</code>: make <code>AnyValue</code> and
<code>AttributeValue</code> explicit type aliases (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5703">#5703</a>)</li>
<li><code>opentelemetry-sdk</code>: fix thread leak bug in resource
initialization logic for long running resource detectors (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5706">#5706</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: restore
<code>requests</code> as the default HTTP transport so proxy environment
variables such as <code>HTTPS_PROXY</code> are honored again (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5714">#5714</a>)</li>
<li><code>opentelemetry-api</code>: fix quadratic time parsing of the
<code>tracestate</code> header and discard <code>tracestate</code>
headers longer than 8192 characters (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5746">#5746</a>)</li>
</ul>
<h2>Version 1.45.0/0.66b0</h2>
<h3>Added</h3>
<ul>
<li><code>opentelemetry-exporter-prometheus</code>: add support to
configure Resource attributes as metric labels (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5122">#5122</a>)</li>
<li>infra: add renovate (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5202">#5202</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
support for extended attribute values everywhere. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5266">#5266</a>)</li>
<li><code>opentelemetry-sdk</code>: wire the top-level
<code>log_level</code> field in declarative configuration — when set,
maps the OTel <code>SeverityNumber</code> value to a Python logging
level and applies it to the <code>opentelemetry</code> logger so SDK
internal diagnostics respect the configured severity. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5351">#5351</a>)</li>
<li><code>opentelemetry-sdk</code>: add the new stable
<code>AlwaysRecordSampler</code> (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5354">#5354</a>)</li>
<li><code>opentelemetry-configuration</code>,
<code>opentelemetry-sdk</code>: wire top-level
<code>attribute_limits</code> into per-signal providers via declarative
config; add <code>log_record_limits</code> support to
<code>LoggerProvider</code> (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5365">#5365</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-http</code>: add OTLP JSON
HTTP exporter package (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5374">#5374</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
<code>enabled()</code> support to the Logger API, SDK, and
<code>LogRecordProcessor</code> to let instrumentation skip expensive
work when logging is disabled (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5380">#5380</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-file</code>: add OTLP JSON
file Docker tests (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5412">#5412</a>)</li>
<li><code>opentelemetry-configuration</code>: wire the experimental
<code>tracer_configurator/development</code>,
<code>meter_configurator/development</code> and
<code>logger_configurator/development</code> fields into
<code>create_tracer_provider</code>, <code>create_meter_provider</code>
and <code>create_logger_provider</code>, so per-instrumentation-scope
<code>enabled</code> overrides declared in the config file are applied
to the provider (previously these fields were parsed but silently
discarded). The logger
<code>minimum_severity</code>/<code>trace_based</code> fields are not
supported by the Python SDK and are ignored with a warning. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5418">#5418</a>)</li>
<li><code>docs/examples</code>: add example on how to manually setup the
SDK to get SDK metrics (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5449">#5449</a>)</li>
<li><code>opentelemetry-docker-tests</code>: add Prometheus exporter
docker tests (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5457">#5457</a>)</li>
<li><code>opentelemetry-sdk</code>: count records dropped after shutdown
on <code>otel.sdk.processor.{span,log}.processed</code> with
<code>error.type=already_shutdown</code> (batch span/log and simple log
processors), which the semantic conventions define as a valid value for
this metric. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5509">#5509</a>)</li>
<li><code>opentelemetry-semantic-conventions</code>: update semantic
conventions to v1.44.0 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5511">#5511</a>)</li>
<li><code>opentelemetry-sdk</code>: add <code>host.id</code> to the host
resource detector (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5653">#5653</a>)</li>
<li><code>opentelemetry-test-utils</code>: add
<code>CapturingSampler</code> to record what samplers receive in
instrumentation tests (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5681">#5681</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Enable <code>PIE</code> (flake8-pie) ruff rule and fix all
violations (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5150">#5150</a>)</li>
<li>The public <code>opentelemetry.util.types.AttributeValue</code> type
in package <code>opentelemetry-api</code> is being expanded to include
<code>None</code>, heterogeneous sequences of primitive types (and
nested sequences) as opposed to only homogeneous primitive sequences,
and Mappings of strings to any primitive types or sequences/mappings
(which themselves must only contain primitive types or
sequences/mappings validated the same way). If a <code>bytes</code> type
is set as an attribute value in the SDK, it will no longer be utf-8
decoded to a string, instead it will be passed along as is in accordance
with the OTEL spec, since <code>bytes</code> is a valid type in the OTLP
proto. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5266">#5266</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: add a
<code>max_request_size</code> argument to the OTLP HTTP exporters
(traces, logs, metrics); serialized requests larger than the limit are
dropped before sending, measured before compression. Defaults to 64 MiB
(enabled); set to 0 to disable. Mirrors opentelemetry-go#8157. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5369">#5369</a>)</li>
<li>[BREAKING] <code>opentelemetry-api</code>: subclasses of
<code>Logger</code> need to implement the <code>enabled</code> method
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5380">#5380</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: refactor to use
shared opentelemetry-exporter-otlp-common and
opentelemetry-exporter-http-transport packages and switch default HTTP
backend to urllib3 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5389">#5389</a>)</li>
<li><code>opentelemetry-sdk</code>: unify logging force_flush timeout
defaults to 30000ms (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5438">#5438</a>)</li>
<li><code>opentelemetry-python</code>: enable Ruff default ruleset and
fix auto-fixable lint issues (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5491">#5491</a>)</li>
<li><code>opentelemetry-sdk</code>: <code>SimpleSpanProcessor</code> now
drops spans ended after <code>shutdown()</code> instead of passing them
to the exporter, and counts them on
<code>otel.sdk.processor.span.processed</code> with
<code>error.type=already_shutdown</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5512">#5512</a>)</li>
<li>Bump pytest to 9.0.3 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5518">#5518</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: clarify that
the <code>endpoint=</code> kwarg requires the full signal path (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5633">#5633</a>)</li>
<li><code>opentelemetry-sdk</code>: fix typos in SpanLimits docstring
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5658">#5658</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>opentelemetry-configuration</code>: perform environment
variable substitution on scalar values after parsing the configuration
file, so <code>${VAR}</code> references inside comments and mapping keys
are no longer substituted and undefined references in comments no longer
abort loading (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5407">#5407</a>)</li>
<li><code>opentelemetry-configuration</code>: declarative config
environment variable substitution now replaces an unset variable that
has no default with an empty value instead of raising an error, per the
configuration spec. Resource attributes whose value resolves to null (an
unset <code>${VAR}</code> with no default) are skipped with a warning
instead of being inserted as a null value. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5408">#5408</a>)</li>
<li>'scripts/build.sh<code>: add
</code>opentelemetry-configuration<code>and</code>opentelemetry-proto-json`
to the package to release (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5425">#5425</a>)</li>
<li><code>opentelemetry-sdk</code>: fix <code>View</code>
instrument-name matching so a view configured with an instrument's real
(mixed-case) name is applied; matching is now case-insensitive and
platform-independent instead of relying on <code>fnmatch</code>'s
OS-dependent case handling (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5430">#5430</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md">opentelemetry-api's
changelog</a>.</em></p>
<blockquote>
<h2>Version 1.45.1/0.66b1 (2026-10-06)</h2>
<h3>Fixed</h3>
<ul>
<li><code>opentelemetry-api</code>: make <code>AnyValue</code> and
<code>AttributeValue</code> explicit type
aliases
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5703">#5703</a>)</li>
<li><code>opentelemetry-sdk</code>: fix thread leak bug in resource
initialization logic for
long running resource detectors
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5706">#5706</a>)</li>
<li><code>opentelemetry-exporter-otlp-proto-http</code>: restore
<code>requests</code> as the default
HTTP transport so proxy environment variables such as
<code>HTTPS_PROXY</code> are
honored again
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5714">#5714</a>)</li>
<li><code>opentelemetry-api</code>: fix quadratic time parsing of the
<code>tracestate</code> header
and discard <code>tracestate</code> headers longer than 8192 characters
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5746">#5746</a>)</li>
</ul>
<h2>Version 1.45.0/0.66b0 (2026-09-25)</h2>
<h3>Added</h3>
<ul>
<li><code>opentelemetry-exporter-prometheus</code>: add support to
configure Resource
attributes as metric labels
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5122">#5122</a>)</li>
<li>infra: add renovate
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5202">#5202</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
support for extended attribute
values everywhere.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5266">#5266</a>)</li>
<li><code>opentelemetry-sdk</code>: wire the top-level
<code>log_level</code> field in declarative
configuration — when set, maps the OTel <code>SeverityNumber</code>
value to a Python
logging level and applies it to the <code>opentelemetry</code> logger so
SDK internal
diagnostics respect the configured severity.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5351">#5351</a>)</li>
<li><code>opentelemetry-sdk</code>: add the new stable
<code>AlwaysRecordSampler</code>
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5354">#5354</a>)</li>
<li><code>opentelemetry-configuration</code>,
<code>opentelemetry-sdk</code>: wire top-level
<code>attribute_limits</code> into per-signal providers via declarative
config; add
<code>log_record_limits</code> support to <code>LoggerProvider</code>
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5365">#5365</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-http</code>: add OTLP JSON
HTTP exporter package
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5374">#5374</a>)</li>
<li><code>opentelemetry-api</code>, <code>opentelemetry-sdk</code>: add
<code>enabled()</code> support to the
Logger API, SDK, and <code>LogRecordProcessor</code> to let
instrumentation skip
expensive work when logging is disabled
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5380">#5380</a>)</li>
<li><code>opentelemetry-exporter-otlp-json-file</code>: add OTLP JSON
file Docker tests
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/pull/5412">#5412</a>)</li>
<li><code>opentelemetry-configuration</code>: wire the experimental</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/14971cef9c84c2354cbaf6871090896fad0f9a1a"><code>14971ce</code></a>
Prepare release 1.45.1/0.66b1 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5752">#5752</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/44ac26edcee5cc99e03a65d6a1a9978775703e28"><code>44ac26e</code></a>
opentelemetry-sdk: fix thread leak bug in resource initialization logic
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/570">#570</a>...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/a2ec3f91f1c2b735ccba177eaa77d6019a4428ae"><code>a2ec3f9</code></a>
[release/v1.45.x-0.66bx] opentelemetry-exporter-otlp-proto-http: restore
requ...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/d5224bf4ffdf2aee1b592ad238dcc32ca957f26b"><code>d5224bf</code></a>
opentelemetry-api: improve performance of tracestate parsing (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5746">#5746</a>)
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5749">#5749</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/db61173a56475d4fa1276d16182e46b43db83b9e"><code>db61173</code></a>
Declare AnyValue and AttributeValue as explicit type aliases (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5703">#5703</a>)
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5750">#5750</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/4f0fcfa53e53a8c654a34df0d0a1fd74483ebca2"><code>4f0fcfa</code></a>
Prepare release 1.45.0/0.66b0 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5690">#5690</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/9406f34ca29d78ad760658ccb9f37b7b6c8e58e9"><code>9406f34</code></a>
opentelemetry-test-utils: add CapturingSampler for instrumentation tests
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5681">#5681</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/1fe31a9bd00c88c824060506aed49b0611fe6e9c"><code>1fe31a9</code></a>
fix: update W3CBaggagePropagator to properly handle whitespace (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5680">#5680</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/008b5b0a06c521203b43842c2604acfce9a2b534"><code>008b5b0</code></a>
feat(config): wire top-level attribute_limits into per-signal providers
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-python/issues/5365">#5365</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-python/commit/edfad0cafec718ff0f635232390e7f37e2460ab1"><code>edfad0c</code></a>
[opentelemetry-sdk] Fix overwriting of the service.instance.id which has
been...</li>
<li>Additional commits viewable in <a
href="https://github.com/open-telemetry/opentelemetry-python/compare/v1.41.0...v1.45.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=opentelemetry-api&package-manager=uv&previous-version=1.41.0&new-version=1.45.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-10-25 02:55:05 -07:00
Elior Nataf Lackritz 4c52e129eb release(langgraph): 1.2.17 (#9418)
Bumps langgraph from 1.2.16 to 1.2.17.

Includes:

- Apply an explicit `recursion_limit` equal to the default (#9417)
- Keep the caller's namespace in `RemoteGraph.stream(subgraphs=True)`
(#9401)
2026-10-24 01:02:32 +00:00
Elior Nataf Lackritz 6c4ceb1332 fix(langgraph): apply an explicit recursion_limit equal to the default (#9417)
Fixes #9416.

`merge_configs` decided whether a `recursion_limit` was set by comparing
it with `DEFAULT_RECURSION_LIMIT`, so an explicit limit equal to the
default was dropped. `LANGGRAPH_DEFAULT_RECURSION_LIMIT` sets that
default, so with it at 1000, `agent.with_config(recursion_limit=1000)`
silently kept the 9999 that `create_agent` and `create_deep_agent` bind,
and a looping agent ran to 9999 steps.

The default is now a sentinel, as #7313 proposed: `ensure_config` fills
in one `int` subclass instance, and `merge_configs` skips that exact
object instead of any equal value. The filled-in default still never
overrides a bound limit (a subgraph bound to 50 keeps 50 when its parent
runs at the default), while an explicit limit equal to the default now
applies wherever configs are merged: `with_config`, `RemoteGraph` calls,
and the limit a parent passes to its subgraphs.

langchain-core has the same check in its own `merge_configs`
(langchain-ai/langchain#39994); this PR does not touch it.

Thanks @awg66 for the report and the repro.
2026-10-24 00:56:19 +00:00
Elior Nataf Lackritz 4897c51365 fix(langgraph): keep the caller's namespace in RemoteGraph.stream(subgraphs=True) (#9401)
`RemoteGraph.stream(..., subgraphs=True)` and `astream` dropped the
caller's namespace prefix from the events they yield. Called inside a
node of a graph running under `outer:123`, a remote subgraph event came
back as `("sub:abc",)` instead of `("outer:123", "sub:abc")`, while the
events RemoteGraph pushes to the parent graph's stream, and an in-memory
subgraph streamed the same way, keep the prefix. Fixes #6604.

The `subgraphs` branch parsed the namespace from the event name a second
time, after the caller prefix was added. This removes that second parse
in `stream` and `astream`.

The new tests fail on `main`.

Same fix as #6605 by @Rakshit-gen.

Co-authored-by: Rakshit Sisodiya
<64471316+rakshit-gen@users.noreply.github.com>
2026-10-23 19:24:04 -04:00
88 changed files with 4864 additions and 1314 deletions

No files matched your search

+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
working-directory: libs/sdk-py
- name: Login to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
cache-suffix: test-${{ inputs.working-directory }}
working-directory: ${{ inputs.working-directory }}
- name: Login to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
+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@dbcb813823bdd20940b903addbd779551569679f # v4
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
+3 -3
View File
@@ -48,7 +48,7 @@ jobs:
working-directory: ${{ inputs.working-directory }}
- name: Upload build
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: test-dist
path: ${{ inputs.working-directory }}/dist/
@@ -76,13 +76,13 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: test-dist
path: ${{ inputs.working-directory }}/dist/
- name: Publish to test PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1
with:
packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true
+1 -1
View File
@@ -114,7 +114,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.11"
- name: Run check_sdk_methods script
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: '3.11'
+5 -5
View File
@@ -51,7 +51,7 @@ jobs:
working-directory: ${{ inputs.working-directory }}
- name: Upload build
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: dist
path: ${{ inputs.working-directory }}/dist/
@@ -167,7 +167,7 @@ jobs:
enable-cache: false
working-directory: ${{ inputs.working-directory }}
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: ${{ inputs.working-directory }}/dist/
@@ -259,13 +259,13 @@ jobs:
enable-cache: false
working-directory: ${{ inputs.working-directory }}
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: ${{ inputs.working-directory }}/dist/
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1
with:
packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true
@@ -301,7 +301,7 @@ jobs:
enable-cache: false
working-directory: ${{ inputs.working-directory }}
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: ${{ inputs.working-directory }}/dist/
+2 -2
View File
@@ -55,7 +55,7 @@ jobs:
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
app-id: ${{ secrets.ORG_MEMBERSHIP_APP_ID }}
private-key: ${{ secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }}
@@ -190,7 +190,7 @@ jobs:
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
app-id: ${{ secrets.ORG_MEMBERSHIP_APP_ID }}
private-key: ${{ secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }}
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
app-id: ${{ secrets.ORG_MEMBERSHIP_APP_ID }}
private-key: ${{ secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }}
+33
View File
@@ -24,6 +24,39 @@ TEST=path/to/test.py make test
Other pytest arguments can also be supplied inside the `TEST` variable.
## Direction: LangGraph is a runtime
LangGraph is consolidating as the low-level runtime for agents and workflows:
graph execution, checkpointing, streaming, and interrupt/resume. Critical
production systems and agent harnesses run on it, so correctness and stability
come first. It is moving away from being a friendly SDK for building agents
directly; agent development belongs one layer up, on a harness — Deep Agents
(github.com/langchain-ai/deepagents, a separate repo) is the recommended one.
Optimize contributions for runtime correctness, reliability, performance, and
simplicity — not for the shortest agent example.
- One way to do each thing. A change never adds a way; removals are always
deliberate (see below). The number of ways only goes down. No aliases,
overloads, extra accepted types, implicit defaults, or customization hooks
for convenience: ergonomics alone never justify new public surface.
- The mandate applies to everything that implements runtime behavior, in
whichever repo it lives: the graph engine (`langgraph`, `langgraph-core`),
the checkpointers (`checkpoint*`), and the server (`langgraph-api`). The
client SDKs and CLI stay ergonomic but are thin, faithful clients, never a
second implementation of runtime behavior.
- Agent-level packages (`prebuilt`, `langgraph-supervisor`, `langgraph-swarm`,
`langgraph-cua`) are maintenance-only: fix bugs, don't add agent features.
New examples and docs that build agents should use Deep Agents.
- Remove deliberately, not opportunistically. Removing or changing public
behavior must be the stated purpose of the PR and go through the deprecation
process — replacement in place, removal version recorded, migration note.
Never drop a default or overload as a side effect of an unrelated change.
- Behavior changes land in Python and JS together (the implementations live in
separate repos; note in the PR when the other one needs a matching change).
- If a task asks for something this section rules out, say so and propose the
runtime-level or harness-level alternative instead of quietly implementing it.
## Libraries
The repository contains several Python and JavaScript/TypeScript libraries.
+46 -46
View File
@@ -1,5 +1,5 @@
version = 1
revision = 3
revision = 5
requires-python = ">=3.10"
[[package]]
@@ -279,7 +279,7 @@ wheels = [
[[package]]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -686,7 +686,7 @@ wheels = [
[[package]]
name = "pytest"
version = "9.1.1"
version = "9.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -697,23 +697,23 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
sdist = { url = "https://files.pythonhosted.org/packages/79/42/bb4641652e58dedeac2ec51abc1f8cae4fee4ec63899a0999016229a0f2a/pytest-9.2.0.tar.gz", hash = "sha256:97c4f3b1f3c7414aa36465e09b8220f1d03627993bd51b1282e193753c80557b", size = 1797555, upload-time = "2026-10-27T21:30:20.728Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
{ url = "https://files.pythonhosted.org/packages/51/39/1f5d8c8877a0fa327db959c19341d4be61660de1ab6b7391f9eb805bcd67/pytest-9.2.0-py3-none-any.whl", hash = "sha256:07acdcf88ec174f907dc01af70bf52d49b014bbab03faeeade31c96b18c2e588", size = 411776, upload-time = "2026-10-27T21:30:19.131Z" },
]
[[package]]
name = "pytest-asyncio"
version = "1.4.0"
version = "1.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
{ name = "pytest" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/9a/887535e477847f5159377ab35303923644840b13c0ca03c0a467f84d4680/pytest_asyncio-1.4.1.tar.gz", hash = "sha256:e8266eb094e5ee9ffdb2068c9836f33e209ea77a970eb846933d416b5602669b", size = 145237, upload-time = "2026-10-25T21:05:37.135Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" },
{ url = "https://files.pythonhosted.org/packages/46/d7/cd9e2708afdbf059658adaf08bdb0792ca52489871051d6ce7d498b51277/pytest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:507afa78ec77c6661164ef1a2368bc7820383fd1a14b2a7f813de8b5a92949cc", size = 17061, upload-time = "2026-10-25T21:05:35.473Z" },
]
[[package]]
@@ -809,27 +809,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.16.9"
version = "0.17.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" }
sdist = { url = "https://files.pythonhosted.org/packages/59/73/81f140c4edd9cc323de5478e6bbd3bbcfae9a82692df1a258de8cec6a6d2/ruff-0.17.3.tar.gz", hash = "sha256:a57049c8addb3662073859bb55a7ad35aa54f8a7c9ed268911d1333c5aadaaa9", size = 5339444, upload-time = "2026-10-27T00:34:05.914Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" },
{ url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" },
{ url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" },
{ url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" },
{ url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" },
{ url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" },
{ url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" },
{ url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" },
{ url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" },
{ url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" },
{ url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" },
{ url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" },
{ url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" },
{ url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" },
{ url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" },
{ url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" },
{ url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" },
{ url = "https://files.pythonhosted.org/packages/9e/8c/083365310c4e79c842f25bf5a842facbd9b0c0ec336f96dffa5074fe723d/ruff-0.17.3-py3-none-linux_armv6l.whl", hash = "sha256:ab86b84cd9d05e56d329742c0cabdcbe2d1d56c98f4a04aab9b5b66196febfb4", size = 10216522, upload-time = "2026-10-27T00:33:29.822Z" },
{ url = "https://files.pythonhosted.org/packages/72/26/4b2edf31cfebe9605d8f6ed8f1d6f376d3a53d4c9a123e62661c64c440f0/ruff-0.17.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e407b4a6852853a9a2cdce96963e94ad9775578bb44e7561a0f2829f3cc04b11", size = 10217786, upload-time = "2026-10-27T00:33:33.949Z" },
{ url = "https://files.pythonhosted.org/packages/a2/7e/537aba4b7f54397a374d2f6a6fc93e9c8015e18553f31a4a7b7dbced48b5/ruff-0.17.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b75cc113b3aca388933d8296b667b1ac401a80317ea3074c9b0a66541199da29", size = 9440362, upload-time = "2026-10-27T00:33:35.784Z" },
{ url = "https://files.pythonhosted.org/packages/3d/66/283e39734f66c8a5ebaa93c22eb7b7515779a0b1d5a5c0ad20e05c0edb41/ruff-0.17.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab5fa7ed5ce18554ea69d850ad162cbaa0c85fc47c08d83a569f806681c9a6ad", size = 10094281, upload-time = "2026-10-27T00:33:38.216Z" },
{ url = "https://files.pythonhosted.org/packages/21/4c/1ffc768497bd025a32c72152771e40c0b99bddc5cc6e4f50c7949a395346/ruff-0.17.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c673c170e27834d1262e3a9c1e97bdc9b9c3b180c63d89173ce69747232474c1", size = 10022543, upload-time = "2026-10-27T00:33:40.181Z" },
{ url = "https://files.pythonhosted.org/packages/73/c3/f90db547301080fcb82c92abf914c8bd57b89911d9b65a6c4cc6b52d0635/ruff-0.17.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a754c0341bc9ca151b7b335988b3263921c08859f84ca219b70cee825571b6f", size = 10683921, upload-time = "2026-10-27T00:33:42.182Z" },
{ url = "https://files.pythonhosted.org/packages/e5/98/92d4a8a26c6c96cf1a5b34dd59036d8a71e52d650aab065c58867e964e63/ruff-0.17.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4590b9d86c0b1543ebb09094254e191cd43fe8edaf330baa035aab03da251532", size = 11373408, upload-time = "2026-10-27T00:33:44.19Z" },
{ url = "https://files.pythonhosted.org/packages/43/02/1f24d2e3064c9753926deeb901afa57777b37ee46799b09ddd7aee6e5145/ruff-0.17.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbde18beccd98c29a227c63c99022e3443b99258e08326df6425dfe0443705ef", size = 10977469, upload-time = "2026-10-27T00:33:46.179Z" },
{ url = "https://files.pythonhosted.org/packages/f0/20/fecedea2209f574ab92033b344e01a7f0c2dc76c6d6d19a14c9f8141173f/ruff-0.17.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fa4b0f931cdbcb8a52044f370a3eb429fb42eb88807b0e800fcaa98dd42b24e", size = 10577491, upload-time = "2026-10-27T00:33:48.233Z" },
{ url = "https://files.pythonhosted.org/packages/33/36/b93fdf0bc6f84f0cd487aadfb0354deb40013669ea35ac3d26855de1e959/ruff-0.17.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:39405f341deec934b4289634324ad695f72df2970f9121582872eaf5b722f0f5", size = 10608748, upload-time = "2026-10-27T00:33:50.284Z" },
{ url = "https://files.pythonhosted.org/packages/5d/b7/7d3f5f14145daead1efb0b9a8289ed1daa100f419d60774375951723ce00/ruff-0.17.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68074094f55ca3d29ab19491f640282f7a84061c4c7977aa5094b968ca457e9a", size = 10075057, upload-time = "2026-10-27T00:33:52.074Z" },
{ url = "https://files.pythonhosted.org/packages/b0/ed/93ff7a5909ee56e2bd4b982deae9e0efb2724417874a90c9e41ad66c6dd7/ruff-0.17.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27bb571ac78fef178fe5118d2827df95c2aae74a48fff2b15f6acd03af5ff5bb", size = 10022431, upload-time = "2026-10-27T00:33:54.061Z" },
{ url = "https://files.pythonhosted.org/packages/9b/07/a83b9eeec8c1c3c0e4eda15f06c3a91001bfd2687ea2f07b750719bf9712/ruff-0.17.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:029c37cbd3de48ca37aa12117bead2d065f15da17d66bf84e0d4274013705db2", size = 10394903, upload-time = "2026-10-27T00:33:56.158Z" },
{ url = "https://files.pythonhosted.org/packages/af/b2/05c39857c2526fcbebe46ad93a8dc97424656dfef14d3ef933abe1ffd331/ruff-0.17.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:692fdb46d0fd9c71e380947dc5fc72f2c3f881073bc1f739e4579376231affbe", size = 10799780, upload-time = "2026-10-27T00:33:58.204Z" },
{ url = "https://files.pythonhosted.org/packages/ec/d9/47171a16739eb7a353936b31c20cf54340531e68c9af7d72fd7a784e235d/ruff-0.17.3-py3-none-win32.whl", hash = "sha256:46539cb031b01fd29ae0a02142483c46640b233ee12bb1c1b690bffa1555d5ca", size = 9908481, upload-time = "2026-10-27T00:34:00.087Z" },
{ url = "https://files.pythonhosted.org/packages/d0/a0/f08c1631bfd81faa6a90eaf70c37c786c11bed2046302b56bb506c25e1a0/ruff-0.17.3-py3-none-win_amd64.whl", hash = "sha256:56b8ffb3f3b3cbccb8b76b71b38261b7ed982ef5d20f5b447b1b61a7f4949b24", size = 10341333, upload-time = "2026-10-27T00:34:02.099Z" },
{ url = "https://files.pythonhosted.org/packages/99/d5/4ad4ed31413c301430918e70d3d38ae6f097af0766cb9ab8c9d502b2a4d6/ruff-0.17.3-py3-none-win_arm64.whl", hash = "sha256:81fce74bcd6be0e9da7c67c7553fd0f1f12964a2052cd728ef917b937a5ca1d9", size = 9854332, upload-time = "2026-10-27T00:34:03.847Z" },
]
[[package]]
@@ -897,27 +897,27 @@ wheels = [
[[package]]
name = "ty"
version = "0.0.84"
version = "0.0.94"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" }
sdist = { url = "https://files.pythonhosted.org/packages/8a/c7/1257c8c70e46274664db1eaf2b8aba99a1277470daaa39c65e7decd67c8f/ty-0.0.94.tar.gz", hash = "sha256:933d2feca6057742d04af00c090621c139e7be2c059aa38b040a70532fd5104b", size = 8106769, upload-time = "2026-10-28T18:06:09.045Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" },
{ url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" },
{ url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" },
{ url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" },
{ url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" },
{ url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" },
{ url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" },
{ url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" },
{ url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" },
{ url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" },
{ url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" },
{ url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" },
{ url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" },
{ url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" },
{ url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" },
{ url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" },
{ url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" },
{ url = "https://files.pythonhosted.org/packages/27/bd/5145d91cf196cb20fc3b005515b21266a419ea7336d3d7891a1769439259/ty-0.0.94-py3-none-linux_armv6l.whl", hash = "sha256:c6463c045880ad17df911a53d367eb2554b17538ca54911bea6ccb20ec95ee11", size = 14528574, upload-time = "2026-10-28T18:05:19.423Z" },
{ url = "https://files.pythonhosted.org/packages/29/0e/2b563132429b5fc6044095061fde4584548cb55abf0d6d9f2829c41ab226/ty-0.0.94-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec3a3892f4290306c674586149668097cf44f13e8b381ba388662f5a440b459b", size = 14067146, upload-time = "2026-10-28T18:05:23.014Z" },
{ url = "https://files.pythonhosted.org/packages/3d/e0/3a458362ea33ca9756cd6b914f905b3dec007d329b21d83e859ecee65a63/ty-0.0.94-py3-none-macosx_11_0_arm64.whl", hash = "sha256:74ddc6eeecd3251cf8980c9abb960d7eeab649dac5cdb7504910d99c479f84ab", size = 13148266, upload-time = "2026-10-28T18:05:25.769Z" },
{ url = "https://files.pythonhosted.org/packages/cd/38/5380bdaad2745a4e191deacce96403af74d1992ed8782d5766c301c32b50/ty-0.0.94-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31bdc3aace142400d7e22b4d32676c063ec42f0437d77940012c3dc60587ecdd", size = 13973583, upload-time = "2026-10-28T18:05:28.731Z" },
{ url = "https://files.pythonhosted.org/packages/1d/bc/7e1dc4f2594010aff6d1e2e34d0ff9003736210983be5720a04d69c27e03/ty-0.0.94-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fecacfe9c41ed1109aa503ed172d84b1b0fc283ee49aaf078d298dbe1c62e75f", size = 14269235, upload-time = "2026-10-28T18:05:31.697Z" },
{ url = "https://files.pythonhosted.org/packages/7b/61/43041f1c53164d6be21ace55047feffeaa8a9709782b01b06c951412b900/ty-0.0.94-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a49762bbc97a732847da8350ad0227c99ce2675fa32e3f58aa9f9ff4a81e4c14", size = 15191720, upload-time = "2026-10-28T18:05:34.455Z" },
{ url = "https://files.pythonhosted.org/packages/22/72/f425d48b13cbf12b92e4f837a5866e9788e9ef4d5a071e2b12f54ee94da4/ty-0.0.94-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76a7b3aea839dfafb4bc068af918e0292b06ac26ea2a43f7ab3dd65548c0ed50", size = 15596604, upload-time = "2026-10-28T18:05:37.377Z" },
{ url = "https://files.pythonhosted.org/packages/13/ea/bd2da666ad4af596886833b095098fa9ff93685285f48bebc4d3865126e0/ty-0.0.94-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f032ce5a9c0c7033a5a8e748f952ece2e22afe2b148d211d05dca2cba8b28000", size = 15572711, upload-time = "2026-10-28T18:05:40.499Z" },
{ url = "https://files.pythonhosted.org/packages/44/f8/4f07eac57e9b60f49cc222d5048d9a61ac7f0902964ec8ac8d4ea8bc521d/ty-0.0.94-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc5e780c2107c1b83a21a394076da328e05a293ce06298df40ecab1ddc31d371", size = 16309102, upload-time = "2026-10-28T18:05:43.539Z" },
{ url = "https://files.pythonhosted.org/packages/22/39/c2ac7e14eb8ca59bd3c71b8c3055294524df84c476ba88545f59c11ceaf9/ty-0.0.94-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5bc969cdabf85aa66987c9b4818072e3af17ce5f2b7bc93550ab896c904c6c40", size = 15073645, upload-time = "2026-10-28T18:05:46.65Z" },
{ url = "https://files.pythonhosted.org/packages/d1/7d/dd4935abf50a97ccc1f46cf6c921a7acdab30f102c3c56aa4e8d6cbc3fa9/ty-0.0.94-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:27bbaff0d7bbcb3e92ed7cab07d6b26dc7ed7fa28ff69910324e5cc347c7b256", size = 13936242, upload-time = "2026-10-28T18:05:49.726Z" },
{ url = "https://files.pythonhosted.org/packages/d6/f0/b65fa6ccde2d629e25031bf2cc2d21ac7b50ee6e0e3d29a5f471c4686c54/ty-0.0.94-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d0960eb71220300655d67db91073fd180f385186b1d186e76faa2fd3ae5bd10f", size = 14280351, upload-time = "2026-10-28T18:05:52.521Z" },
{ url = "https://files.pythonhosted.org/packages/d6/d2/5c901ad5e4f5444852b002b1f4dd3a0b470039305c688facc0483bfccd1a/ty-0.0.94-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab04c14bca92ab92106ceaa19a962a80998389700cf7938576ba3936a304355d", size = 14611163, upload-time = "2026-10-28T18:05:55.208Z" },
{ url = "https://files.pythonhosted.org/packages/6d/99/37d8633e9b5d1dcf1b06f12466825ff0c4028664994e573c352546ee9cbd/ty-0.0.94-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a92ebefa092e6bd58cb5719626858df744575473e2de0cd1c44abd44b9b84b57", size = 14938978, upload-time = "2026-10-28T18:05:58.17Z" },
{ url = "https://files.pythonhosted.org/packages/51/13/804b1f993bf84a819385f8b440fbc4b894499e2c20d865cd74d69327e7b8/ty-0.0.94-py3-none-win32.whl", hash = "sha256:3212dcf30b7567f2bb65a83cdfc4818a5385f82e7afddd83548b0d84e759057e", size = 13448009, upload-time = "2026-10-28T18:06:01.111Z" },
{ url = "https://files.pythonhosted.org/packages/a1/23/81850750cda0ea781ba9d6e08cea1a1039361cb3474c7dd03765bbc4b51f/ty-0.0.94-py3-none-win_amd64.whl", hash = "sha256:daf5bafe8e7d21b8a99a56fc8518f2342a2dbcaa370a5c25cfaedc684088d158", size = 14010576, upload-time = "2026-10-28T18:06:03.773Z" },
{ url = "https://files.pythonhosted.org/packages/d7/d4/ac3a759556493230823abd31e149c95401f96b9cac36567bd403a8454ea3/ty-0.0.94-py3-none-win_arm64.whl", hash = "sha256:ce344353ccdc542cfcfd6abe1481cf23c005f483411ea79652b78ce0beae36f2", size = 13547398, upload-time = "2026-10-28T18:06:06.379Z" },
]
[[package]]
+1 -1
View File
@@ -278,7 +278,7 @@ wheels = [
[[package]]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
+2 -2
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint-sqlite"
version = "3.1.2"
version = "3.1.3"
description = "Library with a SQLite 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>=4.3.0,<5.0.0",
"langgraph-checkpoint>=4.3.1,<5.0.0",
"aiosqlite>=0.20",
"sqlite-vec>=0.1.6",
]
+2 -2
View File
@@ -285,7 +285,7 @@ wheels = [
[[package]]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -360,7 +360,7 @@ test = [
[[package]]
name = "langgraph-checkpoint-sqlite"
version = "3.1.2"
version = "3.1.3"
source = { editable = "." }
dependencies = [
{ name = "aiosqlite" },
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
description = "Library with base interfaces for LangGraph checkpoint savers."
authors = []
requires-python = ">=3.10"
+208 -93
View File
@@ -1,5 +1,5 @@
version = 1
revision = 3
revision = 5
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.15'",
@@ -178,6 +178,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" },
]
[[package]]
name = "distro"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.1"
@@ -212,6 +221,40 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]]
name = "httpcore2"
version = "2.14.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.11'",
]
dependencies = [
{ name = "h11" },
{ name = "truststore" },
]
sdist = { url = "https://files.pythonhosted.org/packages/30/ff/b53af1b49bb2cb5fa97c92215614b173ca5c1a99ded80e2d52f15a94fa6d/httpcore2-2.14.0.tar.gz", hash = "sha256:4f57a1a8249fe6af589d935cfd456f1d13bbdcb0f0fc72af9142368fc94811d7", size = 69096, upload-time = "2026-10-18T16:29:21.898Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/02/06/a8d6a8f90d11b76d9086242b6bb0ee4b503473bfc8be34c2d5b067275c22/httpcore2-2.14.0-py3-none-any.whl", hash = "sha256:4c4f64f4b544afeb778c42c0ffb5941b29e671157e90ed3f6dc787c4bca90cd1", size = 84291, upload-time = "2026-10-18T16:29:19.743Z" },
]
[[package]]
name = "httpcore2"
version = "2.16.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.15'",
"python_full_version >= '3.12' and python_full_version < '3.15'",
"python_full_version == '3.11.*'",
]
dependencies = [
{ name = "h11" },
{ name = "truststore" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c0/61/afa9d6b7452f9eca73f73d45324eb0bb52f098fd5e6049bf6b8263be9b4e/httpcore2-2.16.0.tar.gz", hash = "sha256:c72cd0413563b17e1cd8354677dff887466f8ec2551679759e93126534428643", size = 69886, upload-time = "2026-10-21T07:09:45.31Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/af/c2/ce417c397afa5181f0e3c6ff01a4d01639414e1adef08730d97571297151/httpcore2-2.16.0-py3-none-any.whl", hash = "sha256:5c4358319f2602d984ad93182c9fee5160f22e9da7babdda870a5e7c4e2dc34f", size = 85070, upload-time = "2026-10-21T07:09:42.802Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
@@ -228,12 +271,62 @@ wheels = [
]
[[package]]
name = "idna"
version = "3.15"
name = "httpx2"
version = "2.14.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" }
resolution-markers = [
"python_full_version < '3.11'",
]
dependencies = [
{ name = "anyio", marker = "sys_platform != 'emscripten'" },
{ name = "httpcore2", version = "2.14.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'emscripten'" },
{ name = "idna" },
{ name = "truststore", marker = "sys_platform != 'emscripten'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e8/67/58353f1848b4ba568e1ec22ea1e0d8e039f0bca5fde7e2d1f07739fd83e7/httpx2-2.14.0.tar.gz", hash = "sha256:47ad61a3c40282cd8d0dc96311531f00f5bfc71a066c6fe3b36d8f63fb9e4f6e", size = 102036, upload-time = "2026-10-18T16:29:22.627Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" },
{ url = "https://files.pythonhosted.org/packages/69/5a/469616aa8c2cbb0bb9dd67a3c18d07c01ab604cc0b236ee1c47d260c7f6e/httpx2-2.14.0-py3-none-any.whl", hash = "sha256:9d20c13a10bae1773a0be3b0f1e5beeb4bdd549bc3a2ba18efa32897c65403e4", size = 96901, upload-time = "2026-10-18T16:29:20.819Z" },
]
[[package]]
name = "httpx2"
version = "2.16.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.15'",
"python_full_version >= '3.12' and python_full_version < '3.15'",
"python_full_version == '3.11.*'",
]
dependencies = [
{ name = "anyio", marker = "sys_platform != 'emscripten'" },
{ name = "httpcore2", version = "2.16.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'emscripten'" },
{ name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" },
{ name = "idna" },
{ name = "truststore", marker = "sys_platform != 'emscripten'" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/73/5ca2cedf5e4e50423d1c258877865a6b62793633b561c58499f8aeb33b7e/httpx2-2.16.0.tar.gz", hash = "sha256:0d54c98128e8f2ff7fbbde0fca8c359e5f032edeaaaac443cdb7f31f685bcd7f", size = 103116, upload-time = "2026-10-21T07:09:46.348Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7b/3d/2e4bc4fd1ef9770871669adb348d612a6e4786b303a23f9935b81c48dd60/httpx2-2.16.0-py3-none-any.whl", hash = "sha256:fd2c5d7909e8d86fe92d53020c310ca0e4e431b838c207c5754b9bbf0308d49a", size = 97880, upload-time = "2026-10-21T07:09:44.01Z" },
]
[[package]]
name = "httpx2-jsfetch"
version = "1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" },
]
[[package]]
name = "idna"
version = "3.20"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" },
]
[[package]]
@@ -268,7 +361,7 @@ wheels = [
[[package]]
name = "langchain-core"
version = "1.6.5"
version = "1.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -282,26 +375,26 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "uuid-utils" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/ed/ceecd3bfcfc77a94cf30a9f1506e907fdea447458825e01914a72d2d1a6f/langchain_core-1.6.5.tar.gz", hash = "sha256:bdb059fef65473fd444558dbb18f50901657402c43ceed9dc0fdc7b76a393c6b", size = 1007041, upload-time = "2026-09-24T18:11:06.487Z" }
sdist = { url = "https://files.pythonhosted.org/packages/96/ec/55c77c3d53c65fbf58d41bd78567ed34b5c0e9dcacd00e4568a9067314ab/langchain_core-1.7.1.tar.gz", hash = "sha256:5dc2a357e1aa40e84c7f29755182121b3acf04410e55fa3723915f645033fede", size = 1026919, upload-time = "2026-10-28T14:59:47.314Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/68/447ac7e734990b7ed9dcfc3b6dee9a4cd4d4169e296429fa71a9a47c1c7b/langchain_core-1.6.5-py3-none-any.whl", hash = "sha256:54c7b0e9314b9084fb04405bb33dc5d32986d512d92b7b8863899cd5f6267556", size = 572136, upload-time = "2026-09-24T18:11:04.837Z" },
{ url = "https://files.pythonhosted.org/packages/f3/87/50fa7f000065502ce76ec6b701a13adec62c32171785d5bebb0345219a0f/langchain_core-1.7.1-py3-none-any.whl", hash = "sha256:2d668788945e824832e19bca05788a25dbb864197d63ec48b583057cf8d975fa", size = 575541, upload-time = "2026-10-28T14:59:45.887Z" },
]
[[package]]
name = "langchain-protocol"
version = "0.0.18"
version = "0.0.20"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d2/59/b5959aea96faa9146e2e49a7a22882b3528c62efafe9a6a95beab30c2305/langchain_protocol-0.0.18.tar.gz", hash = "sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6", size = 6150, upload-time = "2026-06-18T17:08:26.959Z" }
sdist = { url = "https://files.pythonhosted.org/packages/cb/8b/d908d082c3cf5368ddd1816ed114de79c873f979ba7f98b95fab0e841643/langchain_protocol-0.0.20.tar.gz", hash = "sha256:0f5bd002474265648e91161771a9e0438fe9480bd55a416e51af0f60070637ec", size = 6280, upload-time = "2026-10-20T11:13:50.84Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/99/2e/d82db9eec13ad0f72e7aaad5c4bc730ab111934fdc83c85523206eb9b0a0/langchain_protocol-0.0.18-py3-none-any.whl", hash = "sha256:70b53a86fbf9cedc863555effe44da192ab02d556ddbf2cf95b8873adcf41b5a", size = 7221, upload-time = "2026-06-18T17:08:25.996Z" },
{ url = "https://files.pythonhosted.org/packages/02/4b/f1c99a0b27c5ff8b4b131c0d6e62012c17cc424089efe213767db927afd2/langchain_protocol-0.0.20-py3-none-any.whl", hash = "sha256:3c935e5e46b0f6f9cb52f45ba7cfa53d1b9fa80c82c4a277d47f03e07f7385b1", size = 7347, upload-time = "2026-10-20T11:13:49.566Z" },
]
[[package]]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
source = { editable = "." }
dependencies = [
{ name = "langchain-core" },
@@ -384,23 +477,28 @@ test = [
[[package]]
name = "langsmith"
version = "0.8.18"
version = "0.14.16"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
{ name = "anyio" },
{ name = "distro" },
{ name = "httpx2", version = "2.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "httpx2", version = "2.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
{ name = "packaging" },
{ name = "pydantic" },
{ name = "requests" },
{ name = "requests-toolbelt" },
{ name = "sniffio" },
{ name = "typing-extensions" },
{ name = "uuid-utils" },
{ name = "websockets" },
{ name = "xxhash" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9a/d9/a6681aa9847bbbc5ec21abe20a5e233b94e5edcfe39624db607ac7e8ccb4/langsmith-0.8.18.tar.gz", hash = "sha256:32dde9c0e67e053e0fb738921fc8ced768af7b8fa83d7a0e3fd63597cf8776dd", size = 4526988, upload-time = "2026-06-19T13:12:17.123Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/45/576803a72da96d9c0866b3815f08f447ed16c14acb52174129daca6847d6/langsmith-0.14.16.tar.gz", hash = "sha256:4e5816cbe1541f4d04d71f652eb8f4137889861bebcf7137bcba3553ae9e335e", size = 5096529, upload-time = "2026-10-29T17:53:27.747Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/03/70/0e0cc80a3b064c8d6c8d697c3125ed86e39d5a7393ec6dc8b07cb1cf13c4/langsmith-0.8.18-py3-none-any.whl", hash = "sha256:3940183349993faef48e6c7d08e4822ee9cefd906b362d0e3c2d650314d2f282", size = 508108, upload-time = "2026-06-19T13:12:15.348Z" },
{ url = "https://files.pythonhosted.org/packages/11/3a/9a072c02a22089ddf20f0f33ea95bee5a5951b31839f118b04f27b94be6e/langsmith-0.14.16-py3-none-any.whl", hash = "sha256:b9ea1a57622b0788b181d78605f974fb206c5f6f75793076ffec23cef4d904c8", size = 933680, upload-time = "2026-10-29T17:53:25.465Z" },
]
[[package]]
@@ -806,37 +904,36 @@ wheels = [
[[package]]
name = "pycryptodome"
version = "3.23.0"
version = "4.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" }
sdist = { url = "https://files.pythonhosted.org/packages/33/c7/f9374c8939560ab22db1dd9dbe215b62d20f32634aeb7f259dc03c807b44/pycryptodome-4.2.0.tar.gz", hash = "sha256:21d7fdba239d6ef863d3d08388801c683453a3bb4897c2b27f27847aad89bdf7", size = 1750332, upload-time = "2026-10-24T10:24:42.016Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" },
{ url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" },
{ url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" },
{ url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" },
{ url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" },
{ url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" },
{ url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" },
{ url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" },
{ url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" },
{ url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" },
{ url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" },
{ url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" },
{ url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" },
{ url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" },
{ url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" },
{ url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" },
{ url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" },
{ url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" },
{ url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" },
{ url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" },
{ url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" },
{ url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" },
{ url = "https://files.pythonhosted.org/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886, upload-time = "2025-05-17T17:21:20.614Z" },
{ url = "https://files.pythonhosted.org/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151, upload-time = "2025-05-17T17:21:22.666Z" },
{ url = "https://files.pythonhosted.org/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461, upload-time = "2025-05-17T17:21:25.225Z" },
{ url = "https://files.pythonhosted.org/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440, upload-time = "2025-05-17T17:21:27.991Z" },
{ url = "https://files.pythonhosted.org/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005, upload-time = "2025-05-17T17:21:31.37Z" },
{ url = "https://files.pythonhosted.org/packages/fb/05/61028b65913438a091ed3a2ec55bb50843e674b6ae14bccf196bbed652a2/pycryptodome-4.2.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:4b58c4fd5615a788e0df6793d578e565b19d75a348c0ba02d469b0c38bbf0332", size = 1684620, upload-time = "2026-10-24T10:23:50.473Z" },
{ url = "https://files.pythonhosted.org/packages/cd/73/0930fd56dd5eb856ecc33ec48845def52a1d25a9cd6b810a4c3ef3633cf4/pycryptodome-4.2.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:42ab554efcc14928f1db42deac9c84989fd49e289a16aacbbee709fe2eb838cc", size = 1520423, upload-time = "2026-10-24T10:23:52.505Z" },
{ url = "https://files.pythonhosted.org/packages/19/ba/4b931de42881053a6d818b9ee70f6dde3dfe1cfcf78527d45db3f99d8b47/pycryptodome-4.2.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b4ed1a34d2d667ec165beb15313a759bcd8e704e2743f9942491c5a88a2d6ca5", size = 2296340, upload-time = "2026-10-24T10:23:54.294Z" },
{ url = "https://files.pythonhosted.org/packages/d4/63/68fc2a01d621c2ff389ea261e731e8f1abe6aba89992681bbcdb19fcca43/pycryptodome-4.2.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1482a7d9ceed7beadbb0853f83d4d44406a9f9fa8ee596d637618f095869e14", size = 2987589, upload-time = "2026-10-24T10:23:56.547Z" },
{ url = "https://files.pythonhosted.org/packages/f5/63/69e211459f4625c89bb47a7ba4527a6cc3276c9bbd79ae871a2d0b05c021/pycryptodome-4.2.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:dceadb6f478b0a687d129f1b58220e0587fb17a31b2502ae7a1a4234bc304ee1", size = 2322482, upload-time = "2026-10-24T10:23:58.538Z" },
{ url = "https://files.pythonhosted.org/packages/4a/65/21a4ed3ae5af153c57861d7f987c81fca5bf02d49b76f34c545f8eb6b95f/pycryptodome-4.2.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8217736d5ac9b7fef49d1d2b292d5af4e8300298ef4926742aaec49b2bf6505b", size = 3548352, upload-time = "2026-10-24T10:24:01.185Z" },
{ url = "https://files.pythonhosted.org/packages/4a/a4/9a7e4e8896cfd8131978d31e39c930fb114f6a63751f39d2069b965dad45/pycryptodome-4.2.0-cp315-cp315t-win32.whl", hash = "sha256:b717e36cf14cffe463ff4e7d423a0c9fd6f83b40d207388e0f40dd0a3d4018bc", size = 1306386, upload-time = "2026-10-24T10:24:03.047Z" },
{ url = "https://files.pythonhosted.org/packages/af/6c/e1a78019b7db83e2cfc1c98cea4a7a0afbb6b233514b9dfee2bad7443bd9/pycryptodome-4.2.0-cp315-cp315t-win_amd64.whl", hash = "sha256:585f0a792cdc7fb381ca0d1fac4e20f03fd99bb78cf2733eaab59f62c77ccf88", size = 1688319, upload-time = "2026-10-24T10:24:04.963Z" },
{ url = "https://files.pythonhosted.org/packages/c6/96/7cb051ba325141b33bc7e6ef6152499cf74b74606c5bb4c8c72411e8afdc/pycryptodome-4.2.0-cp315-cp315t-win_arm64.whl", hash = "sha256:831ae8ce070e990d4fcb913c49c7fb0181ea7ab2691fc58dcb9d41fdb5384d85", size = 1330914, upload-time = "2026-10-24T10:24:06.934Z" },
{ url = "https://files.pythonhosted.org/packages/41/4c/954a9a1451f7bcc89d66db39bf6499ef5701e8ff35b6c898c659a7ec8546/pycryptodome-4.2.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e430168f767ecec60c1f850df4abbd0b9f94786b9cc8041a00e976f6d5190c6", size = 1680663, upload-time = "2026-10-24T10:24:08.982Z" },
{ url = "https://files.pythonhosted.org/packages/6e/84/23f8013dce07cbc8c8b9ed878972294230a1edc49a6b64d271c77c878c29/pycryptodome-4.2.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aea22120791b4534a27e39fc01d91265a249ba72dbd682a8a5c1adee475d5483", size = 1513323, upload-time = "2026-10-24T10:24:11.076Z" },
{ url = "https://files.pythonhosted.org/packages/1a/82/693940a8e4a20c3db91050480429daf39c818be3c7ec59f992ded4567ce6/pycryptodome-4.2.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f6f30a457bd83a3074747d11bcb95375397a2e9b9a6b9179ab001e0cdd8ed0c5", size = 2292885, upload-time = "2026-10-24T10:24:13.486Z" },
{ url = "https://files.pythonhosted.org/packages/18/88/33df6b1176b63aed427745696d88ed38f7877104cf0f6004ee9ad600f897/pycryptodome-4.2.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:76777686de3a354c41710727988c2284448d5db9bd82e5ddb9a9b6710d17d9df", size = 2976488, upload-time = "2026-10-24T10:24:16.139Z" },
{ url = "https://files.pythonhosted.org/packages/b1/c6/c1c137d6e3284409ca5b2cfd7db3621167c316f9c314a96274af69a66fbb/pycryptodome-4.2.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c5bc41a418451616cd0ec3d765ba8539416d7429d7cd633ffb603a5a22355e70", size = 2313324, upload-time = "2026-10-24T10:24:18.302Z" },
{ url = "https://files.pythonhosted.org/packages/ef/26/b7752cffc8200040835ad24dc17c9de6376ac27612c8bf943766cb1b3c36/pycryptodome-4.2.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2c5b1e9058687daca3aaaff23eeb1c1853c8251986f0de7a950235e8bc0cc8b7", size = 3537773, upload-time = "2026-10-24T10:24:20.482Z" },
{ url = "https://files.pythonhosted.org/packages/df/1b/34bec1416507cda23b5dfa33c0bca936fd4ac8ed4d3269803477b897649d/pycryptodome-4.2.0-cp39-abi3-win32.whl", hash = "sha256:bb7659f87d7aa69105e1be12764f9cf1908287eb424b242186f5ac3c192ed612", size = 1292974, upload-time = "2026-10-24T10:24:22.422Z" },
{ url = "https://files.pythonhosted.org/packages/03/5b/a4a31e6e1aabd5a26da77c7960e7fcce4fca9132e51868f1bba10bf1e932/pycryptodome-4.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:3282c9fd744a16594e51e1a9fbfe4d658b6b0aaa1f050926e6d591ac7494f78b", size = 1658684, upload-time = "2026-10-24T10:24:24.009Z" },
{ url = "https://files.pythonhosted.org/packages/2b/81/bddb0637fa85a03d00eea2f3514c5aa8d7b9c4921ef5e0c125b4ce662a8c/pycryptodome-4.2.0-cp39-abi3-win_arm64.whl", hash = "sha256:34ec11258967efec61d92a83a63a050ab7a7c31dc3806412f6334b8aef47e2d9", size = 1305591, upload-time = "2026-10-24T10:24:25.993Z" },
{ url = "https://files.pythonhosted.org/packages/df/a1/5460bbb750121f71abacaaf25f0cb1df67be7fe13c70cf6d7c2b8ad54fc0/pycryptodome-4.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ea7dbf6b8ce0f0d2c0e17c1226c74d07c01499db7704797bdbb6cb9ec511ccb", size = 1472158, upload-time = "2026-10-24T10:24:27.92Z" },
{ url = "https://files.pythonhosted.org/packages/e2/65/27ac8fb15f0ffb70b72a0413291637de46d29b075804221703273c3ca6d1/pycryptodome-4.2.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0106af8684e1d73c93bc39a2f1cb4ae2f2636d1f80e6291caddcc37b4efc5dc1", size = 1247107, upload-time = "2026-10-24T10:24:29.672Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7d/6b3ed6cd8626007f6a6b13616b08150bc0bb936a0b84c310e59558ed5136/pycryptodome-4.2.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4deb46e0d0a540b1f047cb6f430249692da15c21edd75f5e6278314089fb859", size = 1426092, upload-time = "2026-10-24T10:24:31.627Z" },
{ url = "https://files.pythonhosted.org/packages/73/45/5adf4dbb1eab1da6291d25f777d1d34f745c7f2993bf2d4d3b0d9abb87c6/pycryptodome-4.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e094873fe87ae4d546ef393cec7ea1e1cf8e0684f649612b19352afa67eb8bdb", size = 1662257, upload-time = "2026-10-24T10:24:33.373Z" },
{ url = "https://files.pythonhosted.org/packages/57/88/fef93a9ecbda783a312b15a8823fa1a9a4ec3474685e756584af595457aa/pycryptodome-4.2.0-pp311-pypy311_pp80-macosx_10_15_x86_64.whl", hash = "sha256:30f418cd1f4e776bde6c5df1fcd90ddfe74dfc8f679286a82f17ec1d4018cce0", size = 1467319, upload-time = "2026-10-24T10:24:35.108Z" },
{ url = "https://files.pythonhosted.org/packages/78/65/cb53934c9f8d5c41dfcfaddb0d98103f4dd6670af1ffdbc2582bcdd99382/pycryptodome-4.2.0-pp311-pypy311_pp80-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20cb4a489f7a5150a882901884b84ee0df77a47b9a6eb6e2dd8e98997b7b826f", size = 1247105, upload-time = "2026-10-24T10:24:36.859Z" },
{ url = "https://files.pythonhosted.org/packages/11/38/5bba7fd24beb6bc7a82fdd155f2df39f51c8ad16143fc99d3a1ccc7c76b4/pycryptodome-4.2.0-pp311-pypy311_pp80-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f104dc1a38aa1172a166ebe3716f2666f785bfa015b7ca13bb808c12c902e455", size = 1426092, upload-time = "2026-10-24T10:24:38.584Z" },
{ url = "https://files.pythonhosted.org/packages/38/cf/e70a8cb1ab184b59b2d02dfd403965a0c6419571d8520463944bcb0ca8a0/pycryptodome-4.2.0-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:a7c6857077255f5f4e6c34e6325802ba1bb59960133c0a5e584d9371dbdf3dea", size = 1662214, upload-time = "2026-10-24T10:24:40.139Z" },
]
[[package]]
@@ -983,7 +1080,7 @@ wheels = [
[[package]]
name = "pytest"
version = "9.1.1"
version = "9.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -994,35 +1091,35 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
sdist = { url = "https://files.pythonhosted.org/packages/79/42/bb4641652e58dedeac2ec51abc1f8cae4fee4ec63899a0999016229a0f2a/pytest-9.2.0.tar.gz", hash = "sha256:97c4f3b1f3c7414aa36465e09b8220f1d03627993bd51b1282e193753c80557b", size = 1797555, upload-time = "2026-10-27T21:30:20.728Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
{ url = "https://files.pythonhosted.org/packages/51/39/1f5d8c8877a0fa327db959c19341d4be61660de1ab6b7391f9eb805bcd67/pytest-9.2.0-py3-none-any.whl", hash = "sha256:07acdcf88ec174f907dc01af70bf52d49b014bbab03faeeade31c96b18c2e588", size = 411776, upload-time = "2026-10-27T21:30:19.131Z" },
]
[[package]]
name = "pytest-asyncio"
version = "1.4.0"
version = "1.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
{ name = "pytest" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/9a/887535e477847f5159377ab35303923644840b13c0ca03c0a467f84d4680/pytest_asyncio-1.4.1.tar.gz", hash = "sha256:e8266eb094e5ee9ffdb2068c9836f33e209ea77a970eb846933d416b5602669b", size = 145237, upload-time = "2026-10-25T21:05:37.135Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" },
{ url = "https://files.pythonhosted.org/packages/46/d7/cd9e2708afdbf059658adaf08bdb0792ca52489871051d6ce7d498b51277/pytest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:507afa78ec77c6661164ef1a2368bc7820383fd1a14b2a7f813de8b5a92949cc", size = 17061, upload-time = "2026-10-25T21:05:35.473Z" },
]
[[package]]
name = "pytest-mock"
version = "3.16.0"
version = "3.17.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7a/7f/6ed29931d5c8cd396e7c0a55412e6cc88020373365c8685985dea53d26d7/pytest_mock-3.16.0.tar.gz", hash = "sha256:5a8395528b8f498205f3718f575228d0edaed7425fff638f87d1a6c3e0383636", size = 35362, upload-time = "2026-09-27T14:57:55.46Z" }
sdist = { url = "https://files.pythonhosted.org/packages/97/c2/28b9f5b955ff2bcd4727a6f768e2ca9b586cd7c37b95ea5f7b9b873ac7b3/pytest_mock-3.17.0.tar.gz", hash = "sha256:e36576e0985e01027e999891e7a966c67822d40c89ef1b4c8b6e22737e3590b4", size = 43064, upload-time = "2026-10-17T16:21:42.435Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/db/5b/b83a9bf1a3b4ec222f9fa083147ff6816245223da0ab92370e7e056f113f/pytest_mock-3.16.0-py3-none-any.whl", hash = "sha256:007cfeb257801d88d9c0b2a7b5a15a15e73b71968dfd72e7bf8c4a2f8393aec8", size = 10016, upload-time = "2026-09-27T14:57:54.283Z" },
{ url = "https://files.pythonhosted.org/packages/e8/ea/47fbdc8f37410956fd445720e927589b1e2a47772c5f28109f0ce2fa26d9/pytest_mock-3.17.0-py3-none-any.whl", hash = "sha256:1b82aa4b37137248bd687055e109723debeb07b489de1565297dc9589c5f2d6b", size = 11248, upload-time = "2026-10-17T16:21:41.265Z" },
]
[[package]]
@@ -1164,27 +1261,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.16.9"
version = "0.17.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" }
sdist = { url = "https://files.pythonhosted.org/packages/59/73/81f140c4edd9cc323de5478e6bbd3bbcfae9a82692df1a258de8cec6a6d2/ruff-0.17.3.tar.gz", hash = "sha256:a57049c8addb3662073859bb55a7ad35aa54f8a7c9ed268911d1333c5aadaaa9", size = 5339444, upload-time = "2026-10-27T00:34:05.914Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" },
{ url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" },
{ url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" },
{ url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" },
{ url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" },
{ url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" },
{ url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" },
{ url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" },
{ url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" },
{ url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" },
{ url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" },
{ url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" },
{ url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" },
{ url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" },
{ url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" },
{ url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" },
{ url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" },
{ url = "https://files.pythonhosted.org/packages/9e/8c/083365310c4e79c842f25bf5a842facbd9b0c0ec336f96dffa5074fe723d/ruff-0.17.3-py3-none-linux_armv6l.whl", hash = "sha256:ab86b84cd9d05e56d329742c0cabdcbe2d1d56c98f4a04aab9b5b66196febfb4", size = 10216522, upload-time = "2026-10-27T00:33:29.822Z" },
{ url = "https://files.pythonhosted.org/packages/72/26/4b2edf31cfebe9605d8f6ed8f1d6f376d3a53d4c9a123e62661c64c440f0/ruff-0.17.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e407b4a6852853a9a2cdce96963e94ad9775578bb44e7561a0f2829f3cc04b11", size = 10217786, upload-time = "2026-10-27T00:33:33.949Z" },
{ url = "https://files.pythonhosted.org/packages/a2/7e/537aba4b7f54397a374d2f6a6fc93e9c8015e18553f31a4a7b7dbced48b5/ruff-0.17.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b75cc113b3aca388933d8296b667b1ac401a80317ea3074c9b0a66541199da29", size = 9440362, upload-time = "2026-10-27T00:33:35.784Z" },
{ url = "https://files.pythonhosted.org/packages/3d/66/283e39734f66c8a5ebaa93c22eb7b7515779a0b1d5a5c0ad20e05c0edb41/ruff-0.17.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab5fa7ed5ce18554ea69d850ad162cbaa0c85fc47c08d83a569f806681c9a6ad", size = 10094281, upload-time = "2026-10-27T00:33:38.216Z" },
{ url = "https://files.pythonhosted.org/packages/21/4c/1ffc768497bd025a32c72152771e40c0b99bddc5cc6e4f50c7949a395346/ruff-0.17.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c673c170e27834d1262e3a9c1e97bdc9b9c3b180c63d89173ce69747232474c1", size = 10022543, upload-time = "2026-10-27T00:33:40.181Z" },
{ url = "https://files.pythonhosted.org/packages/73/c3/f90db547301080fcb82c92abf914c8bd57b89911d9b65a6c4cc6b52d0635/ruff-0.17.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a754c0341bc9ca151b7b335988b3263921c08859f84ca219b70cee825571b6f", size = 10683921, upload-time = "2026-10-27T00:33:42.182Z" },
{ url = "https://files.pythonhosted.org/packages/e5/98/92d4a8a26c6c96cf1a5b34dd59036d8a71e52d650aab065c58867e964e63/ruff-0.17.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4590b9d86c0b1543ebb09094254e191cd43fe8edaf330baa035aab03da251532", size = 11373408, upload-time = "2026-10-27T00:33:44.19Z" },
{ url = "https://files.pythonhosted.org/packages/43/02/1f24d2e3064c9753926deeb901afa57777b37ee46799b09ddd7aee6e5145/ruff-0.17.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbde18beccd98c29a227c63c99022e3443b99258e08326df6425dfe0443705ef", size = 10977469, upload-time = "2026-10-27T00:33:46.179Z" },
{ url = "https://files.pythonhosted.org/packages/f0/20/fecedea2209f574ab92033b344e01a7f0c2dc76c6d6d19a14c9f8141173f/ruff-0.17.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fa4b0f931cdbcb8a52044f370a3eb429fb42eb88807b0e800fcaa98dd42b24e", size = 10577491, upload-time = "2026-10-27T00:33:48.233Z" },
{ url = "https://files.pythonhosted.org/packages/33/36/b93fdf0bc6f84f0cd487aadfb0354deb40013669ea35ac3d26855de1e959/ruff-0.17.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:39405f341deec934b4289634324ad695f72df2970f9121582872eaf5b722f0f5", size = 10608748, upload-time = "2026-10-27T00:33:50.284Z" },
{ url = "https://files.pythonhosted.org/packages/5d/b7/7d3f5f14145daead1efb0b9a8289ed1daa100f419d60774375951723ce00/ruff-0.17.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68074094f55ca3d29ab19491f640282f7a84061c4c7977aa5094b968ca457e9a", size = 10075057, upload-time = "2026-10-27T00:33:52.074Z" },
{ url = "https://files.pythonhosted.org/packages/b0/ed/93ff7a5909ee56e2bd4b982deae9e0efb2724417874a90c9e41ad66c6dd7/ruff-0.17.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27bb571ac78fef178fe5118d2827df95c2aae74a48fff2b15f6acd03af5ff5bb", size = 10022431, upload-time = "2026-10-27T00:33:54.061Z" },
{ url = "https://files.pythonhosted.org/packages/9b/07/a83b9eeec8c1c3c0e4eda15f06c3a91001bfd2687ea2f07b750719bf9712/ruff-0.17.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:029c37cbd3de48ca37aa12117bead2d065f15da17d66bf84e0d4274013705db2", size = 10394903, upload-time = "2026-10-27T00:33:56.158Z" },
{ url = "https://files.pythonhosted.org/packages/af/b2/05c39857c2526fcbebe46ad93a8dc97424656dfef14d3ef933abe1ffd331/ruff-0.17.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:692fdb46d0fd9c71e380947dc5fc72f2c3f881073bc1f739e4579376231affbe", size = 10799780, upload-time = "2026-10-27T00:33:58.204Z" },
{ url = "https://files.pythonhosted.org/packages/ec/d9/47171a16739eb7a353936b31c20cf54340531e68c9af7d72fd7a784e235d/ruff-0.17.3-py3-none-win32.whl", hash = "sha256:46539cb031b01fd29ae0a02142483c46640b233ee12bb1c1b690bffa1555d5ca", size = 9908481, upload-time = "2026-10-27T00:34:00.087Z" },
{ url = "https://files.pythonhosted.org/packages/d0/a0/f08c1631bfd81faa6a90eaf70c37c786c11bed2046302b56bb506c25e1a0/ruff-0.17.3-py3-none-win_amd64.whl", hash = "sha256:56b8ffb3f3b3cbccb8b76b71b38261b7ed982ef5d20f5b447b1b61a7f4949b24", size = 10341333, upload-time = "2026-10-27T00:34:02.099Z" },
{ url = "https://files.pythonhosted.org/packages/99/d5/4ad4ed31413c301430918e70d3d38ae6f097af0766cb9ab8c9d502b2a4d6/ruff-0.17.3-py3-none-win_arm64.whl", hash = "sha256:81fce74bcd6be0e9da7c67c7553fd0f1f12964a2052cd728ef917b937a5ca1d9", size = 9854332, upload-time = "2026-10-27T00:34:03.847Z" },
]
[[package]]
@@ -1196,6 +1293,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
]
[[package]]
name = "tenacity"
version = "9.1.2"
@@ -1260,28 +1366,37 @@ wheels = [
]
[[package]]
name = "ty"
version = "0.0.84"
name = "truststore"
version = "0.10.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/9f/c5201d42a484c061e528825fc8e2d565f5abd50a4ced6fb7d29c4ec99b2b/truststore-0.10.5.tar.gz", hash = "sha256:30d36967ccaded5cbb38d602c433f53600036c79d502f4533a49b60a03bbefcd", size = 28091, upload-time = "2026-10-12T22:27:31.808Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" },
{ url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" },
{ url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" },
{ url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" },
{ url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" },
{ url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" },
{ url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" },
{ url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" },
{ url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" },
{ url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" },
{ url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" },
{ url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" },
{ url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" },
{ url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" },
{ url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" },
{ url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" },
{ url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" },
{ url = "https://files.pythonhosted.org/packages/51/e9/3a7820be2bb0fe53b6bc9c3be26d3d1158004e4c3ab953aa6840b955b1e9/truststore-0.10.5-py3-none-any.whl", hash = "sha256:9aaaedaefaf06d8b206278cf8b5012bc897f485a874503501e12d776df78951c", size = 19017, upload-time = "2026-10-12T22:27:30.377Z" },
]
[[package]]
name = "ty"
version = "0.0.94"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8a/c7/1257c8c70e46274664db1eaf2b8aba99a1277470daaa39c65e7decd67c8f/ty-0.0.94.tar.gz", hash = "sha256:933d2feca6057742d04af00c090621c139e7be2c059aa38b040a70532fd5104b", size = 8106769, upload-time = "2026-10-28T18:06:09.045Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/27/bd/5145d91cf196cb20fc3b005515b21266a419ea7336d3d7891a1769439259/ty-0.0.94-py3-none-linux_armv6l.whl", hash = "sha256:c6463c045880ad17df911a53d367eb2554b17538ca54911bea6ccb20ec95ee11", size = 14528574, upload-time = "2026-10-28T18:05:19.423Z" },
{ url = "https://files.pythonhosted.org/packages/29/0e/2b563132429b5fc6044095061fde4584548cb55abf0d6d9f2829c41ab226/ty-0.0.94-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec3a3892f4290306c674586149668097cf44f13e8b381ba388662f5a440b459b", size = 14067146, upload-time = "2026-10-28T18:05:23.014Z" },
{ url = "https://files.pythonhosted.org/packages/3d/e0/3a458362ea33ca9756cd6b914f905b3dec007d329b21d83e859ecee65a63/ty-0.0.94-py3-none-macosx_11_0_arm64.whl", hash = "sha256:74ddc6eeecd3251cf8980c9abb960d7eeab649dac5cdb7504910d99c479f84ab", size = 13148266, upload-time = "2026-10-28T18:05:25.769Z" },
{ url = "https://files.pythonhosted.org/packages/cd/38/5380bdaad2745a4e191deacce96403af74d1992ed8782d5766c301c32b50/ty-0.0.94-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31bdc3aace142400d7e22b4d32676c063ec42f0437d77940012c3dc60587ecdd", size = 13973583, upload-time = "2026-10-28T18:05:28.731Z" },
{ url = "https://files.pythonhosted.org/packages/1d/bc/7e1dc4f2594010aff6d1e2e34d0ff9003736210983be5720a04d69c27e03/ty-0.0.94-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fecacfe9c41ed1109aa503ed172d84b1b0fc283ee49aaf078d298dbe1c62e75f", size = 14269235, upload-time = "2026-10-28T18:05:31.697Z" },
{ url = "https://files.pythonhosted.org/packages/7b/61/43041f1c53164d6be21ace55047feffeaa8a9709782b01b06c951412b900/ty-0.0.94-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a49762bbc97a732847da8350ad0227c99ce2675fa32e3f58aa9f9ff4a81e4c14", size = 15191720, upload-time = "2026-10-28T18:05:34.455Z" },
{ url = "https://files.pythonhosted.org/packages/22/72/f425d48b13cbf12b92e4f837a5866e9788e9ef4d5a071e2b12f54ee94da4/ty-0.0.94-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76a7b3aea839dfafb4bc068af918e0292b06ac26ea2a43f7ab3dd65548c0ed50", size = 15596604, upload-time = "2026-10-28T18:05:37.377Z" },
{ url = "https://files.pythonhosted.org/packages/13/ea/bd2da666ad4af596886833b095098fa9ff93685285f48bebc4d3865126e0/ty-0.0.94-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f032ce5a9c0c7033a5a8e748f952ece2e22afe2b148d211d05dca2cba8b28000", size = 15572711, upload-time = "2026-10-28T18:05:40.499Z" },
{ url = "https://files.pythonhosted.org/packages/44/f8/4f07eac57e9b60f49cc222d5048d9a61ac7f0902964ec8ac8d4ea8bc521d/ty-0.0.94-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc5e780c2107c1b83a21a394076da328e05a293ce06298df40ecab1ddc31d371", size = 16309102, upload-time = "2026-10-28T18:05:43.539Z" },
{ url = "https://files.pythonhosted.org/packages/22/39/c2ac7e14eb8ca59bd3c71b8c3055294524df84c476ba88545f59c11ceaf9/ty-0.0.94-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5bc969cdabf85aa66987c9b4818072e3af17ce5f2b7bc93550ab896c904c6c40", size = 15073645, upload-time = "2026-10-28T18:05:46.65Z" },
{ url = "https://files.pythonhosted.org/packages/d1/7d/dd4935abf50a97ccc1f46cf6c921a7acdab30f102c3c56aa4e8d6cbc3fa9/ty-0.0.94-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:27bbaff0d7bbcb3e92ed7cab07d6b26dc7ed7fa28ff69910324e5cc347c7b256", size = 13936242, upload-time = "2026-10-28T18:05:49.726Z" },
{ url = "https://files.pythonhosted.org/packages/d6/f0/b65fa6ccde2d629e25031bf2cc2d21ac7b50ee6e0e3d29a5f471c4686c54/ty-0.0.94-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d0960eb71220300655d67db91073fd180f385186b1d186e76faa2fd3ae5bd10f", size = 14280351, upload-time = "2026-10-28T18:05:52.521Z" },
{ url = "https://files.pythonhosted.org/packages/d6/d2/5c901ad5e4f5444852b002b1f4dd3a0b470039305c688facc0483bfccd1a/ty-0.0.94-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab04c14bca92ab92106ceaa19a962a80998389700cf7938576ba3936a304355d", size = 14611163, upload-time = "2026-10-28T18:05:55.208Z" },
{ url = "https://files.pythonhosted.org/packages/6d/99/37d8633e9b5d1dcf1b06f12466825ff0c4028664994e573c352546ee9cbd/ty-0.0.94-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a92ebefa092e6bd58cb5719626858df744575473e2de0cd1c44abd44b9b84b57", size = 14938978, upload-time = "2026-10-28T18:05:58.17Z" },
{ url = "https://files.pythonhosted.org/packages/51/13/804b1f993bf84a819385f8b440fbc4b894499e2c20d865cd74d69327e7b8/ty-0.0.94-py3-none-win32.whl", hash = "sha256:3212dcf30b7567f2bb65a83cdfc4818a5385f82e7afddd83548b0d84e759057e", size = 13448009, upload-time = "2026-10-28T18:06:01.111Z" },
{ url = "https://files.pythonhosted.org/packages/a1/23/81850750cda0ea781ba9d6e08cea1a1039361cb3474c7dd03765bbc4b51f/ty-0.0.94-py3-none-win_amd64.whl", hash = "sha256:daf5bafe8e7d21b8a99a56fc8518f2342a2dbcaa370a5c25cfaedc684088d158", size = 14010576, upload-time = "2026-10-28T18:06:03.773Z" },
{ url = "https://files.pythonhosted.org/packages/d7/d4/ac3a759556493230823abd31e149c95401f96b9cac36567bd403a8454ea3/ty-0.0.94-py3-none-win_arm64.whl", hash = "sha256:ce344353ccdc542cfcfd6abe1481cf23c005f483411ea79652b78ce0beae36f2", size = 13547398, upload-time = "2026-10-28T18:06:06.379Z" },
]
[[package]]
@@ -5,5 +5,5 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langgraph==1.2.6"
"langgraph==1.2.19"
]
@@ -7,7 +7,7 @@ requires-python = ">=3.10"
dependencies = [
"langchain-openai==1.1.14",
"langchain-anthropic==1.4.6",
"langgraph==1.2.6"
"langgraph==1.2.19"
]
[tool.uv]
+9 -9
View File
@@ -21,8 +21,8 @@
"test:all": "yarn test && yarn test:int && yarn lint:langgraph"
},
"dependencies": {
"@langchain/core": "^1.2.13",
"@langchain/langgraph": "^1.4.18"
"@langchain/core": "^1.3.1",
"@langchain/langgraph": "^1.5.3"
},
"resolutions": {
"@langchain/langgraph-checkpoint": "1.0.4",
@@ -34,17 +34,17 @@
"@eslint/js": "^10.0.1",
"@tsconfig/recommended": "^1.0.13",
"@types/jest": "^30.0.0",
"@typescript-eslint/eslint-plugin": "^8.70.1",
"@typescript-eslint/parser": "^8.70.1",
"dotenv": "^18.0.4",
"eslint": "^10.11.0",
"@typescript-eslint/eslint-plugin": "^8.72.1",
"@typescript-eslint/parser": "^8.72.1",
"dotenv": "^18.0.9",
"eslint": "^10.13.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-no-instanceof": "^1.0.1",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-prettier": "^5.5.7",
"jest": "^30.5.2",
"prettier": "^3.9.9",
"ts-jest": "^29.4.14",
"prettier": "^3.9.10",
"ts-jest": "^29.4.15",
"typescript": "^7.0.2"
}
}
+129 -126
View File
@@ -387,6 +387,11 @@
resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6"
integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==
"@dual-bundle/import-meta-resolve@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.2.1.tgz#cd0b25b3808cd9e684cd6cd549bbf8e1dcf05ee7"
integrity sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==
"@emnapi/core@1.10.0":
version "1.10.0"
resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.10.0.tgz#380ccc8f2412ea22d1d972df7f8ee23a3b9c7467"
@@ -890,50 +895,50 @@
resolved "https://registry.yarnpkg.com/@keyv/serialize/-/serialize-1.1.1.tgz#0c01dd3a3483882af7cf3878d4e71d505c81fc4a"
integrity sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==
"@langchain/core@^1.2.13":
version "1.2.13"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.2.13.tgz#4ea4eb0308a25af350f75de2fbf34aecb819177e"
integrity sha512-ADGTxZ84n3civruUX1LlTOdua/PDGoni2U6TmKTX7hvRU2Jlepu8vLJI4Wnwj45KUKhUCrW94Il12Q2bxE3e8A==
"@langchain/core@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.3.1.tgz#f6cdb6d200def5c2e69d25ff4fef92612d1b7601"
integrity sha512-jt0miXWxNi+rbZLeSeZkc7BBYKnWopksoJSpB+P3zaYgzMMNwkAPWqnd1zSClfYGwLZPXQbbTtaqv8ZFXmE3FA==
dependencies:
"@cfworker/json-schema" "^4.0.2"
"@standard-schema/spec" "^1.1.0"
js-tiktoken "^1.0.12"
langsmith ">=0.5.0 <1.0.0"
langsmith ">=0.11.0 <1.0.0"
mustache "^4.2.0"
p-queue "^6.6.2"
zod "^3.25.76 || ^4"
"@langchain/langgraph-checkpoint@1.0.4", "@langchain/langgraph-checkpoint@^1.1.5":
"@langchain/langgraph-checkpoint@1.0.4", "@langchain/langgraph-checkpoint@^1.2.1":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.4.tgz#893aa7255b2fe810fdeb50281f6750a3b0a6fef7"
integrity sha512-1y5MgZ0gXXrtmoy56e3kaBChI3GwFPIKl27xkrHwN+VE/3iUsyr9gO3Jtp7kdKAe6diZGbcas5bdC/r0yUwTZA==
dependencies:
uuid "^14.0.0"
"@langchain/langgraph-sdk@~1.12.0":
version "1.12.0"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.12.0.tgz#b063a7eba0a66f51cbc841a824626a47eca1eec9"
integrity sha512-F3AOZjKZRUGmE3FzY+RaVZI6WzXOkwnuF7jqgto6rDPSnO9OdVdYGvwGDi3jjRGf5xLoDtX2dpsR9z5KptU+5A==
"@langchain/langgraph-sdk@~1.12.8":
version "1.12.9"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.12.9.tgz#b8e66e00af207f18e56eb6cd916c1ee8e7608745"
integrity sha512-A1LkLPVu00dj+9aErXPBLhmFURHtnnRO9KRFST+xAwurqQTa+LxC9ixxF7p+ZMo5P70qMFoaP8Ud2wuZmYD4xw==
dependencies:
"@langchain/protocol" "^0.0.19"
"@langchain/protocol" "^0.0.20"
"@types/json-schema" "^7.0.15"
p-queue "^9.0.1"
p-retry "^7.1.1"
"@langchain/langgraph@^1.4.18":
version "1.4.18"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.4.18.tgz#c8cc4ea734834262fdb00ae6a45a0849620e4fd2"
integrity sha512-yrMMJ9hk2NVMD2xU2WoVrgFawAU6s/RzEl/dX9BhlyxZHazo1wHY35z4K2BIBzTUh4z3giCzrUoqRAqW2CWpWg==
"@langchain/langgraph@^1.5.3":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.5.3.tgz#9a921c9e099c2e3009f34987e0acb2645636bb74"
integrity sha512-cuqswNgfoF/J3/r9K5Qm7EnQc2f0mz7uWlAAs751KWhoK8sKfmuLABghF4b1LYq1NpK72XG1x8lWPyg45M8BOA==
dependencies:
"@langchain/langgraph-checkpoint" "^1.1.5"
"@langchain/langgraph-sdk" "~1.12.0"
"@langchain/protocol" "^0.0.19"
"@langchain/langgraph-checkpoint" "^1.2.1"
"@langchain/langgraph-sdk" "~1.12.8"
"@langchain/protocol" "^0.0.20"
"@standard-schema/spec" "1.1.0"
"@langchain/protocol@^0.0.19":
version "0.0.19"
resolved "https://registry.yarnpkg.com/@langchain/protocol/-/protocol-0.0.19.tgz#7fe43fed115dc34b3b4c246e8d5283fbcbfab7b0"
integrity sha512-9hKcRrH7cBX6gfutdfXPoft1OCchHe4FEpALoDJMl5Qu+n/YG5ynZmyu8+8cxORlPwHBoKTxggvXz+76M1yX1Q==
"@langchain/protocol@^0.0.20":
version "0.0.20"
resolved "https://registry.yarnpkg.com/@langchain/protocol/-/protocol-0.0.20.tgz#441a57a6176f634306d9214da65d35033355d5e9"
integrity sha512-sxlkDG2/HGyjDFLTJ8B/o3oglRjOv8iLCFgrV0550/Vym3V6RZ0SUjAQFe8jrLQw8ZXjERy3swJp5OODBSrUYA==
"@napi-rs/wasm-runtime@^1.1.4":
version "1.2.3"
@@ -1180,110 +1185,100 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz#d404bac4cb04e4dcd20086e46e13c952bd0771e0"
integrity sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==
"@typescript-eslint/eslint-plugin@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.1.tgz#95e29de63ce7ed793eb1c6efd9f8b802ce808262"
integrity sha512-5CokTWXpmbylfFabsbvHC0CAw3XLaGnoa+nFJX5FR592h/LoZsENUE6p/rg5xvkKrFEXz6pYmgI29AgXc2N3BQ==
dependencies:
"@eslint-community/regexpp" "^4.12.2"
"@typescript-eslint/scope-manager" "8.70.1"
"@typescript-eslint/type-utils" "8.70.1"
"@typescript-eslint/utils" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/scope-manager" "8.72.1"
"@typescript-eslint/type-utils" "8.72.1"
"@typescript-eslint/utils" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.1"
ignore "^7.0.5"
natural-compare "^1.4.0"
ts-api-utils "^2.5.0"
"@typescript-eslint/parser@^8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.70.1.tgz#6c99b7b954efc05dae179001c8b477d1a8948dcf"
integrity sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==
"@typescript-eslint/parser@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.72.1.tgz#7370b68dadc3819b9aac68ea340bd8b43ee62379"
integrity sha512-ZGwZP4OCfyA0AQTpEAMWz+rtX01DRhQWW5hB0ucKDEIX5U7jdmPSaMx94rQfaPAQzIYz2BimOgKOJtjfpRFQ9g==
dependencies:
"@typescript-eslint/scope-manager" "8.70.1"
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/typescript-estree" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/scope-manager" "8.72.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/typescript-estree" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.1"
debug "^4.4.3"
"@typescript-eslint/project-service@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.70.1.tgz#d5635594cc8a573f0e15a772b1704e93a00134c1"
integrity sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==
"@typescript-eslint/project-service@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.72.1.tgz#1d09902a9c23f6cfab779e5acc78a45abf540add"
integrity sha512-fAuVMkwdO/huI8nyo/WKnNKf6Gag93S56Rh3Ts5mGcavwSdaN8tRg1gCeIGX8PF+nzp1BjGfO4gHhGzW1J6jTg==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.70.1"
"@typescript-eslint/types" "^8.70.1"
"@typescript-eslint/tsconfig-utils" "^8.72.1"
"@typescript-eslint/types" "^8.72.1"
debug "^4.4.3"
"@typescript-eslint/scope-manager@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz#0feeae4da2d279b960cdbfe5e0a02fbf1195e93f"
integrity sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==
"@typescript-eslint/scope-manager@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.72.1.tgz#a0fb4dfe5aa2b0149b7a2d1fd00d947992b13067"
integrity sha512-lC/t6eMCZZQAAKZEYGL6RDVSuFehpdmMT6h+nJIjIa6oylN/PwDgDVtEIbB7O9xFfZYtZAYLoLlDNWKJhtF+uQ==
dependencies:
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.1"
"@typescript-eslint/tsconfig-utils@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz#e9353a6e5f3498a29b40946ca3e11f6166fef09e"
integrity sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==
"@typescript-eslint/tsconfig-utils@8.72.1", "@typescript-eslint/tsconfig-utils@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.1.tgz#07394c85095ee38455e76bc221ba23650af73e44"
integrity sha512-e4D5R5vpeAEPBxT2BAzsFQL3sbwyeeeo5+aczgNSRK1V734jE7pTqaGFU+fXapWEQwnZbpFaTn32+Z8DwS6OTg==
"@typescript-eslint/tsconfig-utils@^8.70.1":
version "8.71.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.0.tgz#83a69c9a514af8f5ff099cc6c0d609c273e8f039"
integrity sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==
"@typescript-eslint/type-utils@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz#6ea36f2c7dc0776c1c8e1c380b4896f00bc4c27f"
integrity sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==
"@typescript-eslint/type-utils@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.72.1.tgz#f1f592f63bd9726de21191aae3ccf64a813914b8"
integrity sha512-iY2wlmyrXJmx4yxUGP3aYCFF3cA/6ShddOKmLU2dx7LnEykbAM8WThlxZEfd1NddqceF6eJmZUayxx9oKIYNlg==
dependencies:
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/typescript-estree" "8.70.1"
"@typescript-eslint/utils" "8.70.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/typescript-estree" "8.72.1"
"@typescript-eslint/utils" "8.72.1"
debug "^4.4.3"
ts-api-utils "^2.5.0"
"@typescript-eslint/types@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.70.1.tgz#2022b9d07d057174eba25b9bbd289641d262a196"
integrity sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==
"@typescript-eslint/types@8.72.1", "@typescript-eslint/types@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.72.1.tgz#067d8f738c294611e33a1425ae43ac9b9c2f24e3"
integrity sha512-LmE5IhQet1BWOWEXqBrVtBPsMAOQ6Nzk35d+WLh3mk4EUF33sqlfxjio1MdYeSvKSG5skactKKuixBIKXhL4Rw==
"@typescript-eslint/types@^8.70.1":
version "8.71.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.71.0.tgz#a230a121e71665060afe27d1f1ad110371f9451e"
integrity sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==
"@typescript-eslint/typescript-estree@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz#ced48fee6f7043835d2edfed9786ae74813a3c99"
integrity sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==
"@typescript-eslint/typescript-estree@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.1.tgz#e93b9cc3a56bf12f5fed857bb8f4963cce7924ee"
integrity sha512-DAlUoUV93hIvrtIQLwCbmZQYVZwOMhiHJrOVUUIWicFZaIYjTRqlY3I/NZevfkLmSqkNHRu2qYDcq9lrn16oOQ==
dependencies:
"@typescript-eslint/project-service" "8.70.1"
"@typescript-eslint/tsconfig-utils" "8.70.1"
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/project-service" "8.72.1"
"@typescript-eslint/tsconfig-utils" "8.72.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.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.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.70.1.tgz#6a97887cee702df513692f5cdc7f79baeef48e02"
integrity sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==
"@typescript-eslint/utils@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.72.1.tgz#f210c1bf4a25aa1306df80c702ed30bbbc9b4d3d"
integrity sha512-5v5liypurJBFHCBNqh/RjsymAD+VWcE0djuGY/BUqp5fC3Qn+luWDEksjuTtWjPn6i5zQlUtkZCRG/CYG0yCkA==
dependencies:
"@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.70.1"
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/typescript-estree" "8.70.1"
"@typescript-eslint/scope-manager" "8.72.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/typescript-estree" "8.72.1"
"@typescript-eslint/visitor-keys@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz#b4cde0ed16982bd963a6310fcae554f59fd14124"
integrity sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==
"@typescript-eslint/visitor-keys@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.1.tgz#c7aec9883b44ef795f1b8200df81d9d44d355de7"
integrity sha512-SB8xqipSzZHUePCCLNeYxqnxq83X61/qzNhjQGbTfjxGZIeOEP7tBgWYX05Tcd6CgE02WdTJnh5b8kMZJ/+bnQ==
dependencies:
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/types" "8.72.1"
eslint-visitor-keys "^5.0.0"
"@typescript/typescript-aix-ppc64@7.0.2":
@@ -2109,10 +2104,10 @@ doctrine@^2.1.0:
dependencies:
esutils "^2.0.2"
dotenv@^18.0.4:
version "18.0.4"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-18.0.4.tgz#419bbe784f30915bfeb10e2d407e13ca5528504f"
integrity sha512-jGt9uI5BIxpVO15kvy+iEFqNAwxYClKWnkptLjSNPnFLC003Imd0ofr3aEVsbpHdSBdSsbcApNIlz4nbyGMwdA==
dotenv@^18.0.9:
version "18.0.9"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-18.0.9.tgz#5c565760ce7e6fb69f9477ebcd1a3b64c766f9c8"
integrity sha512-3fVOW4cB02R066PUSwn8O4TJyz9NckQl7F81blIbaq9jKBm2tSmOKky7ZHIXzGvkVPXCSn6jkpziBPZdGFs8Tw==
dunder-proto@^1.0.0, dunder-proto@^1.0.1:
version "1.0.1"
@@ -2430,13 +2425,13 @@ eslint-plugin-no-instanceof@^1.0.1:
resolved "https://registry.yarnpkg.com/eslint-plugin-no-instanceof/-/eslint-plugin-no-instanceof-1.0.1.tgz#5d9fc86d160df6991b654b294a62390207f1bb97"
integrity sha512-zlqQ7EsfzbRO68uI+p8FIE7zYB4njs+nNbkNjSb5QmLi2et67zQLqSeaao5U9SpnlZTTJC87nS2oyHo2ACtajw==
eslint-plugin-prettier@^5.5.6:
version "5.5.6"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz#363ebe4d769bce157ccdd8129ce3efd91dc62564"
integrity sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==
eslint-plugin-prettier@^5.5.7:
version "5.5.7"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.7.tgz#ebef3acc2d664333b1356b717780198abc635398"
integrity sha512-WjHyn3GrFd+ZR0rzsLiM4zJZ35PZMOE0SUIxzGmnWNG0126yNfJKJzObmmkYDwHJfGlkz3PSlzyG9ez/LJntwg==
dependencies:
prettier-linter-helpers "^1.0.1"
synckit "^0.11.13"
synckit "^0.12.3"
eslint-scope@^9.1.2:
version "9.1.2"
@@ -2463,10 +2458,10 @@ 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.11.0:
version "10.11.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.11.0.tgz#304d1591b7c6a327e3f64b4f550f99fda160ae20"
integrity sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==
eslint@^10.13.0:
version "10.13.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.13.0.tgz#a8a4742648c0fa9131f429b509de5407682d4864"
integrity sha512-UohDjD4zSzEwhf58M3O7vc/ycc8O1I7dNlhtD0WxE8Px8EHNJKtUwzBqrzeGhisRuv2ag4sAYswszCJRgKXYUg==
dependencies:
"@eslint-community/eslint-utils" "^4.8.0"
"@eslint-community/regexpp" "^4.12.2"
@@ -2866,7 +2861,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.9:
handlebars@^4.7.10:
version "4.7.10"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.10.tgz#12419ea1b41391b3ffcf003f2164dcbce090c417"
integrity sha512-P5VJMVM7qgBn6vjXMw8WG9uVI+ncf2pi72j4de4yz5ZULLj2RGqLYaKOYGsgyrViQ0tePOVlN1tDCCXXtFqXKg==
@@ -3854,10 +3849,10 @@ keyv@^5.6.0:
dependencies:
"@keyv/serialize" "^1.1.1"
"langsmith@>=0.5.0 <1.0.0":
version "0.6.3"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.6.3.tgz#a3d8ad58d66a47d3697e3c69b2be3f6df5233190"
integrity sha512-pXrQ4/4myQvjFFOAUmt5pWRrLEZR20gzIJD7MNdUH+5/S5nLI4ZRBo/SYKC6coaYj9pYTfQdBIzcs+3kfJ5uDA==
"langsmith@>=0.11.0 <1.0.0":
version "0.11.6"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.11.6.tgz#cdc0f89a14632f8b172deea5daa1ee625f7442c3"
integrity sha512-5hfJM5DEZpXFxUS3U9nY2CMyBRmFnhkJj7znUODJ8vsr/Jx38tGJl4GIhV484xAY7yQFWrFOTDr33PB95kHUKg==
dependencies:
p-queue "6.6.2"
@@ -4312,10 +4307,10 @@ prettier-linter-helpers@^1.0.1:
dependencies:
fast-diff "^1.1.2"
prettier@^3.9.9:
version "3.9.9"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.9.tgz#09b826918c91cd4cbc80e0cbd1d2a922ff04f233"
integrity sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==
prettier@^3.9.10:
version "3.9.10"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.10.tgz#ac378e432f1a7381518f2e080816bbaefa38d585"
integrity sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==
pretty-format@30.2.0, pretty-format@^30.0.0:
version "30.2.0"
@@ -4759,13 +4754,21 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
synckit@^0.11.13, synckit@^0.11.8:
synckit@^0.11.8:
version "0.11.13"
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.13.tgz#062a5ea57d81befc35892f8254de5c567e97c80a"
integrity sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==
dependencies:
"@pkgr/core" "^0.3.6"
synckit@^0.12.3:
version "0.12.3"
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.12.3.tgz#6e30a3441d095ed04c311077350ce205465512d6"
integrity sha512-U+/AXT6LWsxnj2vz9HeUuPWBNgdWxjyIEjEPdbJARC9txmgPzo2VEdqnFo4hHaNyMPtycyVJrSaQYeQv/PoyyA==
dependencies:
"@dual-bundle/import-meta-resolve" "^4.2.1"
"@pkgr/core" "^0.3.6"
test-exclude@^7.0.1:
version "7.0.2"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.2.tgz#482392077630bc57d5630c13abe908bb910dfc65"
@@ -4800,14 +4803,14 @@ 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.14:
version "29.4.14"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.14.tgz#cb36c043df0fada861c8b604a5d514f953126180"
integrity sha512-+943G6wCX5E3VpzTQhBGBFntCmdT+QAkTvUlp03edTONFBvqskuXPhpyX16xdUotO4P5HQKyppldSWjocoltLQ==
ts-jest@^29.4.15:
version "29.4.15"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.15.tgz#238f2d495066a33ca973a54a36a5ef228262a9ad"
integrity sha512-s2qmEUYFRqbjz1wv0EmccNPVOb45HOFpFuhzQJLKShYVisuTbCFPIIAIV2EuK5rIxb4BejLDbH7dnBZfYjybUQ==
dependencies:
bs-logger "^0.2.6"
fast-json-stable-stringify "^2.1.0"
handlebars "^4.7.9"
handlebars "^4.7.10"
json5 "^2.2.3"
lodash.memoize "^4.1.2"
make-error "^1.3.6"
@@ -5217,6 +5220,6 @@ yocto-queue@^0.1.0:
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
"zod@^3.25.76 || ^4":
version "4.3.6"
resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a"
integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==
version "4.6.7"
resolved "https://registry.yarnpkg.com/zod/-/zod-4.6.7.tgz#e806cf64a7f4e37635ce9f2f52c04c3766f75e5b"
integrity sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==
@@ -9,8 +9,8 @@
},
"dependencies": {
"@js-monorepo-example/shared": "*",
"@langchain/core": "^1.2.13",
"@langchain/langgraph": "^1.4.18"
"@langchain/core": "^1.3.1",
"@langchain/langgraph": "^1.5.3"
},
"devDependencies": {
"typescript": "^7.0.2"
+6 -6
View File
@@ -17,18 +17,18 @@
"lint": "eslint 'apps/**/*.ts' 'libs/**/*.ts'"
},
"devDependencies": {
"turbo": "^2.11.5",
"turbo": "^2.11.10",
"typescript": "^7.0.2",
"@tsconfig/recommended": "^1.0.13",
"@eslint/eslintrc": "^3.3.7",
"@eslint/js": "^10.0.1",
"eslint": "^10.11.0",
"eslint": "^10.13.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-no-instanceof": "^1.0.1",
"eslint-plugin-prettier": "^5.5.6",
"@typescript-eslint/eslint-plugin": "^8.70.1",
"@typescript-eslint/parser": "^8.70.1",
"prettier": "^3.9.9"
"eslint-plugin-prettier": "^5.5.7",
"@typescript-eslint/eslint-plugin": "^8.72.1",
"@typescript-eslint/parser": "^8.72.1",
"prettier": "^3.9.10"
}
}
+152 -156
View File
@@ -25,6 +25,11 @@
resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6"
integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==
"@dual-bundle/import-meta-resolve@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.2.1.tgz#cd0b25b3808cd9e684cd6cd549bbf8e1dcf05ee7"
integrity sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==
"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595"
@@ -134,48 +139,48 @@
resolved "https://registry.yarnpkg.com/@keyv/serialize/-/serialize-1.1.1.tgz#0c01dd3a3483882af7cf3878d4e71d505c81fc4a"
integrity sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==
"@langchain/core@^1.2.13":
version "1.2.13"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.2.13.tgz#4ea4eb0308a25af350f75de2fbf34aecb819177e"
integrity sha512-ADGTxZ84n3civruUX1LlTOdua/PDGoni2U6TmKTX7hvRU2Jlepu8vLJI4Wnwj45KUKhUCrW94Il12Q2bxE3e8A==
"@langchain/core@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.3.1.tgz#f6cdb6d200def5c2e69d25ff4fef92612d1b7601"
integrity sha512-jt0miXWxNi+rbZLeSeZkc7BBYKnWopksoJSpB+P3zaYgzMMNwkAPWqnd1zSClfYGwLZPXQbbTtaqv8ZFXmE3FA==
dependencies:
"@cfworker/json-schema" "^4.0.2"
"@standard-schema/spec" "^1.1.0"
js-tiktoken "^1.0.12"
langsmith ">=0.5.0 <1.0.0"
langsmith ">=0.11.0 <1.0.0"
mustache "^4.2.0"
p-queue "^6.6.2"
zod "^3.25.76 || ^4"
"@langchain/langgraph-checkpoint@^1.1.5":
version "1.1.5"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.1.5.tgz#c793177f9afcce31a1e9922f317f88fec1254282"
integrity sha512-BwDwl5VeTOh6CVuiIPgsUgfK51vTJDMSbFcSCUfjJWsl8/DPdK/mbv+ejxJstkSk/BlSPMP4JfXWcN6jD2ea2Q==
"@langchain/langgraph-checkpoint@^1.2.1":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.2.2.tgz#cbfdf0d461385769665e26f70e41f1291ed1d37e"
integrity sha512-KL3nzi7I61ll5nANCQe5RoJ5SqTFrE+si9IK6k7IIj9OrPkIKT/Dz0w8CyJF+K7OEuJfA33HvPrZqfzzW1DXDA==
"@langchain/langgraph-sdk@~1.12.0":
version "1.12.0"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.12.0.tgz#b063a7eba0a66f51cbc841a824626a47eca1eec9"
integrity sha512-F3AOZjKZRUGmE3FzY+RaVZI6WzXOkwnuF7jqgto6rDPSnO9OdVdYGvwGDi3jjRGf5xLoDtX2dpsR9z5KptU+5A==
"@langchain/langgraph-sdk@~1.12.8":
version "1.12.9"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.12.9.tgz#b8e66e00af207f18e56eb6cd916c1ee8e7608745"
integrity sha512-A1LkLPVu00dj+9aErXPBLhmFURHtnnRO9KRFST+xAwurqQTa+LxC9ixxF7p+ZMo5P70qMFoaP8Ud2wuZmYD4xw==
dependencies:
"@langchain/protocol" "^0.0.19"
"@langchain/protocol" "^0.0.20"
"@types/json-schema" "^7.0.15"
p-queue "^9.0.1"
p-retry "^7.1.1"
"@langchain/langgraph@^1.4.18":
version "1.4.18"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.4.18.tgz#c8cc4ea734834262fdb00ae6a45a0849620e4fd2"
integrity sha512-yrMMJ9hk2NVMD2xU2WoVrgFawAU6s/RzEl/dX9BhlyxZHazo1wHY35z4K2BIBzTUh4z3giCzrUoqRAqW2CWpWg==
"@langchain/langgraph@^1.5.3":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.5.3.tgz#9a921c9e099c2e3009f34987e0acb2645636bb74"
integrity sha512-cuqswNgfoF/J3/r9K5Qm7EnQc2f0mz7uWlAAs751KWhoK8sKfmuLABghF4b1LYq1NpK72XG1x8lWPyg45M8BOA==
dependencies:
"@langchain/langgraph-checkpoint" "^1.1.5"
"@langchain/langgraph-sdk" "~1.12.0"
"@langchain/protocol" "^0.0.19"
"@langchain/langgraph-checkpoint" "^1.2.1"
"@langchain/langgraph-sdk" "~1.12.8"
"@langchain/protocol" "^0.0.20"
"@standard-schema/spec" "1.1.0"
"@langchain/protocol@^0.0.19":
version "0.0.19"
resolved "https://registry.yarnpkg.com/@langchain/protocol/-/protocol-0.0.19.tgz#7fe43fed115dc34b3b4c246e8d5283fbcbfab7b0"
integrity sha512-9hKcRrH7cBX6gfutdfXPoft1OCchHe4FEpALoDJMl5Qu+n/YG5ynZmyu8+8cxORlPwHBoKTxggvXz+76M1yX1Q==
"@langchain/protocol@^0.0.20":
version "0.0.20"
resolved "https://registry.yarnpkg.com/@langchain/protocol/-/protocol-0.0.20.tgz#441a57a6176f634306d9214da65d35033355d5e9"
integrity sha512-sxlkDG2/HGyjDFLTJ8B/o3oglRjOv8iLCFgrV0550/Vym3V6RZ0SUjAQFe8jrLQw8ZXjERy3swJp5OODBSrUYA==
"@pkgr/core@^0.3.6":
version "0.3.6"
@@ -197,35 +202,35 @@
resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.13.tgz#269fce3ad04ca70b93269ff44cca81b950f542da"
integrity sha512-sySRuBfMKyKO/j2ZAhR8kSembhjuPEV4Ra3AHtmWLq51+iGaudr45crPSzNC5b7/Ctrh9dfUpBuTlYrH6rM58Q==
"@turbo/darwin-64@2.11.5":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@turbo/darwin-64/-/darwin-64-2.11.5.tgz#6ddc580c2b7d05e578ca1c06b3f61473f4b7b00e"
integrity sha512-KdY4y7BBjIisl+jAkb3FFaRjClGqHyaeHCcVcCT8LBzFFwUu6thgfxyRZAx0uL4KoxLmH+iOan8UhVf/aDDcaA==
"@turbo/darwin-64@2.11.10":
version "2.11.10"
resolved "https://registry.yarnpkg.com/@turbo/darwin-64/-/darwin-64-2.11.10.tgz#824aa74de92806ef2a21932b4148feaea98e8887"
integrity sha512-By4idRap5+nLnMuRzLB4qkOPRlvNdyY1ne4VJYDyUoUB11H94k7uif9mbe3JT8JSNXs1D68uMcBj6hJ/jMQRvA==
"@turbo/darwin-arm64@2.11.5":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@turbo/darwin-arm64/-/darwin-arm64-2.11.5.tgz#a756c67bb828cd00d03e909769dd39453c1f1e69"
integrity sha512-ToZDGi2u410+TfRtC/O1/LpujGgvNJoRBZnwVXob7oO0+3OSlwIO3zofXKUvwQvlba7a8ZXPyCZwufjAXd7f0w==
"@turbo/darwin-arm64@2.11.10":
version "2.11.10"
resolved "https://registry.yarnpkg.com/@turbo/darwin-arm64/-/darwin-arm64-2.11.10.tgz#e9de7c9b9eb2a428cd4fb63fb9826696107339a4"
integrity sha512-pyEYlVcuexOPa92p0Wlwl9YT1Is5D60lxN3ItWm3g30YLvFaUq86TF4BBWg5ZAc0uMTnrOzCvuCsOBTdzUWnRQ==
"@turbo/linux-64@2.11.5":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@turbo/linux-64/-/linux-64-2.11.5.tgz#b95c05218aebb06c06e5f991d48c55b78dc5e005"
integrity sha512-Ub2d/nAbdWLaSytcEaPp1wm/YuIvHEIqNrCRMI/UgOGyFdOhgxm8VyWPAJilQWRYxYVrqEUoy2K5S5K8SG50rA==
"@turbo/linux-64@2.11.10":
version "2.11.10"
resolved "https://registry.yarnpkg.com/@turbo/linux-64/-/linux-64-2.11.10.tgz#e9f48a6383e7c6926a488f329343792de3ee537c"
integrity sha512-1tYeR/IDZ5HEdsb0jNq2L2xam5nMQ+g80tlV2BZmv4OFw+pMcUM0mMmUcfc5wXPPNHVNPuDMjh9Y8G8HgXPLZA==
"@turbo/linux-arm64@2.11.5":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@turbo/linux-arm64/-/linux-arm64-2.11.5.tgz#c7a1d1a44c8c8dcff4b72fbafe9a5059c8fb668a"
integrity sha512-JQjmGxp724Wuft9FAxPMtmBur1CEdUyxJ+FVoIvKkgj63rXHl9PYCWrsU2tMjVi609XTQ3M8vZD9Yc9qVAVYrw==
"@turbo/linux-arm64@2.11.10":
version "2.11.10"
resolved "https://registry.yarnpkg.com/@turbo/linux-arm64/-/linux-arm64-2.11.10.tgz#d76831f850d30ef386b221b8ee72270a5ee7e2ba"
integrity sha512-HuekQxvVGhUd46g1Im/DKdOtglHr4/gguZAvzEpPUgtxNkztSa8lWhQEGQ+EmPQ0n7w19qjThFU+OfEEd0uvlw==
"@turbo/windows-64@2.11.5":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@turbo/windows-64/-/windows-64-2.11.5.tgz#60bdaaa7e8ba32f32623b31a1c75f6c3db400a0c"
integrity sha512-AFkbPmXNd8XgCi/66YEHR2bVl3gtusXyDihTIVEPAuSB+7ulLmJePvz1unWPVKLHdbCTTIdKq2mpJ1B1e3lJXw==
"@turbo/windows-64@2.11.10":
version "2.11.10"
resolved "https://registry.yarnpkg.com/@turbo/windows-64/-/windows-64-2.11.10.tgz#fb29a315a112afac9f01a4a1ea7601b28f140f31"
integrity sha512-UJOP3BkbreY3/JlMgJR6ZiimxYIBn+c+B4tnBIxD0E9c3Estmk+XoVNVBNBottZLNRXe3/ScgOU4+/k8tIWysg==
"@turbo/windows-arm64@2.11.5":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@turbo/windows-arm64/-/windows-arm64-2.11.5.tgz#18bd5a0ad30c4e094995786bd78916b6de6b5c1c"
integrity sha512-52ziXYP3snGOxjv19xA3WL1WDKv9Q6uDAa/7EKIR5j4hocMlYURkrOHbJ2ZZI+VfPU5MwKpcGeRfHxHJu7Y/Pw==
"@turbo/windows-arm64@2.11.10":
version "2.11.10"
resolved "https://registry.yarnpkg.com/@turbo/windows-arm64/-/windows-arm64-2.11.10.tgz#bac3077b0a39643043ece360ff1f4f2af9272106"
integrity sha512-xOkEjF3ws/b/YNlc+bvJHMYBn+vL+QbEISZFVi0qS+ImmhA1X3/gmNmBrjjIG3dg6aeN+n6gQitzSkXQ286qJQ==
"@types/esrecurse@^4.3.1":
version "4.3.1"
@@ -247,110 +252,100 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@typescript-eslint/eslint-plugin@^8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz#d404bac4cb04e4dcd20086e46e13c952bd0771e0"
integrity sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==
"@typescript-eslint/eslint-plugin@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.1.tgz#95e29de63ce7ed793eb1c6efd9f8b802ce808262"
integrity sha512-5CokTWXpmbylfFabsbvHC0CAw3XLaGnoa+nFJX5FR592h/LoZsENUE6p/rg5xvkKrFEXz6pYmgI29AgXc2N3BQ==
dependencies:
"@eslint-community/regexpp" "^4.12.2"
"@typescript-eslint/scope-manager" "8.70.1"
"@typescript-eslint/type-utils" "8.70.1"
"@typescript-eslint/utils" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/scope-manager" "8.72.1"
"@typescript-eslint/type-utils" "8.72.1"
"@typescript-eslint/utils" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.1"
ignore "^7.0.5"
natural-compare "^1.4.0"
ts-api-utils "^2.5.0"
"@typescript-eslint/parser@^8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.70.1.tgz#6c99b7b954efc05dae179001c8b477d1a8948dcf"
integrity sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==
"@typescript-eslint/parser@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.72.1.tgz#7370b68dadc3819b9aac68ea340bd8b43ee62379"
integrity sha512-ZGwZP4OCfyA0AQTpEAMWz+rtX01DRhQWW5hB0ucKDEIX5U7jdmPSaMx94rQfaPAQzIYz2BimOgKOJtjfpRFQ9g==
dependencies:
"@typescript-eslint/scope-manager" "8.70.1"
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/typescript-estree" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/scope-manager" "8.72.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/typescript-estree" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.1"
debug "^4.4.3"
"@typescript-eslint/project-service@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.70.1.tgz#d5635594cc8a573f0e15a772b1704e93a00134c1"
integrity sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==
"@typescript-eslint/project-service@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.72.1.tgz#1d09902a9c23f6cfab779e5acc78a45abf540add"
integrity sha512-fAuVMkwdO/huI8nyo/WKnNKf6Gag93S56Rh3Ts5mGcavwSdaN8tRg1gCeIGX8PF+nzp1BjGfO4gHhGzW1J6jTg==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.70.1"
"@typescript-eslint/types" "^8.70.1"
"@typescript-eslint/tsconfig-utils" "^8.72.1"
"@typescript-eslint/types" "^8.72.1"
debug "^4.4.3"
"@typescript-eslint/scope-manager@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz#0feeae4da2d279b960cdbfe5e0a02fbf1195e93f"
integrity sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==
"@typescript-eslint/scope-manager@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.72.1.tgz#a0fb4dfe5aa2b0149b7a2d1fd00d947992b13067"
integrity sha512-lC/t6eMCZZQAAKZEYGL6RDVSuFehpdmMT6h+nJIjIa6oylN/PwDgDVtEIbB7O9xFfZYtZAYLoLlDNWKJhtF+uQ==
dependencies:
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.1"
"@typescript-eslint/tsconfig-utils@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz#e9353a6e5f3498a29b40946ca3e11f6166fef09e"
integrity sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==
"@typescript-eslint/tsconfig-utils@8.72.1", "@typescript-eslint/tsconfig-utils@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.1.tgz#07394c85095ee38455e76bc221ba23650af73e44"
integrity sha512-e4D5R5vpeAEPBxT2BAzsFQL3sbwyeeeo5+aczgNSRK1V734jE7pTqaGFU+fXapWEQwnZbpFaTn32+Z8DwS6OTg==
"@typescript-eslint/tsconfig-utils@^8.70.1":
version "8.71.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.0.tgz#83a69c9a514af8f5ff099cc6c0d609c273e8f039"
integrity sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==
"@typescript-eslint/type-utils@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz#6ea36f2c7dc0776c1c8e1c380b4896f00bc4c27f"
integrity sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==
"@typescript-eslint/type-utils@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.72.1.tgz#f1f592f63bd9726de21191aae3ccf64a813914b8"
integrity sha512-iY2wlmyrXJmx4yxUGP3aYCFF3cA/6ShddOKmLU2dx7LnEykbAM8WThlxZEfd1NddqceF6eJmZUayxx9oKIYNlg==
dependencies:
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/typescript-estree" "8.70.1"
"@typescript-eslint/utils" "8.70.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/typescript-estree" "8.72.1"
"@typescript-eslint/utils" "8.72.1"
debug "^4.4.3"
ts-api-utils "^2.5.0"
"@typescript-eslint/types@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.70.1.tgz#2022b9d07d057174eba25b9bbd289641d262a196"
integrity sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==
"@typescript-eslint/types@8.72.1", "@typescript-eslint/types@^8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.72.1.tgz#067d8f738c294611e33a1425ae43ac9b9c2f24e3"
integrity sha512-LmE5IhQet1BWOWEXqBrVtBPsMAOQ6Nzk35d+WLh3mk4EUF33sqlfxjio1MdYeSvKSG5skactKKuixBIKXhL4Rw==
"@typescript-eslint/types@^8.70.1":
version "8.71.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.71.0.tgz#a230a121e71665060afe27d1f1ad110371f9451e"
integrity sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==
"@typescript-eslint/typescript-estree@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz#ced48fee6f7043835d2edfed9786ae74813a3c99"
integrity sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==
"@typescript-eslint/typescript-estree@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.1.tgz#e93b9cc3a56bf12f5fed857bb8f4963cce7924ee"
integrity sha512-DAlUoUV93hIvrtIQLwCbmZQYVZwOMhiHJrOVUUIWicFZaIYjTRqlY3I/NZevfkLmSqkNHRu2qYDcq9lrn16oOQ==
dependencies:
"@typescript-eslint/project-service" "8.70.1"
"@typescript-eslint/tsconfig-utils" "8.70.1"
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/visitor-keys" "8.70.1"
"@typescript-eslint/project-service" "8.72.1"
"@typescript-eslint/tsconfig-utils" "8.72.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/visitor-keys" "8.72.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.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.70.1.tgz#6a97887cee702df513692f5cdc7f79baeef48e02"
integrity sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==
"@typescript-eslint/utils@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.72.1.tgz#f210c1bf4a25aa1306df80c702ed30bbbc9b4d3d"
integrity sha512-5v5liypurJBFHCBNqh/RjsymAD+VWcE0djuGY/BUqp5fC3Qn+luWDEksjuTtWjPn6i5zQlUtkZCRG/CYG0yCkA==
dependencies:
"@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.70.1"
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/typescript-estree" "8.70.1"
"@typescript-eslint/scope-manager" "8.72.1"
"@typescript-eslint/types" "8.72.1"
"@typescript-eslint/typescript-estree" "8.72.1"
"@typescript-eslint/visitor-keys@8.70.1":
version "8.70.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz#b4cde0ed16982bd963a6310fcae554f59fd14124"
integrity sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==
"@typescript-eslint/visitor-keys@8.72.1":
version "8.72.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.1.tgz#c7aec9883b44ef795f1b8200df81d9d44d355de7"
integrity sha512-SB8xqipSzZHUePCCLNeYxqnxq83X61/qzNhjQGbTfjxGZIeOEP7tBgWYX05Tcd6CgE02WdTJnh5b8kMZJ/+bnQ==
dependencies:
"@typescript-eslint/types" "8.70.1"
"@typescript-eslint/types" "8.72.1"
eslint-visitor-keys "^5.0.0"
"@typescript/typescript-aix-ppc64@7.0.2":
@@ -890,13 +885,13 @@ eslint-plugin-no-instanceof@^1.0.1:
resolved "https://registry.yarnpkg.com/eslint-plugin-no-instanceof/-/eslint-plugin-no-instanceof-1.0.1.tgz#5d9fc86d160df6991b654b294a62390207f1bb97"
integrity sha512-zlqQ7EsfzbRO68uI+p8FIE7zYB4njs+nNbkNjSb5QmLi2et67zQLqSeaao5U9SpnlZTTJC87nS2oyHo2ACtajw==
eslint-plugin-prettier@^5.5.6:
version "5.5.6"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz#363ebe4d769bce157ccdd8129ce3efd91dc62564"
integrity sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==
eslint-plugin-prettier@^5.5.7:
version "5.5.7"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.7.tgz#ebef3acc2d664333b1356b717780198abc635398"
integrity sha512-WjHyn3GrFd+ZR0rzsLiM4zJZ35PZMOE0SUIxzGmnWNG0126yNfJKJzObmmkYDwHJfGlkz3PSlzyG9ez/LJntwg==
dependencies:
prettier-linter-helpers "^1.0.1"
synckit "^0.11.13"
synckit "^0.12.3"
eslint-scope@^9.1.2:
version "9.1.2"
@@ -923,10 +918,10 @@ 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.11.0:
version "10.11.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.11.0.tgz#304d1591b7c6a327e3f64b4f550f99fda160ae20"
integrity sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==
eslint@^10.13.0:
version "10.13.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.13.0.tgz#a8a4742648c0fa9131f429b509de5407682d4864"
integrity sha512-UohDjD4zSzEwhf58M3O7vc/ycc8O1I7dNlhtD0WxE8Px8EHNJKtUwzBqrzeGhisRuv2ag4sAYswszCJRgKXYUg==
dependencies:
"@eslint-community/eslint-utils" "^4.8.0"
"@eslint-community/regexpp" "^4.12.2"
@@ -1476,10 +1471,10 @@ keyv@^5.6.0:
dependencies:
"@keyv/serialize" "^1.1.1"
"langsmith@>=0.5.0 <1.0.0":
version "0.7.1"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.7.1.tgz#d721ad2e5211f9a5373e8a84eb4fed36b5621971"
integrity sha512-Wjk90UjNoY5cBHMlNAC/eZx5clI8jnjBOBW8uJu8+MWBtx0QesNjsUiLtjI+I3UnrpxFFpDqGXcnhBjH654Mqg==
"langsmith@>=0.11.0 <1.0.0":
version "0.11.6"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.11.6.tgz#cdc0f89a14632f8b172deea5daa1ee625f7442c3"
integrity sha512-5hfJM5DEZpXFxUS3U9nY2CMyBRmFnhkJj7znUODJ8vsr/Jx38tGJl4GIhV484xAY7yQFWrFOTDr33PB95kHUKg==
dependencies:
p-queue "6.6.2"
@@ -1707,10 +1702,10 @@ prettier-linter-helpers@^1.0.1:
dependencies:
fast-diff "^1.1.2"
prettier@^3.9.9:
version "3.9.9"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.9.tgz#09b826918c91cd4cbc80e0cbd1d2a922ff04f233"
integrity sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==
prettier@^3.9.10:
version "3.9.10"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.10.tgz#ac378e432f1a7381518f2e080816bbaefa38d585"
integrity sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==
punycode@^2.1.0:
version "2.3.1"
@@ -1940,11 +1935,12 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
synckit@^0.11.13:
version "0.11.13"
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.13.tgz#062a5ea57d81befc35892f8254de5c567e97c80a"
integrity sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==
synckit@^0.12.3:
version "0.12.3"
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.12.3.tgz#6e30a3441d095ed04c311077350ce205465512d6"
integrity sha512-U+/AXT6LWsxnj2vz9HeUuPWBNgdWxjyIEjEPdbJARC9txmgPzo2VEdqnFo4hHaNyMPtycyVJrSaQYeQv/PoyyA==
dependencies:
"@dual-bundle/import-meta-resolve" "^4.2.1"
"@pkgr/core" "^0.3.6"
tinyglobby@^0.2.15:
@@ -1970,17 +1966,17 @@ tsconfig-paths@^3.15.0:
minimist "^1.2.6"
strip-bom "^3.0.0"
turbo@^2.11.5:
version "2.11.5"
resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.11.5.tgz#e7223abd0555b978d3efe1b00ea9a98f43afb7ab"
integrity sha512-qLXheqz3TUvJpHIV6MUBwkNkvwKxMVG/QsimyiKT9DfNWPHJ294G/i5nDaH+fbHJt3UH7orubNNxAqYkbUNGAQ==
turbo@^2.11.10:
version "2.11.10"
resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.11.10.tgz#8b1d40debab2b871ff9e908d6f3018aeda04879d"
integrity sha512-3GJYwig8ygEI89oaOZdduJX8/aw6O9eRPYHoIt6T3FZl9hkKjiyIspufJoVgqXihPoJu8M5CSV0DlZvzfohBCw==
optionalDependencies:
"@turbo/darwin-64" "2.11.5"
"@turbo/darwin-arm64" "2.11.5"
"@turbo/linux-64" "2.11.5"
"@turbo/linux-arm64" "2.11.5"
"@turbo/windows-64" "2.11.5"
"@turbo/windows-arm64" "2.11.5"
"@turbo/darwin-64" "2.11.10"
"@turbo/darwin-arm64" "2.11.10"
"@turbo/linux-64" "2.11.10"
"@turbo/linux-arm64" "2.11.10"
"@turbo/windows-64" "2.11.10"
"@turbo/windows-arm64" "2.11.10"
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
@@ -2148,6 +2144,6 @@ yocto-queue@^0.1.0:
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
"zod@^3.25.76 || ^4":
version "4.3.6"
resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a"
integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==
version "4.6.7"
resolved "https://registry.yarnpkg.com/zod/-/zod-4.6.7.tgz#e806cf64a7f4e37635ce9f2f52c04c3766f75e5b"
integrity sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.4.38"
__version__ = "0.4.40"
+136 -129
View File
@@ -12,7 +12,7 @@ from collections.abc import Callable, Mapping, Sequence
from contextlib import contextmanager
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from typing import Protocol, TypeVar
from typing import Generic, Protocol, TypeVar
import click
import click.exceptions
@@ -26,7 +26,6 @@ from langgraph_cli.dependency_tracking import find_tracked_packages
from langgraph_cli.docker import build_docker_image, can_build_locally
from langgraph_cli.exec import CommandRunner, Runner, subp_exec
from langgraph_cli.host_backend import (
MAX_PAGE_SIZE,
ControlPlaneEndpoints,
HostBackendClient,
HostBackendError,
@@ -183,12 +182,15 @@ class Listener:
)
return cls(
identifier,
str(resource.get("compute_id", "")),
str(resource.get("compute_id") or ""),
tuple(str(namespace) for namespace in namespaces)
if isinstance(namespaces, list)
else (),
)
def cells(self) -> tuple[str | None, ...]:
return (self.id, self.compute_id, ", ".join(self.namespaces))
@dataclass(frozen=True, slots=True)
class Unplaced:
@@ -276,8 +278,6 @@ def _describe_listeners(listeners: Sequence[Listener]) -> str:
]
if len(listeners) > len(shown):
lines.append(f" ... and {len(listeners) - len(shown)} more")
if len(listeners) == MAX_PAGE_SIZE:
lines.append(f" (only the first {MAX_PAGE_SIZE} listeners were read)")
return "\n".join(lines)
@@ -499,16 +499,9 @@ def _source_of(resource: object) -> str | None:
def find_deployment_by_name(
client: HostBackendClient, name: str
) -> ExistingDeployment | None:
listed = client.list_deployments(name=name, name_contains=name, limit=MAX_PAGE_SIZE)
for resource in listed:
for resource in client.list_deployments(name=name, name_contains=name):
if resource.get("name") == name and resource.get("id"):
return ExistingDeployment(str(resource["id"]), _source_of(resource))
if len(listed) >= MAX_PAGE_SIZE:
raise click.ClickException(
"This workspace has more deployments than the CLI can search, so it "
f"cannot tell whether '{name}' already exists. Pass --deployment-id to "
"update an existing deployment."
)
return None
@@ -583,103 +576,96 @@ def _validate_prebuilt_image(
_get_emitter().info(f"Image is available for {_DEPLOYMENT_PLATFORM}")
def _extract_deployment_url(deployment: dict[str, object]) -> str:
source_config = deployment.get("source_config")
if isinstance(source_config, dict):
custom_url = source_config.get("custom_url")
if isinstance(custom_url, str) and custom_url:
return custom_url
return "-"
Resource = Mapping[str, object]
RowT = TypeVar("RowT")
def format_deployments_table(deployments: Sequence[dict[str, object]]) -> str:
headers = ("Deployment ID", "Deployment Name", "Deployment URL")
rows = [
(
str(deployment.get("id", "-") or "-"),
str(deployment.get("name", "-") or "-"),
_extract_deployment_url(deployment),
)
for deployment in deployments
]
widths = [
max(len(headers[index]), *(len(row[index]) for row in rows))
for index in range(len(headers))
]
@dataclass(frozen=True, slots=True)
class Table(Generic[RowT]):
headers: tuple[str, ...]
cells: Callable[[RowT], tuple[str | None, ...]]
def format_row(row: Sequence[str]) -> str:
return " ".join(value.ljust(widths[index]) for index, value in enumerate(row))
lines = [format_row(headers), format_row(tuple("-" * width for width in widths))]
lines.extend(format_row(row) for row in rows)
return "\n".join(lines)
def _extract_listener_namespaces(listener: dict[str, object]) -> str:
compute_config = listener.get("compute_config")
namespaces = (
compute_config.get("k8s_namespaces")
if isinstance(compute_config, dict)
else None
)
if isinstance(namespaces, list) and namespaces:
return ", ".join(str(namespace) for namespace in namespaces)
return "-"
def format_listeners_table(listeners: Sequence[dict[str, object]]) -> str:
headers = ("Listener ID", "Compute ID", "Namespaces")
rows = [
(
str(listener.get("id", "-") or "-"),
str(listener.get("compute_id", "-") or "-"),
_extract_listener_namespaces(listener),
)
for listener in listeners
]
widths = [
max(len(headers[index]), *(len(row[index]) for row in rows))
for index in range(len(headers))
]
def format_row(row: Sequence[str]) -> str:
return " ".join(value.ljust(widths[index]) for index, value in enumerate(row))
lines = [format_row(headers), format_row(tuple("-" * width for width in widths))]
lines.extend(format_row(row) for row in rows)
return "\n".join(lines)
def format_revisions_table(revisions: Sequence[dict[str, object]]) -> str:
headers = ("Revision ID", "Status", "Created At")
latest_deployed_seen = False
rows = []
for revision in revisions:
status = str(revision.get("status", "-") or "-")
if status == "DEPLOYED":
if latest_deployed_seen:
status = "REPLACED"
else:
latest_deployed_seen = True
rows.append(
(
str(revision.get("id", "-") or "-"),
status,
str(revision.get("created_at", "-") or "-"),
def render(self, rows: Sequence[RowT]) -> str:
grid = [
self.headers,
*(tuple(cell or "-" for cell in self.cells(row)) for row in rows),
]
widths = [max(map(len, column)) for column in zip(*grid, strict=True)]
rule = tuple("-" * width for width in widths)
return "\n".join(
" ".join(
cell.ljust(width) for cell, width in zip(line, widths, strict=True)
)
for line in (grid[0], rule, *grid[1:])
)
widths = [
max(len(headers[index]), *(len(row[index]) for row in rows))
for index in range(len(headers))
def _text(resource: Resource, key: str) -> str | None:
value = resource.get(key)
return str(value) if value else None
def _deployment_url(deployment: Resource) -> str | None:
source_config = deployment.get("source_config")
custom_url = (
source_config.get("custom_url") if isinstance(source_config, Mapping) else None
)
return custom_url if isinstance(custom_url, str) and custom_url else None
def _with_replaced_status(revisions: Sequence[Resource]) -> list[Resource]:
live_index = next(
(
index
for index, revision in enumerate(revisions)
if revision.get("status") == "DEPLOYED"
),
None,
)
return [
{**revision, "status": "REPLACED"}
if revision.get("status") == "DEPLOYED" and index != live_index
else revision
for index, revision in enumerate(revisions)
]
def format_row(row: Sequence[str]) -> str:
return " ".join(value.ljust(widths[index]) for index, value in enumerate(row))
lines = [format_row(headers), format_row(tuple("-" * width for width in widths))]
lines.extend(format_row(row) for row in rows)
return "\n".join(lines)
@dataclass(frozen=True, slots=True)
class DeploymentRow:
id: str
name: str | None
url: str | None
@classmethod
def from_resource(cls, deployment: Resource) -> "DeploymentRow":
identifier = _text(deployment, "id")
if identifier is None:
raise HostBackendError(
"The control plane returned a deployment without an id."
)
return cls(identifier, _text(deployment, "name"), _deployment_url(deployment))
def cells(self) -> tuple[str | None, ...]:
return (self.id, self.name, self.url)
def as_json(self) -> dict[str, str | None]:
return {"id": self.id, "name": self.name}
DEPLOYMENTS_TABLE = Table(
("Deployment ID", "Deployment Name", "Deployment URL"), DeploymentRow.cells
)
LISTENERS_TABLE = Table(("Listener ID", "Compute ID", "Namespaces"), Listener.cells)
REVISIONS_TABLE = Table(
("Revision ID", "Status", "Created At"),
lambda revision: (
_text(revision, "id"),
_text(revision, "status"),
_text(revision, "created_at"),
),
)
def format_timestamp(ts) -> str:
@@ -841,7 +827,6 @@ def _find_deployment(
lambda c: c.list_deployments(
agent_id=selector.agent_id,
agent_environment=selector.environment,
limit=MAX_PAGE_SIZE,
),
)
if len(existing) > 1:
@@ -1007,20 +992,15 @@ def _print_deployment_result(
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):
sc = dep_info.get("source_config")
if isinstance(sc, dict):
custom_url = sc.get("custom_url")
deployment = client.get_deployment(deployment_id)
url = _deployment_url(deployment) if isinstance(deployment, Mapping) else None
if last_status == "DEPLOYED":
em.result(
"succeeded",
deployment_id=deployment_id,
url=custom_url,
url=url,
status_url=status_url,
)
elif last_status in ("BUILD_FAILED", "DEPLOY_FAILED", "CREATE_FAILED"):
@@ -1081,6 +1061,8 @@ def _docker_config_for_token(registry_host: str, token: str, username: str):
_UPLOAD_TIMEOUT_SECONDS = 300
_BYTES_PER_MIB = 1_048_576
# Byte range Cloud Storage signs into upload URLs, up to the 200 MB archive limit.
_GCS_SIZE_RANGE = f"0,{200 * _BYTES_PER_MIB}"
class _ProgressReader:
@@ -1106,12 +1088,24 @@ class _ProgressReader:
return self._file_size
def _upload_to_gcs(signed_url: str, file_path: str, file_size: int) -> None:
"""Upload tarball to GCS via signed PUT URL with progress display."""
def _upload_source_tarball(
signed_url: str,
file_path: str,
file_size: int,
upload_headers: dict[str, str] | None,
) -> None:
"""Upload tarball via signed PUT URL with progress display."""
import urllib.error
import urllib.request
em = _get_emitter()
# Control planes that return no `upload_headers` sign for the Cloud Storage
# size range. Content-Type is a fallback: without one, urllib sends a form
# content type, which breaks signatures that cover it.
headers = {
"Content-Type": "application/gzip",
**(upload_headers or {"X-Goog-Content-Length-Range": _GCS_SIZE_RANGE}),
}
with open(file_path, "rb") as f:
reader = _ProgressReader(f, file_size, em)
@@ -1119,11 +1113,7 @@ def _upload_to_gcs(signed_url: str, file_path: str, file_size: int) -> None:
signed_url,
data=reader,
method="PUT",
headers={
"Content-Type": "application/gzip",
"Content-Length": str(file_size),
"X-Goog-Content-Length-Range": "0,209715200",
},
headers={**headers, "Content-Length": str(file_size)},
)
try:
urllib.request.urlopen(req, timeout=_UPLOAD_TIMEOUT_SECONDS)
@@ -1443,7 +1433,9 @@ def _run_remote_build(
step += 1
_log_deploy_step(step, "Uploading source")
_upload_to_gcs(signed_url, archive_path, file_size)
_upload_source_tarball(
signed_url, archive_path, file_size, upload_data.get("upload_headers")
)
step += 1
_log_deploy_step(step, "Triggering remote build")
@@ -2439,7 +2431,6 @@ def _deploy_cmd(
validate_deploy_commands(install_command, build_command)
agent = None
if agent_id is not None or environment is not None:
em.note("Note: --agent-id and --agent-environment flags are in private beta")
if not agent_id or not agent_id.strip() or not environment:
raise click.UsageError(
"--agent-id and --agent-environment are required together."
@@ -2594,6 +2585,13 @@ def _deploy_cmd(
default="",
help="Only show deployments whose names contain this value.",
)
@click.option(
"--json",
"json_output",
is_flag=True,
default=False,
help="Print the deployments as a JSON array of {id, name} instead of a table.",
)
@deploy.command("list", help="[Beta] List LangSmith Deployments.")
def deploy_list(
api_key: str | None,
@@ -2601,11 +2599,13 @@ def deploy_list(
name_contains: str,
agent_id: str | None,
environment: str | None,
json_output: bool,
) -> None:
if agent_id is not None or environment is not None:
click.secho(
"Note: --agent-id and --agent-environment flags are in private beta",
fg="yellow",
err=True,
)
if agent_id is not None and not agent_id.strip():
raise click.UsageError("--agent-id must not be empty.")
@@ -2614,15 +2614,19 @@ def deploy_list(
filters["agent_id"] = agent_id
if environment is not None:
filters["agent_environment"] = environment
client = _create_host_backend_client(host_url, api_key)
deployments = _call_host_backend_with_optional_tenant(
client = _create_host_backend_client(host_url, api_key, no_input=json_output)
resources = _call_host_backend_with_optional_tenant(
client,
lambda c: c.list_deployments(name_contains=name_contains, **filters),
no_input=json_output,
)
if not deployments:
deployments = [DeploymentRow.from_resource(resource) for resource in resources]
if json_output:
click.echo(json_mod.dumps([deployment.as_json() for deployment in deployments]))
elif not deployments:
click.echo("No deployments found.")
return
click.echo(format_deployments_table(deployments))
else:
click.echo(DEPLOYMENTS_TABLE.render(deployments))
# ---------------------------------------------------------------------------
@@ -2665,11 +2669,14 @@ def deploy_listeners_list(
)
if json_output:
click.echo(json_mod.dumps(listeners))
return
if not listeners:
elif not listeners:
click.echo("No listeners found for this workspace.")
return
click.echo(format_listeners_table(listeners))
else:
click.echo(
LISTENERS_TABLE.render(
[Listener.from_resource(listener) for listener in listeners]
)
)
# ---------------------------------------------------------------------------
@@ -2712,7 +2719,7 @@ def deploy_revisions_list(
if not revisions:
click.echo(f"No revisions found for deployment {deployment_id}.")
return
click.echo(format_revisions_table(revisions))
click.echo(REVISIONS_TABLE.render(_with_replaced_status(revisions)))
# ---------------------------------------------------------------------------
+38 -10
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any, Literal
from urllib.parse import urlparse
@@ -220,27 +221,51 @@ class HostBackendClient:
*,
name: str | None = None,
name_contains: str | None = None,
limit: int | None = None,
agent_id: str | None = None,
agent_environment: str | None = None,
) -> list[dict[str, Any]]:
) -> list[dict[str, object]]:
given = (
("name", name),
("name_contains", name_contains),
("limit", limit),
("agent_id", agent_id),
("agent_environment", agent_environment),
)
params = {key: value for key, value in given if value is not None}
return _resources(self._request("GET", "/v2/deployments", params=params))
filters = {key: value for key, value in given if value is not None}
return self._every_page("/v2/deployments", filters)
def _every_page(
self, path: str, filters: Mapping[str, object]
) -> list[dict[str, object]]:
resources: list[dict[str, object]] = []
seen_ids: set[object] = set()
while True:
page = _resources(
self._request(
"GET",
path,
params={
**filters,
"limit": MAX_PAGE_SIZE,
"offset": len(resources),
},
)
)
page_ids = {resource.get("id") for resource in page}
if page_ids and page_ids <= seen_ids:
raise HostBackendError(
f"GET {path} returned the same resources again: this control "
"plane ignores 'offset', so the CLI cannot read past the first page."
)
seen_ids |= page_ids
resources.extend(page)
if len(page) < MAX_PAGE_SIZE:
return resources
def get_listener(self, listener_id: str) -> dict[str, Any]:
return self._request("GET", f"/v2/listeners/{listener_id}")
def list_listeners(self) -> list[dict[str, Any]]:
return _resources(
self._request("GET", "/v2/listeners", params={"limit": MAX_PAGE_SIZE})
)
def list_listeners(self) -> list[dict[str, object]]:
return self._every_page("/v2/listeners", {})
def get_deployment(self, deployment_id: str) -> dict[str, Any]:
return self._request("GET", f"/v2/deployments/{deployment_id}")
@@ -258,10 +283,13 @@ class HostBackendClient:
)
def request_upload_url(self, deployment_id: str) -> dict[str, Any]:
"""Get a signed GCS URL for uploading the source tarball."""
"""Get a signed URL for uploading the source tarball."""
# Tells the control plane this client sends the returned
# `upload_headers` with its upload, which some storage requires.
return self._request(
"POST",
f"/v2/deployments/{deployment_id}/upload-url",
{"supports_upload_headers": True},
)
def update_deployment(
+297
View File
@@ -482,6 +482,177 @@ def test_deploy_list_command_no_results(monkeypatch) -> None:
assert result.output.strip() == "No deployments found."
def test_deploy_list_prints_an_aligned_table(monkeypatch) -> None:
class FakeClient:
def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None):
pass
def list_deployments(self, name_contains: str = ""):
return [
{
"id": "dep-123",
"name": "alpha",
"source_config": {"custom_url": "https://alpha.example.com"},
},
{"id": "dep-4567", "name": "beta-agent", "source_config": {}},
]
monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient)
result = CliRunner().invoke(
cli,
[
"deploy",
"list",
"--api-key",
"test-key",
"--host-url",
"https://api.example.com",
],
)
assert result.exit_code == 0, result.output
assert result.output.splitlines() == [
"Deployment ID Deployment Name Deployment URL ",
"------------- --------------- -------------------------",
"dep-123 alpha https://alpha.example.com",
"dep-4567 beta-agent - ",
]
def _deploy_list_json(
monkeypatch, deployments: list[dict], *extra_args: str
) -> click.testing.Result:
class FakeClient:
def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None):
pass
def list_deployments(
self,
name_contains: str = "",
agent_id: str | None = None,
agent_environment: str | None = None,
):
return deployments
monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient)
return CliRunner().invoke(
cli,
[
"deploy",
"list",
"--json",
"--api-key",
"test-key",
"--host-url",
"https://api.example.com",
*extra_args,
],
)
def test_deploy_list_command_json(monkeypatch) -> None:
result = _deploy_list_json(
monkeypatch,
[
{
"id": "dep-123",
"name": "alpha",
"source_config": {"custom_url": "https://alpha.example.com"},
},
{"id": "dep-456", "name": "beta", "source_config": {}},
],
)
assert result.exit_code == 0, result.output
assert json.loads(result.stdout) == [
{"id": "dep-123", "name": "alpha"},
{"id": "dep-456", "name": "beta"},
]
def test_deploy_list_json_never_prints_deployment_secrets(monkeypatch) -> None:
result = _deploy_list_json(
monkeypatch,
[
{
"id": "dep-123",
"name": "alpha",
"source_config": {"custom_url": None},
"secrets": [{"name": "OPENAI_API_KEY", "value": "sk-plain-value"}],
}
],
)
assert result.exit_code == 0, result.output
assert "sk-plain-value" not in result.output
assert json.loads(result.stdout) == [{"id": "dep-123", "name": "alpha"}]
def test_deploy_list_command_json_no_results(monkeypatch) -> None:
result = _deploy_list_json(monkeypatch, [])
assert result.exit_code == 0, result.output
assert json.loads(result.stdout) == []
def test_deploy_list_rejects_a_deployment_without_an_id(monkeypatch) -> None:
result = _deploy_list_json(monkeypatch, [{"name": "alpha"}])
assert result.exit_code != 0
assert "deployment without an id" in result.output
def test_deploy_list_json_keeps_the_agent_note_off_stdout(monkeypatch) -> None:
result = _deploy_list_json(
monkeypatch,
[{"id": "dep-123", "name": "alpha"}],
"--agent-id",
"customer-support",
"--agent-environment",
"staging",
)
assert result.exit_code == 0, result.output
assert json.loads(result.stdout) == [{"id": "dep-123", "name": "alpha"}]
assert "private beta" in result.stderr
def test_deploy_list_json_never_prompts_for_a_workspace(monkeypatch) -> None:
class FakeClient:
def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None):
pass
def list_deployments(self, name_contains: str = ""):
raise HostBackendError("requires workspace specification", status_code=403)
def set_tenant(self, tenant_id: str) -> None:
raise AssertionError("must not retry with a prompted tenant with --json")
monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient)
prompt_calls = []
monkeypatch.setattr(
click, "prompt", lambda *a, **k: prompt_calls.append((a, k)) or "unreachable"
)
result = CliRunner().invoke(
cli,
[
"deploy",
"list",
"--json",
"--api-key",
"test-key",
"--host-url",
"https://api.example.com",
],
)
assert prompt_calls == []
assert result.exit_code != 0
assert "LANGSMITH_TENANT_ID" in result.output
def test_deploy_listeners_list_command(monkeypatch) -> None:
runner = CliRunner()
captured: dict[str, str] = {}
@@ -564,6 +735,84 @@ def test_deploy_listeners_list_command_no_results(monkeypatch) -> None:
assert result.output.strip() == "No listeners found for this workspace."
def test_deploy_listeners_list_prints_an_aligned_table(monkeypatch) -> None:
class FakeClient:
def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None):
pass
def list_listeners(self):
return [
{
"id": "listener-1",
"compute_id": "prod-cluster",
"compute_config": {"k8s_namespaces": ["agents"]},
},
{
"id": "listener-22",
"compute_id": "multi",
"compute_config": {"k8s_namespaces": ["agents", "agents-staging"]},
},
{"id": "listener-3", "compute_id": "", "compute_config": {}},
{
"id": "listener-4",
"compute_id": None,
"compute_config": {"k8s_namespaces": ["agents"]},
},
]
monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient)
result = CliRunner().invoke(
cli,
[
"deploy",
"listeners",
"list",
"--api-key",
"test-key",
"--host-url",
"https://api.example.com",
],
)
assert result.exit_code == 0, result.output
assert result.output.splitlines() == [
"Listener ID Compute ID Namespaces ",
"----------- ------------ ----------------------",
"listener-1 prod-cluster agents ",
"listener-22 multi agents, agents-staging",
"listener-3 - - ",
"listener-4 - agents ",
]
def test_deploy_listeners_list_rejects_a_listener_without_an_id(monkeypatch) -> None:
class FakeClient:
def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None):
pass
def list_listeners(self):
return [{"compute_id": "prod-cluster"}]
monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient)
result = CliRunner().invoke(
cli,
[
"deploy",
"listeners",
"list",
"--api-key",
"test-key",
"--host-url",
"https://api.example.com",
],
)
assert result.exit_code != 0
assert "listener without an id" in result.output
def test_deploy_listeners_list_command_json(monkeypatch) -> None:
runner = CliRunner()
@@ -766,6 +1015,54 @@ def test_deploy_revisions_list_command_no_results(monkeypatch) -> None:
assert result.output.strip() == "No revisions found for deployment dep-123."
def test_deploy_revisions_list_marks_older_deployed_revisions_as_replaced(
monkeypatch,
) -> None:
class FakeClient:
def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None):
pass
def list_revisions(self, deployment_id: str, limit: int = 1):
return [
{
"id": "rev-3",
"status": "DEPLOYING",
"created_at": "2026-10-20T10:00:00Z",
},
{
"id": "rev-2",
"status": "DEPLOYED",
"created_at": "2026-10-19T10:00:00Z",
},
{"id": "rev-1", "status": "DEPLOYED", "created_at": None},
]
monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient)
result = CliRunner().invoke(
cli,
[
"deploy",
"revisions",
"list",
"--api-key",
"test-key",
"--host-url",
"https://api.example.com",
"dep-123",
],
)
assert result.exit_code == 0, result.output
assert result.output.splitlines() == [
"Revision ID Status Created At ",
"----------- --------- --------------------",
"rev-3 DEPLOYING 2026-10-20T10:00:00Z",
"rev-2 DEPLOYED 2026-10-19T10:00:00Z",
"rev-1 REPLACED - ",
]
def test_deploy_revisions_list_command_with_explicit_limit(monkeypatch) -> None:
runner = CliRunner()
captured: dict[str, str] = {}
@@ -96,6 +96,7 @@ class ControlPlaneDouble:
existing_deployments: list[dict] = field(default_factory=list)
push_token_status: int = 200
push_token_extra: dict = field(default_factory=dict)
upload_url_extra: dict = field(default_factory=dict)
create_error: str | None = None
listeners: list[dict] = field(default_factory=list)
listeners_by_id: dict[str, dict] = field(default_factory=dict)
@@ -111,7 +112,11 @@ class ControlPlaneDouble:
def _respond(self, request: httpx.Request) -> httpx.Response:
method, path = request.method, request.url.path
if (method, path) == ("GET", "/v2/listeners"):
return httpx.Response(200, json={"resources": self.listeners})
offset = int(request.url.params.get("offset", 0))
limit = int(request.url.params.get("limit", 20))
return httpx.Response(
200, json={"resources": self.listeners[offset : offset + limit]}
)
if method == "GET" and path.startswith("/v2/listeners/"):
listener_id = path.rsplit("/", 1)[-1]
if not _looks_like_a_uuid(listener_id):
@@ -159,7 +164,12 @@ class ControlPlaneDouble:
)
if path.endswith("/upload-url"):
return httpx.Response(
200, json={"upload_url": SIGNED_UPLOAD_URL, "object_path": OBJECT_PATH}
200,
json={
"upload_url": SIGNED_UPLOAD_URL,
"object_path": OBJECT_PATH,
**self.upload_url_extra,
},
)
if method == "PATCH":
return httpx.Response(200, json={"tenant_id": "tenant-1"})
@@ -276,7 +286,7 @@ class DeployProject:
control_plane: ControlPlaneDouble
docker: DockerDouble
timeline: list[str]
uploads: list[tuple[str, str, int]]
uploads: list[tuple[str, str, int, dict[str, str] | None]]
def run(self, *args: str, host_url: str = CONTROL_PLANE_URL) -> Result:
return CliRunner().invoke(
@@ -313,16 +323,21 @@ def deploy_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> DeployPro
timeline: list[str] = []
control_plane = ControlPlaneDouble(timeline)
docker = DockerDouble(timeline)
uploads: list[tuple[str, str, int]] = []
uploads: list[tuple[str, str, int, dict[str, str] | None]] = []
@contextmanager
def fake_create_archive(config_path: Path, config: dict) -> Iterator[tuple]:
timeline.append("create_archive")
yield ARCHIVE
def fake_upload(signed_url: str, file_path: str, file_size: int) -> None:
def fake_upload(
signed_url: str,
file_path: str,
file_size: int,
upload_headers: dict[str, str] | None,
) -> None:
timeline.append("upload_archive")
uploads.append((signed_url, file_path, file_size))
uploads.append((signed_url, file_path, file_size, upload_headers))
monkeypatch.setattr(deploy_module, "_no_input", False)
monkeypatch.setattr(deploy_module, "_emitter", None)
@@ -340,7 +355,7 @@ def deploy_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> DeployPro
)
monkeypatch.setattr(deploy_module.platform, "machine", lambda: "x86_64")
monkeypatch.setattr(archive_module, "create_archive", fake_create_archive)
monkeypatch.setattr(deploy_module, "_upload_to_gcs", fake_upload)
monkeypatch.setattr(deploy_module, "_upload_source_tarball", fake_upload)
return DeployProject(control_plane, docker, timeline, uploads)
@@ -699,6 +714,9 @@ def test_deployment_not_created_by_the_cli_gets_an_actionable_error(
def test_remote_build_creates_an_internal_source_deployment_and_uploads_the_archive(
deploy_project: DeployProject,
) -> None:
upload_headers = {"Content-Type": "application/gzip", "x-ms-blob-type": "BlockBlob"}
deploy_project.control_plane.upload_url_extra = {"upload_headers": upload_headers}
result = deploy_project.run("--remote", "--install-command", "yarn install")
assert result.exit_code == 0, result.output
@@ -713,7 +731,12 @@ def test_remote_build_creates_an_internal_source_deployment_and_uploads_the_arch
assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source"] == (
"internal_source"
)
assert deploy_project.uploads == [(SIGNED_UPLOAD_URL, ARCHIVE[0], ARCHIVE[1])]
assert deploy_project.control_plane.bodies[_upload_url(CREATED_ID)] == {
"supports_upload_headers": True
}
assert deploy_project.uploads == [
(SIGNED_UPLOAD_URL, ARCHIVE[0], ARCHIVE[1], upload_headers)
]
assert deploy_project.control_plane.bodies[_patch(CREATED_ID)] == {
"revision_source": "internal_source",
"source_revision_config": {
@@ -1288,14 +1311,16 @@ def test_listener_flags_without_push_to_make_no_call_at_all(
assert deploy_project.timeline == []
def test_a_truncated_listener_page_says_so(deploy_project: DeployProject) -> None:
def test_listeners_beyond_the_first_page_are_counted(
deploy_project: DeployProject,
) -> None:
deploy_project.control_plane.listeners = [
{
"id": str(uuid.UUID(int=index)),
"compute_id": "cluster",
"compute_config": {"k8s_namespaces": ["agents"]},
}
for index in range(100)
for index in range(101)
]
result = deploy_project.run(
@@ -1303,7 +1328,8 @@ def test_a_truncated_listener_page_says_so(deploy_project: DeployProject) -> Non
)
assert result.exit_code != 0
assert "first 100" in result.output
assert "... and 91 more" in result.output
assert "first 100" not in result.output
def test_a_managed_build_in_a_listener_workspace_points_at_push_to(
@@ -75,6 +75,7 @@ def test_agent_create(deployment_api, tmp_path, monkeypatch):
"agent_id": "customer-support",
"agent_environment": "staging",
"limit": "100",
"offset": "0",
}
payload = json.loads(requests[1].content)
assert payload["agent"] == {
@@ -84,6 +85,8 @@ def test_agent_create(deployment_api, tmp_path, monkeypatch):
assert "name" not in payload
assert build.call_args.kwargs["deployment_id"] == "runtime-id"
assert "server-name" in result.output
assert "'langgraph deploy' is in beta" in result.output
assert "private beta" not in result.output
assert (tmp_path / ".env").read_text() == "LANGSMITH_DEPLOYMENT_NAME=legacy\n"
+111 -11
View File
@@ -33,6 +33,7 @@ from langgraph_cli.deploy import (
_Emitter,
_env_without_deployment_name,
_parse_env_from_config,
_print_deployment_result,
_resolve_env_path,
_resolve_pushed_image_digest,
_resource_spec,
@@ -536,6 +537,39 @@ class TestEmitterTextMode:
assert "URL: https://app.test" in lines
@pytest.mark.parametrize(
("deployment", "expected_url_lines"),
[
pytest.param(
{
"id": "dep-1",
"source_config": {"custom_url": "https://agent.example.com"},
},
["URL: https://agent.example.com"],
id="custom_url_is_printed",
),
pytest.param(
{"id": "dep-1", "source_config": {}}, [], id="no_custom_url_no_line"
),
],
)
def test_a_deployed_result_prints_the_deployment_url(
capsys, monkeypatch, deployment, expected_url_lines
):
class FakeClient:
def get_deployment(self, deployment_id: str) -> dict:
return deployment
monkeypatch.setattr(deploy_mod, "_emitter", None)
_print_deployment_result(
FakeClient(), "dep-1", "DEPLOYED", dashboard_label="Deployment dashboard"
)
lines = [line.strip() for line in capsys.readouterr().out.splitlines()]
assert [line for line in lines if line.startswith("URL:")] == expected_url_lines
# ---------------------------------------------------------------------------
# --no-input guard on _create_host_backend_client
# ---------------------------------------------------------------------------
@@ -1210,6 +1244,7 @@ def test_finding_a_deployment_by_name_narrows_the_search_for_every_server_versio
"name": "agent",
"name_contains": "agent",
"limit": "100",
"offset": "0",
}
assert found == ExistingDeployment("dep-1", "github")
@@ -1237,20 +1272,29 @@ def test_a_server_that_ignores_the_exact_name_filter_never_matches_another_deplo
assert find_deployment_by_name(client, "brand-new-agent") is None
def test_a_full_page_without_a_match_refuses_to_claim_the_name_is_free():
page = [
{"id": f"dep-{index}", "name": f"other-agent-{index}"} for index in range(100)
]
client = HostBackendClient(
"https://api.example.com",
"key",
transport=httpx.MockTransport(
lambda req: httpx.Response(200, json={"resources": page})
def test_a_deployment_beyond_the_first_page_is_found_by_name():
deployments = [
*(
{"id": f"dep-{index}", "name": f"other-agent-{index}"}
for index in range(100)
),
{"id": "dep-target", "name": "agent", "source": "external_docker"},
]
def handler(req: httpx.Request) -> httpx.Response:
offset = int(req.url.params["offset"])
limit = int(req.url.params["limit"])
return httpx.Response(
200, json={"resources": deployments[offset : offset + limit]}
)
client = HostBackendClient(
"https://api.example.com", "key", transport=httpx.MockTransport(handler)
)
with pytest.raises(click.ClickException, match="--deployment-id"):
find_deployment_by_name(client, "brand-new-agent")
assert find_deployment_by_name(client, "agent") == ExistingDeployment(
"dep-target", "external_docker"
)
def test_a_partial_page_without_a_match_means_the_name_is_free():
@@ -1414,3 +1458,59 @@ class TestPlacementOnAKnownListener:
def test_refuses_and_names_the_namespaces(self, request_, listener, message):
with pytest.raises(click.UsageError, match=message):
request_.on(listener)
DEFAULT_UPLOAD_HEADERS = {
"content-type": "application/gzip",
"content-length": "7",
"x-goog-content-length-range": "0,209715200",
}
@pytest.mark.parametrize(
("upload_headers", "expected"),
[
pytest.param(None, DEFAULT_UPLOAD_HEADERS, id="missing_sends_defaults"),
pytest.param({}, DEFAULT_UPLOAD_HEADERS, id="empty_sends_defaults"),
pytest.param(
{
"content-type": "application/x-gzip",
"x-ms-blob-type": "BlockBlob",
"content-length": "1",
},
{
"content-type": "application/x-gzip",
"x-ms-blob-type": "BlockBlob",
"content-length": "7",
},
id="returned_headers_replace_defaults",
),
pytest.param(
{"x-ms-blob-type": "BlockBlob"},
{
"content-type": "application/gzip",
"x-ms-blob-type": "BlockBlob",
"content-length": "7",
},
id="returned_headers_keep_a_content_type",
),
],
)
def test_source_upload_sends_the_headers_the_signed_url_requires(
tmp_path, mocker, upload_headers, expected
):
archive = tmp_path / "source.tar.gz"
archive.write_bytes(b"tarball")
sent = []
mocker.patch(
"langgraph_cli.deploy._get_emitter", return_value=MagicMock(json_mode=True)
)
mocker.patch(
"urllib.request.urlopen", side_effect=lambda req, timeout: sent.append(req)
)
deploy_mod._upload_source_tarball(
"https://storage.example/signed", str(archive), 7, upload_headers
)
assert {name.lower(): value for name, value in sent[0].header_items()} == expected
+72 -9
View File
@@ -575,7 +575,7 @@ def test_list_listeners_asks_for_a_full_page():
)
assert c.list_listeners() == [{"id": "listener-1"}]
assert seen["url"] == "https://api.example.com/v2/listeners?limit=100"
assert seen["url"] == "https://api.example.com/v2/listeners?limit=100&offset=0"
@pytest.mark.parametrize(
@@ -602,29 +602,30 @@ def test_is_cloud_recognises_the_managed_control_plane(control_plane_url, expect
assert endpoints.is_cloud is expected
FIRST_PAGE = {"limit": "100", "offset": "0"}
@pytest.mark.parametrize(
("call", "expected_params"),
[
pytest.param(
lambda c: c.list_deployments(name="agent"),
{"name": "agent"},
{"name": "agent", **FIRST_PAGE},
id="exact_name_filters_server_side",
),
pytest.param(
lambda c: c.list_deployments(name_contains="age"),
{"name_contains": "age"},
{"name_contains": "age", **FIRST_PAGE},
id="substring_search_keeps_its_own_parameter",
),
pytest.param(
lambda c: c.list_deployments(),
{},
id="no_filter_sends_no_parameters",
FIRST_PAGE,
id="no_filter_sends_only_the_page",
),
pytest.param(
lambda c: c.list_deployments(
name="agent", name_contains="agent", limit=100
),
{"name": "agent", "name_contains": "agent", "limit": "100"},
lambda c: c.list_deployments(name="agent", name_contains="agent"),
{"name": "agent", "name_contains": "agent", **FIRST_PAGE},
id="both_filters_travel_together_for_older_servers",
),
],
@@ -645,6 +646,68 @@ def test_list_deployments_sends_one_name_filter(call, expected_params):
assert seen == expected_params
@pytest.mark.parametrize(
"list_every",
[
pytest.param(HostBackendClient.list_deployments, id="deployments"),
pytest.param(HostBackendClient.list_listeners, id="listeners"),
],
)
def test_list_endpoints_read_every_page(list_every):
resources = [{"id": f"resource-{index}"} for index in range(101)]
offsets: list[str] = []
def handler(req: httpx.Request) -> httpx.Response:
offset = int(req.url.params["offset"])
limit = int(req.url.params["limit"])
offsets.append(req.url.params["offset"])
return httpx.Response(
200, json={"resources": resources[offset : offset + limit]}
)
c = HostBackendClient(
"https://api.example.com", "key", transport=httpx.MockTransport(handler)
)
assert list_every(c) == resources
assert offsets == ["0", "100"]
FIRST_PAGE_OF_DEPLOYMENTS = [
{"id": f"dep-{index}", "status": "DEPLOYED"} for index in range(100)
]
@pytest.mark.parametrize(
"repeated_page",
[
pytest.param(FIRST_PAGE_OF_DEPLOYMENTS, id="same_page_again"),
pytest.param(
[
{"id": "dep-99", "status": "DEPLOYING"},
*FIRST_PAGE_OF_DEPLOYMENTS[:99],
],
id="same_deployments_reordered_and_updated",
),
],
)
def test_list_deployments_fails_when_the_server_ignores_the_offset(repeated_page):
pages = iter([FIRST_PAGE_OF_DEPLOYMENTS, repeated_page])
def handler(req: httpx.Request) -> httpx.Response:
page = next(pages, None)
if page is None:
return httpx.Response(500, json={"detail": "the guard let paging run on"})
return httpx.Response(200, json={"resources": page})
c = HostBackendClient(
"https://api.example.com", "key", transport=httpx.MockTransport(handler)
)
with pytest.raises(HostBackendError, match="ignores 'offset'"):
c.list_deployments()
@pytest.mark.parametrize(
("body", "expected"),
[
+4 -98
View File
@@ -1,11 +1,6 @@
from unittest.mock import patch
from langgraph_cli.deploy import (
_extract_deployment_url,
format_deployments_table,
format_listeners_table,
format_revisions_table,
)
from langgraph_cli.deploy import Table
from langgraph_cli.util import clean_empty_lines, warn_non_wolfi_distro
@@ -194,96 +189,7 @@ def test_warn_non_wolfi_distro_does_not_modify_config():
assert config_copy == original_config # Config should remain unchanged
def test_extract_deployment_url_uses_custom_url():
deployment = {"source_config": {"custom_url": "https://example.com/custom"}}
assert _extract_deployment_url(deployment) == "https://example.com/custom"
def test_a_table_without_rows_still_renders_its_headers():
table = Table(("Name", "Status"), lambda resource: (None, None))
def test_extract_deployment_url_defaults_to_dash():
assert _extract_deployment_url({"id": "dep-123"}) == "-"
def test_format_deployments_table():
output = format_deployments_table(
[
{
"id": "dep-123",
"name": "alpha",
"source_config": {"custom_url": "https://alpha.example.com"},
},
{
"id": "dep-456",
"name": "beta",
"url": "https://beta.example.com",
},
]
)
assert "Deployment ID" in output
assert "Deployment Name" in output
assert "Deployment URL" in output
assert "dep-123" in output
assert "alpha" in output
assert "https://alpha.example.com" in output
assert "dep-456" in output
def test_format_revisions_table():
output = format_revisions_table(
[
{
"id": "rev-123",
"status": "DEPLOYED",
"created_at": "2023-11-09T10:00:00Z",
},
{
"id": "rev-456",
"status": "CREATING",
"created_at": "2023-11-07T05:31:56Z",
},
{
"id": "rev-789",
"status": "DEPLOYED",
"created_at": "2023-11-08T10:00:00Z",
},
]
)
assert "Revision ID" in output
assert "Status" in output
assert "Created At" in output
assert "rev-123" in output
assert "CREATING" in output
assert "2023-11-07T05:31:56Z" in output
assert "rev-456" in output
assert "rev-789" in output
assert "REPLACED" in output
def test_format_listeners_table():
output = format_listeners_table(
[
{
"id": "listener-1",
"compute_id": "prod-cluster",
"compute_config": {"k8s_namespaces": ["agents"]},
},
{
"id": "listener-2",
"compute_id": "multi-cluster",
"compute_config": {"k8s_namespaces": ["agents", "agents-staging"]},
},
{
"id": "listener-3",
"compute_id": "broken-cluster",
},
]
)
assert "Listener ID" in output
assert "Compute ID" in output
assert "Namespaces" in output
assert "listener-1" in output
assert "prod-cluster" in output
assert "agents" in output
assert "listener-2" in output
assert "agents, agents-staging" in output
assert "listener-3" in output
assert "broken-cluster" in output
assert table.render([]).splitlines() == ["Name Status", "---- ------"]
+13 -13
View File
@@ -1,5 +1,5 @@
version = 1
revision = 3
revision = 5
requires-python = ">=3.11"
[manifest]
@@ -250,7 +250,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.11"
version = "1.2.19"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langchain-core" },
@@ -260,27 +260,27 @@ dependencies = [
{ name = "pydantic" },
{ name = "xxhash" },
]
sdist = { url = "https://files.pythonhosted.org/packages/56/0d/c8e7ee98896659e1b6555db0ab115a9ca899844744645d5d894032bab1d7/langgraph-1.2.11.tar.gz", hash = "sha256:9ecfe11e50d338b34b15cf4d8a442642de103e8ae6971320efba84e4542eb363", size = 725753, upload-time = "2026-08-11T14:00:36.945Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ba/10/329dc49ce3fc40e36faafd407f08607fe3fab75613e709a66758b53faac2/langgraph-1.2.19.tar.gz", hash = "sha256:4a00fa1aad2b5fc2d3a789a8e9eaf993f76218ca924b434902ecf1bbaaf300f6", size = 782618, upload-time = "2026-10-30T22:23:32.962Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/7f/c5c30e4be99ff821029c7ac872a480676bb179c9f3df85ea3f38d13f86d4/langgraph-1.2.11-py3-none-any.whl", hash = "sha256:8bab70de7b2d00b5300fb289bcf38d8b241400f3184c1e95e8ce706fb0e8686b", size = 248854, upload-time = "2026-08-11T14:00:35.494Z" },
{ url = "https://files.pythonhosted.org/packages/d7/9e/bd29e368fb16feea8d24270d377d56c34b49afea371717124f19cdfa890c/langgraph-1.2.19-py3-none-any.whl", hash = "sha256:6094a5cbed52a77d8c74c276fcd2056abde7b2836eab1c7a6b74bacbd1a269b3", size = 262285, upload-time = "2026-10-30T22:23:31.334Z" },
]
[[package]]
name = "langgraph-checkpoint"
version = "4.2.0"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langchain-core" },
{ name = "ormsgpack" },
]
sdist = { url = "https://files.pythonhosted.org/packages/dc/e1/089c4c9e0a2fec7f883f82ae8e6a727138d50074cfeb6644bc2d13b1019b/langgraph_checkpoint-4.2.0.tar.gz", hash = "sha256:51a593b6bee684b0818e5d6e58e28ab340c6db7794575056ce7bd1b746a84ed7", size = 180239, upload-time = "2026-08-07T20:05:03.756Z" }
sdist = { url = "https://files.pythonhosted.org/packages/0f/69/31fdbdc65a85bbd6178afa193c772bb926620f47b4869638bc2bc80afaaa/langgraph_checkpoint-4.3.0.tar.gz", hash = "sha256:c75965d84cc2c1d549163e910a15bcb577758001b141619d05297c463280b018", size = 182652, upload-time = "2026-10-12T22:26:31.478Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/71/3b475f09bd57d3a5649792c66353312b4432afd843f301739dfcebd157f0/langgraph_checkpoint-4.2.0-py3-none-any.whl", hash = "sha256:0547fd228935a0b758865de3a3d6d7a2537c308895d0f9ab092ce9151b5da942", size = 56833, upload-time = "2026-08-07T20:05:02.655Z" },
{ url = "https://files.pythonhosted.org/packages/1f/0c/84747e340bf4f29291c84cdd5733fc8d0a822f3d33bb24e664a18afa4a7c/langgraph_checkpoint-4.3.0-py3-none-any.whl", hash = "sha256:bedfafe2f997ded60e4fa593e79f56f436a6e45586392dc382aa810d0c751c64", size = 58063, upload-time = "2026-10-12T22:26:30.429Z" },
]
[[package]]
name = "langgraph-checkpoint-postgres"
version = "3.1.1"
version = "3.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langgraph-checkpoint" },
@@ -288,9 +288,9 @@ dependencies = [
{ name = "psycopg" },
{ name = "psycopg-pool" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/92/1e8959f8cd1b56e672fde3227f6fd642be85af6c5fd662d73921074aa39d/langgraph_checkpoint_postgres-3.1.1.tar.gz", hash = "sha256:d320e147ddad8c374cd546df0b52b532dd54d0541dd9fd23fc738cbd5de76f41", size = 150413, upload-time = "2026-07-30T19:15:39.014Z" }
sdist = { url = "https://files.pythonhosted.org/packages/78/bf/d0ab4d6e4d61952de2f77044d7407b7ce09e07d53e7bb448cf9df55c35e5/langgraph_checkpoint_postgres-3.1.3.tar.gz", hash = "sha256:a152a9c0c3d5931bc949b64e01e8c7da20be57a32aa754626446318e90a07650", size = 158122, upload-time = "2026-10-12T23:05:19.759Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/03/32/ba457698a48a0e18d786caa770033067049fbe36d6846f8e50f13b594b51/langgraph_checkpoint_postgres-3.1.1-py3-none-any.whl", hash = "sha256:6e353aecd8150de144fef8e51a49076f58b7d6830d4cf51392b7ad4d79832ba7", size = 50778, upload-time = "2026-07-30T19:15:37.405Z" },
{ url = "https://files.pythonhosted.org/packages/41/42/659106ed829ee026144e32ddd589735f978d2ed09681020e5965cdfca04c/langgraph_checkpoint_postgres-3.1.3-py3-none-any.whl", hash = "sha256:050ae583223e24d97747f27b9e06e7345bf13c972f1fb6ed33bb3d1f9c11cee4", size = 52048, upload-time = "2026-10-12T23:05:18.854Z" },
]
[[package]]
@@ -308,7 +308,7 @@ wheels = [
[[package]]
name = "langgraph-sdk"
version = "0.4.4"
version = "0.4.11"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -317,9 +317,9 @@ dependencies = [
{ name = "orjson" },
{ name = "websockets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3f/ae/91446c1fffa04a2dc1f81afbfb5bfff3590452891a72bd9098a656ab2657/langgraph_sdk-0.4.4.tar.gz", hash = "sha256:4e651ffa09de695681579396375377bdde23bedbc8e35b070e615cbd5af7da8b", size = 345789, upload-time = "2026-08-27T21:25:22.593Z" }
sdist = { url = "https://files.pythonhosted.org/packages/64/a8/d5807df5976f2b8e181baddbddf857cc0ba501bba274e81711b10385c65b/langgraph_sdk-0.4.11.tar.gz", hash = "sha256:2550af3bc7cb78ac4facb97d141ec1a76568fa3eb3d29040cff70b45bda84740", size = 366656, upload-time = "2026-10-30T22:17:49.202Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/d5/2ad7f3a835c6fcebf58b6669552536ecd52d2dfe4cf49c6c36648fd83572/langgraph_sdk-0.4.4-py3-none-any.whl", hash = "sha256:39afe416c91742925e6f8a93715f566d499b36e1b636b804a4ffe3190e4f4e64", size = 162270, upload-time = "2026-08-27T21:25:21.072Z" },
{ url = "https://files.pythonhosted.org/packages/01/88/69899bb20b96b80468e98b90377e605c36658125e7e7fba401868a86138e/langgraph_sdk-0.4.11-py3-none-any.whl", hash = "sha256:6a65be4a8f825966a07317b8e34e3337cd227b9b58b185f0123fd13c3d0116fa", size = 169311, upload-time = "2026-10-30T22:17:47.96Z" },
]
[[package]]
+10 -10
View File
@@ -1,5 +1,5 @@
version = 1
revision = 3
revision = 5
requires-python = ">=3.11"
[[package]]
@@ -224,7 +224,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.11"
version = "1.2.19"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langchain-core" },
@@ -234,22 +234,22 @@ dependencies = [
{ name = "pydantic" },
{ name = "xxhash" },
]
sdist = { url = "https://files.pythonhosted.org/packages/56/0d/c8e7ee98896659e1b6555db0ab115a9ca899844744645d5d894032bab1d7/langgraph-1.2.11.tar.gz", hash = "sha256:9ecfe11e50d338b34b15cf4d8a442642de103e8ae6971320efba84e4542eb363", size = 725753, upload-time = "2026-08-11T14:00:36.945Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ba/10/329dc49ce3fc40e36faafd407f08607fe3fab75613e709a66758b53faac2/langgraph-1.2.19.tar.gz", hash = "sha256:4a00fa1aad2b5fc2d3a789a8e9eaf993f76218ca924b434902ecf1bbaaf300f6", size = 782618, upload-time = "2026-10-30T22:23:32.962Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/7f/c5c30e4be99ff821029c7ac872a480676bb179c9f3df85ea3f38d13f86d4/langgraph-1.2.11-py3-none-any.whl", hash = "sha256:8bab70de7b2d00b5300fb289bcf38d8b241400f3184c1e95e8ce706fb0e8686b", size = 248854, upload-time = "2026-08-11T14:00:35.494Z" },
{ url = "https://files.pythonhosted.org/packages/d7/9e/bd29e368fb16feea8d24270d377d56c34b49afea371717124f19cdfa890c/langgraph-1.2.19-py3-none-any.whl", hash = "sha256:6094a5cbed52a77d8c74c276fcd2056abde7b2836eab1c7a6b74bacbd1a269b3", size = 262285, upload-time = "2026-10-30T22:23:31.334Z" },
]
[[package]]
name = "langgraph-checkpoint"
version = "4.1.1"
version = "4.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langchain-core" },
{ name = "ormsgpack" },
]
sdist = { url = "https://files.pythonhosted.org/packages/83/47/886af6f886f0bff2273164a45f008694e48a96ff3cd25ff0228f2aa9480e/langgraph_checkpoint-4.1.1.tar.gz", hash = "sha256:6c2bdb530c91f91d7d9c1bd100925d0fc4f498d418c17f3587d1526279482a25", size = 184020, upload-time = "2026-05-22T16:57:38.503Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f0/c4/f4107f50dd07169d08d95766cd69104231e694f44a1b25b68ad8b393712d/langgraph_checkpoint-4.3.1.tar.gz", hash = "sha256:625e7c78a72b33e7c47f7e87ca051060fb90aa66f5fd551c472c08376fb669cb", size = 182679, upload-time = "2026-10-26T19:57:31.416Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bd/b4/71425e3e38be92611300b9cc5e46a5bf98ab23f5ea8a75b73d02a2f1413c/langgraph_checkpoint-4.1.1-py3-none-any.whl", hash = "sha256:25d29144b082827218e7bc3f1e9b0566a4bb007895cd6cc26f66a8428739f56e", size = 56212, upload-time = "2026-05-22T16:57:37.203Z" },
{ url = "https://files.pythonhosted.org/packages/f3/40/4251d676b5c585d42175c901175d4bff56e22939625e18bcef781fc3e893/langgraph_checkpoint-4.3.1-py3-none-any.whl", hash = "sha256:215a826935c8db4d9676e79fd95943e5b3847d78604390898e1d98581cc594e5", size = 58093, upload-time = "2026-10-26T19:57:30.361Z" },
]
[[package]]
@@ -267,7 +267,7 @@ wheels = [
[[package]]
name = "langgraph-sdk"
version = "0.4.4"
version = "0.4.11"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -276,9 +276,9 @@ dependencies = [
{ name = "orjson" },
{ name = "websockets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3f/ae/91446c1fffa04a2dc1f81afbfb5bfff3590452891a72bd9098a656ab2657/langgraph_sdk-0.4.4.tar.gz", hash = "sha256:4e651ffa09de695681579396375377bdde23bedbc8e35b070e615cbd5af7da8b", size = 345789, upload-time = "2026-08-27T21:25:22.593Z" }
sdist = { url = "https://files.pythonhosted.org/packages/64/a8/d5807df5976f2b8e181baddbddf857cc0ba501bba274e81711b10385c65b/langgraph_sdk-0.4.11.tar.gz", hash = "sha256:2550af3bc7cb78ac4facb97d141ec1a76568fa3eb3d29040cff70b45bda84740", size = 366656, upload-time = "2026-10-30T22:17:49.202Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/d5/2ad7f3a835c6fcebf58b6669552536ecd52d2dfe4cf49c6c36648fd83572/langgraph_sdk-0.4.4-py3-none-any.whl", hash = "sha256:39afe416c91742925e6f8a93715f566d499b36e1b636b804a4ffe3190e4f4e64", size = 162270, upload-time = "2026-08-27T21:25:21.072Z" },
{ url = "https://files.pythonhosted.org/packages/01/88/69899bb20b96b80468e98b90377e605c36658125e7e7fba401868a86138e/langgraph_sdk-0.4.11-py3-none-any.whl", hash = "sha256:6a65be4a8f825966a07317b8e34e3337cd227b9b58b185f0123fd13c3d0116fa", size = 169311, upload-time = "2026-10-30T22:17:47.96Z" },
]
[[package]]
+132 -66
View File
@@ -1,10 +1,10 @@
version = 1
revision = 3
revision = 5
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.15'",
"python_full_version >= '3.12' and python_full_version < '3.15' and sys_platform == 'emscripten'",
"(python_full_version >= '3.11' and python_full_version < '3.15' and sys_platform != 'emscripten') or (python_full_version == '3.11.*' and sys_platform == 'emscripten')",
"python_full_version == '3.11.*' or (python_full_version >= '3.12' and python_full_version < '3.15' and sys_platform != 'emscripten')",
"python_full_version < '3.11'",
]
@@ -24,7 +24,8 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "idna" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" },
{ name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" }
wheels = [
@@ -416,7 +417,7 @@ version = "50.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" }
wheels = [
@@ -469,11 +470,11 @@ wheels = [
[[package]]
name = "distlib"
version = "0.4.0"
version = "0.4.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
sdist = { url = "https://files.pythonhosted.org/packages/68/bc/d36966de7304047a1001a7559fbf266c464bb5d8cc85464536ef8c9bcc32/distlib-0.4.4.tar.gz", hash = "sha256:ca4175b4ac7f71223cc30ed7248c45d2b38c2439d8ee6e498ab67ed63cf3508d", size = 621304, upload-time = "2026-10-16T13:56:34.203Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
{ url = "https://files.pythonhosted.org/packages/c1/2b/238f02a4927fa2e27aa995371d240a468b52e42eae44a678434aff736b38/distlib-0.4.4-py2.py3-none-any.whl", hash = "sha256:cf09d55b1e85a89576b036aeb71550bfd68320f3f21761ca329eadadad918638", size = 472099, upload-time = "2026-10-16T13:56:32.053Z" },
]
[[package]]
@@ -496,7 +497,7 @@ name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
@@ -535,7 +536,7 @@ name = "grpcio"
version = "1.80.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" }
wheels = [
@@ -811,7 +812,8 @@ dependencies = [
{ name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" },
{ name = "idna" },
{ name = "truststore", marker = "sys_platform != 'emscripten'" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" },
{ name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7f/f8/579a8b51e42e38ee32647df9f08aa25643ae788e275cc625b199829c4671/httpx2-2.12.0.tar.gz", hash = "sha256:7631fe9887a8a2275f4a2540e053aa670fcc50742864a9ae7c66e609fdcf12cf", size = 100040, upload-time = "2026-08-18T13:22:09.086Z" }
wheels = [
@@ -990,9 +992,11 @@ wheels = [
[[package]]
name = "langchain-core"
version = "1.4.0"
version = "1.7.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "httpx" },
{ name = "jsonpatch" },
{ name = "langchain-protocol" },
{ name = "langsmith" },
@@ -1000,29 +1004,29 @@ dependencies = [
{ name = "pydantic" },
{ name = "pyyaml" },
{ name = "tenacity" },
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
{ name = "uuid-utils" },
]
sdist = { url = "https://files.pythonhosted.org/packages/59/de/679a53472c25860837e32c0442c962fa86e95317a36460e2c9d5c91b17c2/langchain_core-1.4.0.tar.gz", hash = "sha256:1dc341eed802ed9c117c0df3923c991e5e9e226571e5725c194eeb5bd93d1a7f", size = 920260, upload-time = "2026-05-11T18:42:35.919Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ac/90/f932d9c82d15033ae9676fb3670993e10e1b4d9c59fe1b7ae024dee11955/langchain_core-1.7.3.tar.gz", hash = "sha256:77f70b81e238c981bfd8405840b03fdf143735ac93c0729c2bdb39d6f0648024", size = 1029116, upload-time = "2026-10-29T20:55:56.42Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0f/1a/86c38c27b81913a1c6c12448cab55defb5a1097c7dc9a4cea83f55477a2d/langchain_core-1.4.0-py3-none-any.whl", hash = "sha256:23cbbdb46e38ddd1dd5247e6167e96013eae74bea4c5949c550809970a9e565c", size = 548120, upload-time = "2026-05-11T18:42:33.992Z" },
{ url = "https://files.pythonhosted.org/packages/b4/c6/88bcc77899601e09a428d9c764e41ac3e7ede161384f85b9a2a8a828f81d/langchain_core-1.7.3-py3-none-any.whl", hash = "sha256:460c214bf31495d7b7e0a879e408dbfda677fd53fd879da786c1b966a4f6bff6", size = 577074, upload-time = "2026-10-29T20:55:54.722Z" },
]
[[package]]
name = "langchain-protocol"
version = "0.0.17"
version = "0.0.20"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/44/b3/4e2429876c7a35585618caa2b9f9089f7162a6b50562b614ad82ac11c17e/langchain_protocol-0.0.17.tar.gz", hash = "sha256:e7cbe58c205df4b4fd87dc6d5bb23f10e13b236d0e2e1b0b9d05bc2b648f3eea", size = 6026, upload-time = "2026-06-12T18:39:51.923Z" }
sdist = { url = "https://files.pythonhosted.org/packages/cb/8b/d908d082c3cf5368ddd1816ed114de79c873f979ba7f98b95fab0e841643/langchain_protocol-0.0.20.tar.gz", hash = "sha256:0f5bd002474265648e91161771a9e0438fe9480bd55a416e51af0f60070637ec", size = 6280, upload-time = "2026-10-20T11:13:50.84Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/0a/a1bfe72c6ec856e99773bbd96c8086421e554b3693d0142b9ea009c6ac92/langchain_protocol-0.0.17-py3-none-any.whl", hash = "sha256:982a08fe152586ed10d4ff3d538c2e0b5766e5f307cdea325e10be3f2c17cae6", size = 7096, upload-time = "2026-06-12T18:39:50.973Z" },
{ url = "https://files.pythonhosted.org/packages/02/4b/f1c99a0b27c5ff8b4b131c0d6e62012c17cc424089efe213767db927afd2/langchain_protocol-0.0.20-py3-none-any.whl", hash = "sha256:3c935e5e46b0f6f9cb52f45ba7cfa53d1b9fa80c82c4a277d47f03e07f7385b1", size = 7347, upload-time = "2026-10-20T11:13:49.566Z" },
]
[[package]]
name = "langgraph"
version = "1.2.3"
version = "1.2.14"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langchain-core" },
@@ -1032,9 +1036,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "xxhash" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5c/55/e42f59b5e4cce75085f68abb77114c4d1152628157bd60c2587881bca559/langgraph-1.2.3.tar.gz", hash = "sha256:7f89cd5f0946fe29bd7ca048e2a84d3c14e7f652e38bb98e00f0ba8b7004b9d0", size = 718812, upload-time = "2026-06-01T18:55:54.458Z" }
sdist = { url = "https://files.pythonhosted.org/packages/49/3f/6e60789eab2c5ceaf34ba2c626ac345df93b026bbe7385018aa4b9c30397/langgraph-1.2.14.tar.gz", hash = "sha256:5b1a72b7a7f994ac6947b7751fbbe00e949f8bb44d9ab78881604b703247ae55", size = 752793, upload-time = "2026-10-06T14:41:19.789Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/9b/287f11be799f2f4dd289ab19d23788df22ac20ce884a010d0696f79016db/langgraph-1.2.3-py3-none-any.whl", hash = "sha256:22e6c89084adb3edb7855f6ffa692af3d75925f9c70deb43b86da14db1b02c78", size = 244841, upload-time = "2026-06-01T18:55:52.945Z" },
{ url = "https://files.pythonhosted.org/packages/ec/4a/f976df0d3cf7fb7986dc39f656c6b7b5655834430e9bdfaef1e1e4fffaba/langgraph-1.2.14-py3-none-any.whl", hash = "sha256:e3f5f8ff4259f9281b73ff50fc9ef5cc3b025b8936b8283ecdca7cc0c4128690", size = 255980, upload-time = "2026-10-06T14:41:18.129Z" },
]
[[package]]
@@ -1207,7 +1211,7 @@ wheels = [
[[package]]
name = "langgraph-sdk"
version = "0.4.5"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -1216,30 +1220,34 @@ dependencies = [
{ name = "orjson" },
{ name = "websockets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e8/9c/7ff305b366ce986052a27dc5946839bef2823b3567664cbad22e294dff5a/langgraph_sdk-0.4.5.tar.gz", hash = "sha256:d49a98a2ee8e0c494b101a7caf8061c8b4b94d3ee5ada0ea2dbe50903a5487b1", size = 349783, upload-time = "2026-09-21T14:42:55.732Z" }
sdist = { url = "https://files.pythonhosted.org/packages/78/85/54edf4902c0378510f09a009f64e3d3f5644aeeb51d13cf67ec380a5245b/langgraph_sdk-0.4.6.tar.gz", hash = "sha256:ea5b1749d70987d741c9b9fe2b7e1c5f7da7e4d102f64e390333c1890c9bbd5b", size = 350632, upload-time = "2026-10-06T14:17:53.641Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/a9/71a6499f3481bc00bf4f42a17cb81caf19a02fce762db356626d7214e2c2/langgraph_sdk-0.4.5-py3-none-any.whl", hash = "sha256:e03ef033a20d21288af496e9b6c08ae3afcc1b58dc5abcd37575bfd1a93045de", size = 162304, upload-time = "2026-09-21T14:42:54.623Z" },
{ url = "https://files.pythonhosted.org/packages/6c/c7/69d13026db3577c157877dea47a9dc5fffd3dcde2d1ab8009fa8f7e5f6a6/langgraph_sdk-0.4.6-py3-none-any.whl", hash = "sha256:57122b02bda41d37a97a5e09eb018aa8c9b3dc85c433880073779bd9324b7eea", size = 162390, upload-time = "2026-10-06T14:17:52.134Z" },
]
[[package]]
name = "langsmith"
version = "0.8.18"
version = "0.14.16"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
{ name = "anyio" },
{ name = "distro" },
{ name = "httpx2" },
{ name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
{ name = "packaging" },
{ name = "pydantic" },
{ name = "requests" },
{ name = "requests-toolbelt" },
{ name = "sniffio" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
{ name = "uuid-utils" },
{ name = "websockets" },
{ name = "xxhash" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9a/d9/a6681aa9847bbbc5ec21abe20a5e233b94e5edcfe39624db607ac7e8ccb4/langsmith-0.8.18.tar.gz", hash = "sha256:32dde9c0e67e053e0fb738921fc8ced768af7b8fa83d7a0e3fd63597cf8776dd", size = 4526988, upload-time = "2026-06-19T13:12:17.123Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/45/576803a72da96d9c0866b3815f08f447ed16c14acb52174129daca6847d6/langsmith-0.14.16.tar.gz", hash = "sha256:4e5816cbe1541f4d04d71f652eb8f4137889861bebcf7137bcba3553ae9e335e", size = 5096529, upload-time = "2026-10-29T17:53:27.747Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/03/70/0e0cc80a3b064c8d6c8d697c3125ed86e39d5a7393ec6dc8b07cb1cf13c4/langsmith-0.8.18-py3-none-any.whl", hash = "sha256:3940183349993faef48e6c7d08e4822ee9cefd906b362d0e3c2d650314d2f282", size = 508108, upload-time = "2026-06-19T13:12:15.348Z" },
{ url = "https://files.pythonhosted.org/packages/11/3a/9a072c02a22089ddf20f0f33ea95bee5a5951b31839f118b04f27b94be6e/langsmith-0.14.16-py3-none-any.whl", hash = "sha256:b9ea1a57622b0788b181d78605f974fb206c5f6f75793076ffec23cef4d904c8", size = 933680, upload-time = "2026-10-29T17:53:25.465Z" },
]
[package.optional-dependencies]
@@ -1337,84 +1345,114 @@ wheels = [
[[package]]
name = "opentelemetry-api"
version = "1.41.0"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata" },
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/47/8e/3778a7e87801d994869a9396b9fc2a289e5f9be91ff54a27d41eace494b0/opentelemetry_api-1.41.0.tar.gz", hash = "sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09", size = 71416, upload-time = "2026-04-09T14:38:34.544Z" }
sdist = { url = "https://files.pythonhosted.org/packages/2e/02/6e0ae9cc61bd3169d401077b507b3ebc344745171e1051ab430be012dcd9/opentelemetry_api-1.45.1.tar.gz", hash = "sha256:aa38ed19bcc084ba42782a73255b3582283eced7ad6dddbd6695189e69adfb75", size = 72804, upload-time = "2026-10-06T17:32:58.133Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/ee/99ab786653b3bda9c37ade7e24a7b607a1b1f696063172768417539d876d/opentelemetry_api-1.41.0-py3-none-any.whl", hash = "sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f", size = 69007, upload-time = "2026-04-09T14:38:11.833Z" },
{ url = "https://files.pythonhosted.org/packages/1e/41/f7dcf80b81ee8e71c1a2b59f14208bc723edbd89ed027a73b175abf6348e/opentelemetry_api-1.45.1-py3-none-any.whl", hash = "sha256:b31553efa588ae44bc306f863c785c5333a9ecc091248c6ee68b4b6c87fdedfb", size = 60256, upload-time = "2026-10-06T17:32:33.506Z" },
]
[[package]]
name = "opentelemetry-exporter-http-transport"
version = "0.66b1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
]
sdist = { url = "https://files.pythonhosted.org/packages/62/0c/e3ebdb4b507f66afcc905e6885a4946969bd75b45988492643356fbbdc63/opentelemetry_exporter_http_transport-0.66b1.tar.gz", hash = "sha256:443080203bf52586ce0b2ad901e8951c61833eab1aa539ae6f1f16fe9e8e7952", size = 11693, upload-time = "2026-10-06T17:32:59.65Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/69/6af86ff66492b481c6a4c05dcfd68beb47ed8ba046440a26a2aac76b95c7/opentelemetry_exporter_http_transport-0.66b1-py3-none-any.whl", hash = "sha256:2f95404bdee7f9d2d529c7de56c7bd86d014d774d8fbf137810e0167f8a492bf", size = 12155, upload-time = "2026-10-06T17:32:35.454Z" },
]
[package.optional-dependencies]
requests = [
{ name = "requests" },
]
[[package]]
name = "opentelemetry-exporter-otlp-common"
version = "0.66b1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-sdk" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cb/19/41de712173f43057e4532d42ece7d0c6d4210d353e5752433cb14987643f/opentelemetry_exporter_otlp_common-0.66b1.tar.gz", hash = "sha256:6b1403487a2185ac1feb45fd5546fdf8630ce71c36bcefaadf51e2130e9e23f9", size = 14325, upload-time = "2026-10-06T17:33:01.725Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fc/39/8c23d67665c762aa51840fa06f86e902e8f6f1693bc8d7e3d98cd6e2f753/opentelemetry_exporter_otlp_common-0.66b1-py3-none-any.whl", hash = "sha256:00ff8592c3a7cb729ff3fdc7ffa12372c243bdf2163e80c180994d0c7bd83ee9", size = 12385, upload-time = "2026-10-06T17:32:38.177Z" },
]
[[package]]
name = "opentelemetry-exporter-otlp-proto-common"
version = "1.41.0"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-proto" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8c/28/e8eca94966fe9a1465f6094dc5ddc5398473682180279c94020bc23b4906/opentelemetry_exporter_otlp_proto_common-1.41.0.tar.gz", hash = "sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee", size = 20411, upload-time = "2026-04-09T14:38:36.572Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c1/8e/65e85e5137991a3c493b11682151d198638a5bc1dd4b4c5f67e013c57d7c/opentelemetry_exporter_otlp_proto_common-1.45.1.tar.gz", hash = "sha256:2e4adcc3a67bcf57804fc49514f0ef64974ca7590aa3491da389852b4a0628f6", size = 18873, upload-time = "2026-10-06T17:33:04.471Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/c4/78b9bf2d9c1d5e494f44932988d9d91c51a66b9a7b48adf99b62f7c65318/opentelemetry_exporter_otlp_proto_common-1.41.0-py3-none-any.whl", hash = "sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0", size = 18366, upload-time = "2026-04-09T14:38:15.135Z" },
{ url = "https://files.pythonhosted.org/packages/84/aa/92f225d353904e7f70b8b3e3c1b02db0cf56f744c2e83c581dc372e78873/opentelemetry_exporter_otlp_proto_common-1.45.1-py3-none-any.whl", hash = "sha256:2f446183ae7047b036226f1d846c41a834b0e8755ad13b51a51dd38952eb466c", size = 15393, upload-time = "2026-10-06T17:32:41.911Z" },
]
[[package]]
name = "opentelemetry-exporter-otlp-proto-http"
version = "1.41.0"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "googleapis-common-protos" },
{ name = "opentelemetry-api" },
{ name = "opentelemetry-exporter-http-transport", extra = ["requests"] },
{ name = "opentelemetry-exporter-otlp-common" },
{ name = "opentelemetry-exporter-otlp-proto-common" },
{ name = "opentelemetry-proto" },
{ name = "opentelemetry-sdk" },
{ name = "requests" },
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/19/63/d9f43cd75f3fabb7e01148c89cfa9491fc18f6580a6764c554ff7c953c46/opentelemetry_exporter_otlp_proto_http-1.41.0.tar.gz", hash = "sha256:dcd6e0686f56277db4eecbadd5262124e8f2cc739cadbc3fae3d08a12c976cf5", size = 24139, upload-time = "2026-04-09T14:38:38.128Z" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/17/26487707ea4caa97b17e6e4b5fa72133a53512ffa2f5cf7a49ef284b29cb/opentelemetry_exporter_otlp_proto_http-1.45.1.tar.gz", hash = "sha256:45c218405ce3fd879596924b1874bf9a8f6880206d61065c5a912c8e5c297fb7", size = 28839, upload-time = "2026-10-06T17:33:05.713Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/64/b5/a214cd907eedc17699d1c2d602288ae17cb775526df04db3a3b3585329d2/opentelemetry_exporter_otlp_proto_http-1.41.0-py3-none-any.whl", hash = "sha256:a9c4ee69cce9c3f4d7ee736ad1b44e3c9654002c0816900abbafd9f3cf289751", size = 22673, upload-time = "2026-04-09T14:38:18.349Z" },
{ url = "https://files.pythonhosted.org/packages/aa/1f/517eaa0187ba106a9da97160ce2add3a371812681dc440930b267f714e42/opentelemetry_exporter_otlp_proto_http-1.45.1-py3-none-any.whl", hash = "sha256:24a97cf3753c7fb52fad44a696e452ff371686339e2acf3309e2eda3d0230700", size = 22180, upload-time = "2026-10-06T17:32:43.946Z" },
]
[[package]]
name = "opentelemetry-proto"
version = "1.41.0"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "protobuf" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e0/d9/08e3dc6156878713e8c811682bc76151f5fe1a3cb7f3abda3966fd56e71e/opentelemetry_proto-1.41.0.tar.gz", hash = "sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6", size = 45669, upload-time = "2026-04-09T14:38:45.978Z" }
sdist = { url = "https://files.pythonhosted.org/packages/4b/7f/15f014fb195da6c2dbb6c71399b8e76824878718e94de6454038488eed28/opentelemetry_proto-1.45.1.tar.gz", hash = "sha256:79e0fb95e4616691a469439238aa9224d75779b3e108e895d1aa125ab29ca77c", size = 46488, upload-time = "2026-10-06T17:33:11.49Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/8c/65ef7a9383a363864772022e822b5d5c6988e6f9dabeebb9278f5b86ebc3/opentelemetry_proto-1.41.0-py3-none-any.whl", hash = "sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247", size = 72074, upload-time = "2026-04-09T14:38:29.38Z" },
{ url = "https://files.pythonhosted.org/packages/ab/9a/42ec8180a769516ae757e893b69736826efceac7332553915b4528a91c6d/opentelemetry_proto-1.45.1-py3-none-any.whl", hash = "sha256:f38e2a8413053c180cd3d2637fbb279673ec2f6a6e09c995aafa2f452c52b46e", size = 72488, upload-time = "2026-10-06T17:32:53.057Z" },
]
[[package]]
name = "opentelemetry-sdk"
version = "1.41.0"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "opentelemetry-semantic-conventions" },
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/f8/0e/a586df1186f9f56b5a0879d52653effc40357b8e88fc50fe300038c3c08b/opentelemetry_sdk-1.41.0.tar.gz", hash = "sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd", size = 230181, upload-time = "2026-04-09T14:38:47.225Z" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/79/7392e21a1c8f0c61d90b223e31c7e48cb9d452e91a6b820ad24cca5f23c4/opentelemetry_sdk-1.45.1.tar.gz", hash = "sha256:63d24a6ca645019a631e6a51999c73e93adcac1196ca640b8ae78a7cc4762bf3", size = 218324, upload-time = "2026-10-06T17:33:13.26Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/13/a7825118208cb32e6a4edcd0a99f925cbef81e77b3b0aedfd9125583c543/opentelemetry_sdk-1.41.0-py3-none-any.whl", hash = "sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd", size = 180214, upload-time = "2026-04-09T14:38:30.657Z" },
{ url = "https://files.pythonhosted.org/packages/95/3c/87c42b4bd6dd297536f04cd9383d212ac557ecd49f2cbdcd46da1c9ef5c8/opentelemetry_sdk-1.45.1-py3-none-any.whl", hash = "sha256:c604c11dc429810812348989115fa44bd558772a3d7442afc43d024f2c250ca4", size = 140063, upload-time = "2026-10-06T17:32:55.04Z" },
]
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.62b0"
version = "0.66b1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/b0/c14f723e86c049b7bf8ff431160d982519b97a7be2857ed2247377397a24/opentelemetry_semantic_conventions-0.62b0.tar.gz", hash = "sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097", size = 145753, upload-time = "2026-04-09T14:38:48.274Z" }
sdist = { url = "https://files.pythonhosted.org/packages/46/e4/dbbfb2a010c4db2224a5114638acede6fe563d33cc20fb1752cebcbe6298/opentelemetry_semantic_conventions-0.66b1.tar.gz", hash = "sha256:497ca63bf383723411e8eaf60c8779e9877633c936bb641080adab59d0eb6ec8", size = 150250, upload-time = "2026-10-06T17:33:14.073Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/6c/5e86fa1759a525ef91c2d8b79d668574760ff3f900d114297765eb8786cb/opentelemetry_semantic_conventions-0.62b0-py3-none-any.whl", hash = "sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489", size = 231619, upload-time = "2026-04-09T14:38:32.394Z" },
{ url = "https://files.pythonhosted.org/packages/bc/14/67f8aa798857f8cf686f515bf93d9bb877ce952ddc8efae0fa25b45ce0d6/opentelemetry_semantic_conventions-0.66b1-py3-none-any.whl", hash = "sha256:d4cddeb4315490b35213f55e2bdc9ac54bb1e4d318927475bed62b35545e581b", size = 206279, upload-time = "2026-10-06T17:32:56.103Z" },
]
[[package]]
@@ -1556,11 +1594,11 @@ wheels = [
[[package]]
name = "packaging"
version = "26.1"
version = "26.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" }
sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" },
{ url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
]
[[package]]
@@ -1642,7 +1680,7 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/09/e5/06d23afac9973109d1e3c8ad38e1547a12e860610e327c05ee686827dc37/pydantic-2.13.2.tar.gz", hash = "sha256:b418196607e61081c3226dcd4f0672f2a194828abb9109e9cfb84026564df2d1", size = 843836, upload-time = "2026-04-17T09:31:59.636Z" }
@@ -1655,7 +1693,7 @@ name = "pydantic-core"
version = "2.46.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/bb/4742f05b739b2478459bb16fa8470549518c802e06ddcf3f106c5081315e/pydantic_core-2.46.2.tar.gz", hash = "sha256:37bb079f9ee3f1a519392b73fda2a96379b31f2013c6b467fe693e7f2987f596", size = 471269, upload-time = "2026-04-17T09:10:07.017Z" }
wheels = [
@@ -1818,7 +1856,8 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
{ name = "pytest" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" },
{ name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" }
wheels = [
@@ -2035,11 +2074,11 @@ wheels = [
[[package]]
name = "setuptools"
version = "83.0.0"
version = "84.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" }
sdist = { url = "https://files.pythonhosted.org/packages/73/eb/7e5f628ee2eb168fecbf8026de7856b2543d6d609cc85f086b9a91ac93d6/setuptools-84.1.0.tar.gz", hash = "sha256:23657205b63d4d31395ab917815247f694aafad28cd21316bf5fd45a51048ea0", size = 1171783, upload-time = "2026-10-19T20:11:43.489Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" },
{ url = "https://files.pythonhosted.org/packages/18/02/b4bbb90f8f29e334898ff45c6d9b1fffaa6f378017052b3023a06262b5f7/setuptools-84.1.0-py3-none-any.whl", hash = "sha256:5231689c6ac8156cd155cfb98c3fd6fb252c3e7e9bd3d78b64d3c55f132624c9", size = 819380, upload-time = "2026-10-19T20:11:41.684Z" },
]
[[package]]
@@ -2060,6 +2099,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
]
[[package]]
name = "sse-starlette"
version = "3.3.4"
@@ -2079,7 +2127,7 @@ version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" }
wheels = [
@@ -2223,17 +2271,34 @@ wheels = [
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.15'",
"python_full_version >= '3.12' and python_full_version < '3.15' and sys_platform == 'emscripten'",
"python_full_version == '3.11.*' or (python_full_version >= '3.12' and python_full_version < '3.15' and sys_platform != 'emscripten')",
]
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "typing-extensions"
version = "4.16.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.11'",
]
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
]
[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
{ name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
@@ -2375,18 +2440,19 @@ wheels = [
[[package]]
name = "virtualenv"
version = "21.7.13"
version = "21.14.13"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "distlib" },
{ name = "filelock" },
{ name = "packaging" },
{ name = "platformdirs" },
{ name = "python-discovery" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/13/50/c9b84eb106d0db420b9878dac0a386c5791726f127ce20b06897f6e3a1e9/virtualenv-21.7.13.tar.gz", hash = "sha256:0355558b6f33619aab31347e43643b0ebc97f61ea3acf617b2b69e1f8a843d11", size = 5360306, upload-time = "2026-09-18T04:35:49.354Z" }
sdist = { url = "https://files.pythonhosted.org/packages/70/aa/bff50daee0488e16d611258a9b6c5a1d0c9e128f8ca424e9f4e1c0444ef2/virtualenv-21.14.13.tar.gz", hash = "sha256:de5307ba4b9a0001d9c8ea02832d38f63ca432880f416c1bface61c4323fd905", size = 5570301, upload-time = "2026-10-22T20:10:16.952Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/ce/e74453531b0c49a58d0e8a4f9bab4495705859fee4a4f7de27d58f4a791a/virtualenv-21.7.13-py3-none-any.whl", hash = "sha256:1bea5af7463f59c4719db48fe739579a2a4f569c96f26c086edda85c96da9f59", size = 5328680, upload-time = "2026-09-18T04:35:47.194Z" },
{ url = "https://files.pythonhosted.org/packages/bb/a9/c10d8a44a29fb94a18b43fdf11f38caca53ae7c9406f11a72b1cb8f624f1/virtualenv-21.14.13-py3-none-any.whl", hash = "sha256:5fbb04138a4861580b78ba9110d529a26bc48fcc93c8ec36bc0202eeed96a788", size = 5557094, upload-time = "2026-10-22T20:10:15.096Z" },
]
[[package]]
+22 -6
View File
@@ -29,7 +29,19 @@ from langgraph._internal._constants import (
NS_SEP,
)
DEFAULT_RECURSION_LIMIT = int(getenv("LANGGRAPH_DEFAULT_RECURSION_LIMIT", "10007"))
class _DefaultRecursionLimit(int):
"""The limit `ensure_config` fills in when none is given.
`merge_configs` skips values of this type rather than any equal value, so an
explicit limit that happens to equal the default still applies. It checks the
type, not identity, because copying or pickling a config rebuilds the value.
"""
DEFAULT_RECURSION_LIMIT = _DefaultRecursionLimit(
getenv("LANGGRAPH_DEFAULT_RECURSION_LIMIT", "10007")
)
DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT = int(
getenv("LANGGRAPH_DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT", "5000")
)
@@ -182,7 +194,7 @@ def merge_configs(*configs: RunnableConfig | None) -> RunnableConfig:
base.get("callbacks"), cast(Callbacks, value)
)
elif key == "recursion_limit":
if config["recursion_limit"] != DEFAULT_RECURSION_LIMIT:
if not isinstance(config["recursion_limit"], _DefaultRecursionLimit):
base["recursion_limit"] = config["recursion_limit"]
else:
base[key] = config[key] # type: ignore[literal-required]
@@ -335,6 +347,7 @@ def ensure_config(*configs: RunnableConfig | None) -> RunnableConfig:
recursion_limit=DEFAULT_RECURSION_LIMIT,
configurable={},
)
inherited_tags: set[str] = set()
if var_config := var_child_runnable_config.get():
empty.update(
{
@@ -343,6 +356,7 @@ def ensure_config(*configs: RunnableConfig | None) -> RunnableConfig:
if _is_not_empty(v)
},
)
inherited_tags.update(empty["tags"])
# An explicit config that supplies its own checkpoint coordinate (a
# thread_id, or any checkpoint_ns/checkpoint_id/checkpoint_map) is addressing
# its own checkpoint lineage, so drop the inherited ambient configurable
@@ -395,12 +409,14 @@ def ensure_config(*configs: RunnableConfig | None) -> RunnableConfig:
elif k == "tags":
# Concatenate tags across configs so values bound via
# with_config(...) are preserved when later configs
# supply additional tags. Matches merge_configs.
# supply additional tags. Matches merge_configs. Tags the
# ambient config already has are skipped: a subgraph gets
# its parent's config both explicitly and as the ambient
# one, so they would double at every nesting level.
existing_tags: list[str] | None = empty.get("tags")
new_tags = [t for t in cast(list, v) if t not in inherited_tags]
empty["tags"] = (
[*existing_tags, *cast(list, v)]
if existing_tags
else list(cast(list, v))
[*existing_tags, *new_tags] if existing_tags else new_tags
)
else:
empty[k] = v # type: ignore[literal-required]
+119 -60
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import asyncio
import copy
import enum
import inspect
import logging
@@ -23,9 +24,16 @@ from typing import (
Optional,
Protocol,
TypeGuard,
TypeVar,
cast,
)
from langchain_core.callbacks.base import BaseCallbackHandler, BaseCallbackManager
from langchain_core.callbacks.manager import (
AsyncCallbackManagerForChainRun,
BaseRunManager,
CallbackManagerForChainRun,
)
from langchain_core.runnables.base import (
Runnable,
RunnableConfig,
@@ -41,6 +49,7 @@ from langchain_core.runnables.config import (
var_child_runnable_config,
)
from langchain_core.runnables.utils import Input, Output
from langchain_core.tracers.base import BaseTracer
from langchain_core.tracers.langchain import LangChainTracer
from langgraph.store.base import BaseStore
@@ -57,7 +66,7 @@ from langgraph._internal._constants import (
)
from langgraph._internal._typing import MISSING
from langgraph.errors import NodeError
from langgraph.types import StreamWriter
from langgraph.types import StreamWriter, TracePolicy
try:
from langchain_core.tracers._streaming import _StreamingCallbackHandler
@@ -85,6 +94,29 @@ def _trace_payload(value: Any, transform: Callable[[Any], Any] | None) -> Any:
return value
def _is_stream_handler(handler: BaseCallbackHandler) -> bool:
# Inheritance, not isinstance on the runtime-checkable protocol, which also
# matches any exporter that merely has the tap methods: stream handlers get a
# node's real payload and its streamed output.
return _StreamingCallbackHandler in type(handler).__mro__
_M = TypeVar("_M", bound=BaseCallbackManager | BaseRunManager)
def _split_handlers(manager: _M) -> tuple[_M, _M]:
# The recorded payload exists to keep tracer ingest small, so only tracers get it.
# `astream_log`'s handler is a tracer too, but it feeds the caller's own stream.
real, tracers = copy.copy(manager), copy.copy(manager)
tracers.handlers = [
h
for h in manager.handlers
if isinstance(h, BaseTracer) and not _is_stream_handler(h)
]
real.handlers = [h for h in manager.handlers if h not in tracers.handlers]
return real, tracers
def _set_config_context(
config: RunnableConfig, run: Any = None
) -> Token[RunnableConfig | None]:
@@ -593,13 +625,15 @@ class RunnableSeq(Runnable):
*steps: RunnableLike,
name: str | None = None,
trace_inputs: Callable[[Any], Any] | None = None,
trace_outputs: Callable[[Any], Any] | None = None,
trace_policy: TracePolicy | None = None,
) -> None:
"""Create a new RunnableSeq.
Args:
steps: The steps to include in the sequence.
name: The name of the `Runnable`.
trace_inputs: Transforms the input every callback handler receives.
trace_policy: Processes what tracers record, after `trace_inputs`.
Raises:
ValueError: If the sequence has less than 2 steps.
@@ -619,7 +653,67 @@ class RunnableSeq(Runnable):
self.steps = steps_flat
self.name = name
self.trace_inputs = trace_inputs
self.trace_outputs = trace_outputs
self.trace_policy = trace_policy or TracePolicy()
def _start(self, input: Any, config: RunnableConfig) -> CallbackManagerForChainRun:
callback_manager = get_callback_manager_for_config(config)
payload = _trace_payload(input, self.trace_inputs)
recorded = _trace_payload(payload, self.trace_policy.process_inputs)
name = config.get("run_name") or self.get_name()
run_id = config.pop("run_id", None)
if recorded is payload:
return callback_manager.on_chain_start(
None, payload, name=name, run_id=run_id
)
real, tracers = _split_handlers(callback_manager)
run_manager = real.on_chain_start(None, payload, name=name, run_id=run_id)
tracers.on_chain_start(None, recorded, name=name, run_id=run_manager.run_id)
run_manager.handlers = callback_manager.handlers
return run_manager
async def _astart(
self, input: Any, config: RunnableConfig
) -> AsyncCallbackManagerForChainRun:
callback_manager = get_async_callback_manager_for_config(config)
payload = _trace_payload(input, self.trace_inputs)
recorded = _trace_payload(payload, self.trace_policy.process_inputs)
name = config.get("run_name") or self.get_name()
run_id = config.pop("run_id", None)
if recorded is payload:
return await callback_manager.on_chain_start(
None, payload, name=name, run_id=run_id
)
real, tracers = _split_handlers(callback_manager)
# Tracers start last, so a cancellation before then leaves no tracer run open.
run_manager = await real.on_chain_start(None, payload, name=name, run_id=run_id)
await tracers.on_chain_start(
None, recorded, name=name, run_id=run_manager.run_id
)
run_manager.handlers = callback_manager.handlers
return run_manager
def _end(self, run_manager: CallbackManagerForChainRun, output: Any) -> None:
recorded = _trace_payload(output, self.trace_policy.process_outputs)
if recorded is output:
run_manager.on_chain_end(output)
else:
real, tracers = _split_handlers(run_manager)
tracers.on_chain_end(recorded)
real.on_chain_end(output)
async def _aend(
self, run_manager: AsyncCallbackManagerForChainRun, output: Any
) -> None:
recorded = _trace_payload(output, self.trace_policy.process_outputs)
if recorded is output:
await run_manager.on_chain_end(output)
else:
real, tracers = _split_handlers(run_manager)
# Tracers end first: on_chain_end is shielded once it starts, so a
# cancellation can only skip the other handlers' end, never leave a
# tracer run open.
await tracers.on_chain_end(recorded)
await real.on_chain_end(output)
def __or__(
self,
@@ -676,15 +770,8 @@ class RunnableSeq(Runnable):
) -> Any:
if config is None:
config = ensure_config()
# setup callbacks and context
callback_manager = get_callback_manager_for_config(config)
# start the root run
run_manager = callback_manager.on_chain_start(
None,
_trace_payload(input, self.trace_inputs),
name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None),
)
run_manager = self._start(input, config)
# invoke all steps in sequence
try:
for i, step in enumerate(self.steps):
@@ -712,7 +799,7 @@ class RunnableSeq(Runnable):
run_manager.on_chain_error(e)
raise
else:
run_manager.on_chain_end(_trace_payload(input, self.trace_outputs))
self._end(run_manager, input)
return input
async def ainvoke(
@@ -723,15 +810,8 @@ class RunnableSeq(Runnable):
) -> Any:
if config is None:
config = ensure_config()
# setup callbacks
callback_manager = get_async_callback_manager_for_config(config)
# start the root run
run_manager = await callback_manager.on_chain_start(
None,
_trace_payload(input, self.trace_inputs),
name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None),
)
run_manager = await self._astart(input, config)
# invoke all steps in sequence
try:
@@ -765,7 +845,7 @@ class RunnableSeq(Runnable):
await run_manager.on_chain_error(e)
raise
else:
await run_manager.on_chain_end(_trace_payload(input, self.trace_outputs))
await self._aend(run_manager, input)
return input
def stream(
@@ -776,15 +856,8 @@ class RunnableSeq(Runnable):
) -> Iterator[Any]:
if config is None:
config = ensure_config()
# setup callbacks
callback_manager = get_callback_manager_for_config(config)
# start the root run
run_manager = callback_manager.on_chain_start(
None,
_trace_payload(input, self.trace_inputs),
name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None),
)
run_manager = self._start(input, config)
# get the run object
for h in run_manager.handlers:
if isinstance(h, LangChainTracer):
@@ -814,10 +887,9 @@ class RunnableSeq(Runnable):
)
iterator = step.transform(iterator, config)
# populates streamed_output in astream_log() output if needed
if _StreamingCallbackHandler is not None:
for h in run_manager.handlers:
if isinstance(h, _StreamingCallbackHandler):
iterator = h.tap_output_iter(run_manager.run_id, iterator)
for h in run_manager.handlers:
if _is_stream_handler(h):
iterator = h.tap_output_iter(run_manager.run_id, iterator)
# consume into final output
output = context.run(_consume_iter, iterator)
# sequence doesn't emit output, yield to mark as generator
@@ -826,7 +898,7 @@ class RunnableSeq(Runnable):
run_manager.on_chain_error(e)
raise
else:
run_manager.on_chain_end(_trace_payload(output, self.trace_outputs))
self._end(run_manager, output)
async def astream(
self,
@@ -836,15 +908,8 @@ class RunnableSeq(Runnable):
) -> AsyncIterator[Any]:
if config is None:
config = ensure_config()
# setup callbacks
callback_manager = get_async_callback_manager_for_config(config)
# start the root run
run_manager = await callback_manager.on_chain_start(
None,
_trace_payload(input, self.trace_inputs),
name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None),
)
run_manager = await self._astart(input, config)
# stream the last steps
# transform the input stream of each step with the next
# steps that don't natively support transforming an input stream will
@@ -880,12 +945,11 @@ class RunnableSeq(Runnable):
if hasattr(aiterator, "aclose"):
stack.push_async_callback(aiterator.aclose)
# populates streamed_output in astream_log() output if needed
if _StreamingCallbackHandler is not None:
for h in run_manager.handlers:
if isinstance(h, _StreamingCallbackHandler):
aiterator = h.tap_output_aiter(
run_manager.run_id, aiterator
)
for h in run_manager.handlers:
if _is_stream_handler(h):
aiterator = h.tap_output_aiter(
run_manager.run_id, aiterator
)
# consume into final output
output = await asyncio.create_task(
_consume_aiter(aiterator), context=context
@@ -896,9 +960,7 @@ class RunnableSeq(Runnable):
await run_manager.on_chain_error(e)
raise
else:
await run_manager.on_chain_end(
_trace_payload(output, self.trace_outputs)
)
await self._aend(run_manager, output)
else:
try:
async with AsyncExitStack() as stack:
@@ -914,12 +976,11 @@ class RunnableSeq(Runnable):
if hasattr(aiterator, "aclose"):
stack.push_async_callback(aiterator.aclose)
# populates streamed_output in astream_log() output if needed
if _StreamingCallbackHandler is not None:
for h in run_manager.handlers:
if isinstance(h, _StreamingCallbackHandler):
aiterator = h.tap_output_aiter(
run_manager.run_id, aiterator
)
for h in run_manager.handlers:
if _is_stream_handler(h):
aiterator = h.tap_output_aiter(
run_manager.run_id, aiterator
)
# consume into final output
output = await _consume_aiter(aiterator)
# sequence doesn't emit output, yield to mark as generator
@@ -928,9 +989,7 @@ class RunnableSeq(Runnable):
await run_manager.on_chain_error(e)
raise
else:
await run_manager.on_chain_end(
_trace_payload(output, self.trace_outputs)
)
await self._aend(run_manager, output)
def _consume_iter(it: Iterator[Any]) -> Any:
+2 -2
View File
@@ -60,7 +60,7 @@ class DeltaChannel(Generic[Value], BaseChannel[Any, Any, Any]):
typ: The value type (e.g. `list`, `dict`). Inferred automatically
from the outer type when used inside `Annotated[T, DeltaChannel(...)]`.
snapshot_frequency: Every Nth update to this channel writes a snapshot
blob (default `1000`). Must be a positive int.
blob (default `100`). Must be a positive int.
"""
__slots__ = ("value", "reducer", "snapshot_frequency")
@@ -71,7 +71,7 @@ class DeltaChannel(Generic[Value], BaseChannel[Any, Any, Any]):
reducer: Callable[[Any, Sequence[Any]], Any],
typ: type[Value] | None = None,
*,
snapshot_frequency: int = 1000,
snapshot_frequency: int = 100,
) -> None:
if snapshot_frequency <= 0:
raise ValueError(
+8 -11
View File
@@ -22,7 +22,7 @@ from langchain_core.messages import (
)
from typing_extensions import TypedDict, deprecated
from langgraph._internal._constants import CONF, CONFIG_KEY_SEND, NS_SEP
from langgraph._internal._constants import CONF, CONFIG_KEY_SEND
from langgraph.graph.state import StateGraph
from langgraph.pregel._messages import (
_messages_delta_reducer as _messages_delta_reducer,
@@ -343,7 +343,7 @@ def push_message(
)
from langgraph.config import get_config
from langgraph.pregel._messages import StreamMessagesHandler
from langgraph.pregel._messages import StreamMessagesHandler, graph_namespace
config = get_config()
message = next(x for x in convert_to_messages([message]))
@@ -359,15 +359,12 @@ def push_message(
):
handlers = config["callbacks"]
if stream_handler := next(
(x for x in handlers if isinstance(x, StreamMessagesHandler)), None
):
metadata = config["metadata"]
message_meta = (
tuple(cast(str, metadata["langgraph_checkpoint_ns"]).split(NS_SEP)),
metadata,
)
stream_handler._emit(message_meta, message, dedupe=False)
metadata = config["metadata"]
for handler in handlers:
if isinstance(handler, StreamMessagesHandler):
ns = graph_namespace(metadata)
if handler.subgraphs or not ns or ns == handler.parent_ns:
handler._emit((ns, metadata), message, dedupe=False)
if state_key:
config[CONF][CONFIG_KEY_SEND]([(state_key, message)])
+4
View File
@@ -228,6 +228,10 @@ def draw_graph(
metadata["__interrupt"] = "before"
elif name in interrupt_after_nodes:
metadata["__interrupt"] = "after"
# Duck-typed so core doesn't import ToolNode from langgraph-prebuilt.
tools_by_name = getattr(node.bound, "tools_by_name", None)
if isinstance(tools_by_name, Mapping) and tools_by_name:
metadata["__tools"] = list(tools_by_name)
graph.add_node(node.bound, name, metadata=metadata or None)
# add start node
if START not in nodes:
+10 -2
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
import concurrent.futures
import time
import weakref
from collections.abc import Awaitable, Callable, Coroutine
from contextlib import AbstractAsyncContextManager, AbstractContextManager, ExitStack
from contextvars import copy_context
@@ -37,6 +38,13 @@ class Submit(Protocol[P, T]):
) -> concurrent.futures.Future[T]: ...
# Futures whose exception a node's error handler took over; the executors
# don't re-raise them on exit.
SKIP_RERAISE_SET: weakref.WeakSet[concurrent.futures.Future | asyncio.Future] = (
weakref.WeakSet()
)
class BackgroundExecutor(AbstractContextManager):
"""A context manager that runs sync tasks in the background.
Uses a thread pool executor to delegate tasks to separate threads.
@@ -111,7 +119,7 @@ class BackgroundExecutor(AbstractContextManager):
if exc_type is None:
# re-raise the first exception that occurred in a task
for task, (_, reraise) in tasks.items():
if not reraise:
if not reraise or task in SKIP_RERAISE_SET:
continue
try:
task.result()
@@ -202,7 +210,7 @@ class AsyncBackgroundExecutor(AbstractAsyncContextManager):
if exc_type is None:
# re-raise the first exception that occurred in a task
for task, (_, reraise) in tasks.items():
if not reraise:
if not reraise or task in SKIP_RERAISE_SET:
continue
try:
if exc := task.exception():
+46 -40
View File
@@ -657,48 +657,11 @@ class PregelLoop:
# check if iteration limit is reached
if self.step > self.stop:
self.status = "out_of_steps"
# stream the checkpoint the last step saved, as a tick that runs would
self._prepare_tasks_and_emit_checkpoint()
return False
# prepare next tasks
self.tasks = prepare_next_tasks(
self.checkpoint,
self.checkpoint_pending_writes,
self.nodes,
self.channels,
self.managed,
self.config,
self.step,
self.stop,
for_execution=True,
manager=self.manager,
store=self.store,
checkpointer=self.checkpointer,
trigger_to_nodes=self.trigger_to_nodes,
updated_channels=self.updated_channels,
retry_policy=self.retry_policy,
cache_policy=self.cache_policy,
)
# produce debug output
if self._checkpointer_put_after_previous is not None:
self._emit(
"checkpoints",
map_debug_checkpoint,
{
**self.checkpoint_config,
CONF: {
**self.checkpoint_config[CONF],
CONFIG_KEY_CHECKPOINT_ID: self.checkpoint["id"],
},
},
self.channels,
self.stream_keys,
self.checkpoint_metadata,
self.tasks.values(),
self.checkpoint_pending_writes,
self.prev_checkpoint_config,
self.output_keys,
)
self._prepare_tasks_and_emit_checkpoint()
# if no more tasks, we're done
if not self.tasks:
@@ -806,10 +769,53 @@ class PregelLoop:
self.checkpoint, self.interrupt_after, self.tasks.values()
):
self.status = "interrupt_after"
# the next tick, which streams this checkpoint, never runs
self._prepare_tasks_and_emit_checkpoint()
raise GraphInterrupt()
# unset resuming flag
self.config[CONF].pop(CONFIG_KEY_RESUMING, None)
def _prepare_tasks_and_emit_checkpoint(self) -> None:
self.tasks = prepare_next_tasks(
self.checkpoint,
self.checkpoint_pending_writes,
self.nodes,
self.channels,
self.managed,
self.config,
self.step,
self.stop,
for_execution=True,
manager=self.manager,
store=self.store,
checkpointer=self.checkpointer,
trigger_to_nodes=self.trigger_to_nodes,
updated_channels=self.updated_channels,
retry_policy=self.retry_policy,
cache_policy=self.cache_policy,
)
# produce debug output
if self._checkpointer_put_after_previous is not None:
self._emit(
"checkpoints",
map_debug_checkpoint,
{
**self.checkpoint_config,
CONF: {
**self.checkpoint_config[CONF],
CONFIG_KEY_CHECKPOINT_ID: self.checkpoint["id"],
},
},
self.channels,
self.stream_keys,
self.checkpoint_metadata,
self.tasks.values(),
self.checkpoint_pending_writes,
self.prev_checkpoint_config,
self.output_keys,
)
def match_cached_writes(self) -> Sequence[PregelExecutableTask]:
raise NotImplementedError
+21 -8
View File
@@ -23,7 +23,7 @@ from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, LLMResul
from pydantic import BaseModel
from langgraph._internal._config import filter_to_user_tags
from langgraph._internal._constants import NS_SEP
from langgraph._internal._constants import NS_SEP, PUSH
from langgraph.channels.binop import _get_overwrite
from langgraph.channels.delta import DeltaChannel
from langgraph.constants import TAG_HIDDEN, TAG_NOSTREAM
@@ -55,6 +55,23 @@ def _state_values(obj: Any) -> Sequence[Any]:
return ()
def graph_namespace(metadata: Mapping[str, Any]) -> tuple[str, ...]:
"""Namespace of the graph a task runs in, from the task's run metadata.
Drops the task's own segment, plus one per enclosing `@task` call: a call
runs in its caller's graph but nests its segment under the caller's. A
call's `langgraph_path` is `(PUSH, <caller's path>, <index>, True)`, while
a `Send` task's ends in `False`.
"""
ns = tuple(cast(str, metadata["langgraph_checkpoint_ns"]).split(NS_SEP))
path = metadata.get("langgraph_path", ())
drop = 1
while len(path) == 4 and path[0] == PUSH and path[-1] is True:
drop += 1
path = path[1]
return ns[:-drop]
class StreamMessagesHandler(BaseCallbackHandler, _StreamingCallbackHandler):
"""A callback handler that implements stream_mode=messages.
@@ -148,9 +165,7 @@ class StreamMessagesHandler(BaseCallbackHandler, _StreamingCallbackHandler):
**kwargs: Any,
) -> Any:
if metadata and (not tags or (TAG_NOSTREAM not in tags)):
ns = tuple(cast(str, metadata["langgraph_checkpoint_ns"]).split(NS_SEP))[
:-1
]
ns = graph_namespace(metadata)
if not self.subgraphs and len(ns) > 0 and ns != self.parent_ns:
return
if (filtered_tags := filter_to_user_tags(tags)) is not None:
@@ -213,10 +228,8 @@ class StreamMessagesHandler(BaseCallbackHandler, _StreamingCallbackHandler):
and kwargs.get("name") == metadata.get("langgraph_node")
and (not tags or TAG_HIDDEN not in tags)
):
ns = tuple(cast(str, metadata["langgraph_checkpoint_ns"]).split(NS_SEP))[
:-1
]
if not self.subgraphs and len(ns) > 0:
ns = graph_namespace(metadata)
if not self.subgraphs and len(ns) > 0 and ns != self.parent_ns:
return
self.metadata[run_id] = (ns, metadata)
for value in _state_values(inputs):
+2 -11
View File
@@ -227,23 +227,14 @@ class PregelNode:
def node(self) -> Runnable[Any, Any] | None:
"""Get a runnable that combines `bound` and `writers`."""
writers = self.flat_writers
trace_inputs = self.trace_policy.process_inputs if self.trace_policy else None
trace_outputs = self.trace_policy.process_outputs if self.trace_policy else None
if self.bound is DEFAULT_BOUND and not writers:
return None
elif self.bound is DEFAULT_BOUND and len(writers) == 1:
return writers[0]
elif self.bound is DEFAULT_BOUND:
return RunnableSeq(
*writers, trace_inputs=trace_inputs, trace_outputs=trace_outputs
)
return RunnableSeq(*writers, trace_policy=self.trace_policy)
elif writers:
return RunnableSeq(
self.bound,
*writers,
trace_inputs=trace_inputs,
trace_outputs=trace_outputs,
)
return RunnableSeq(self.bound, *writers, trace_policy=self.trace_policy)
else:
return self.bound
+9 -3
View File
@@ -34,6 +34,7 @@ from langgraph._internal._constants import (
CONFIG_KEY_THREAD_ID,
CONFIG_KEY_TIMED_ATTEMPT_OBSERVER,
NS_SEP,
RETURN,
)
from langgraph._internal._runnable import create_task_in_config_context
from langgraph._internal._timeout import sync_timeout_unsupported
@@ -733,15 +734,14 @@ async def arun_with_retry(
if stream:
async for _ in task.proc.astream(task.input, config):
pass
break
return _return_value(task)
return await task.proc.ainvoke(task.input, config)
result = await _arun_with_timeout(
task, config, resolved_timeout, attempt_ctx, stream=stream
)
_finish_timed_attempt(config, attempt_ctx)
if stream:
# if successful, end
break
return _return_value(task)
return result
except ParentCommand as exc:
ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]
@@ -831,6 +831,12 @@ async def arun_with_retry(
config = patch_configurable(config, {CONFIG_KEY_RESUMING: True})
def _return_value(task: PregelExecutableTask) -> Any:
# Streaming drains the task instead of returning its result, so a caller
# awaiting a `@task` needs the value the task wrote.
return next((value for channel, value in task.writes if channel == RETURN), None)
def _should_retry_on(retry_policy: RetryPolicy, exc: Exception) -> bool:
"""Check if the given exception should be retried based on the retry policy."""
if isinstance(retry_policy.retry_on, Sequence):
+5 -5
View File
@@ -43,7 +43,7 @@ from langgraph._internal._typing import MISSING
from langgraph.constants import TAG_HIDDEN
from langgraph.errors import GraphBubbleUp, GraphInterrupt, is_invalid_resume
from langgraph.pregel._algo import Call
from langgraph.pregel._executor import Submit
from langgraph.pregel._executor import SKIP_RERAISE_SET, Submit
from langgraph.pregel._retry import arun_with_retry, run_with_retry
from langgraph.pregel._task_status import CONTROL_WRITES
from langgraph.types import (
@@ -68,10 +68,6 @@ EXCLUDED_FRAME_FNAMES = (
"concurrent/futures/_base.py",
)
SKIP_RERAISE_SET: weakref.WeakSet[concurrent.futures.Future | asyncio.Future] = (
weakref.WeakSet()
)
class FuturesDict(Generic[F, E], dict[F, PregelExecutableTask | None]):
event: E
@@ -239,6 +235,8 @@ class PregelRunner:
fut.set_exception(exc)
futures.done.add(fut)
elif reraise and id(exc) not in self._handled_exception_ids:
# let the caller stream this task's result before it raises
yield
if tb := exc.__traceback__:
while tb.tb_next is not None and any(
tb.tb_frame.f_code.co_filename.endswith(name)
@@ -430,6 +428,8 @@ class PregelRunner:
fut.set_exception(exc)
futures.done.add(fut)
elif reraise and id(exc) not in self._handled_exception_ids:
# let the caller stream this task's result before it raises
yield
if tb := exc.__traceback__:
while tb.tb_next is not None and any(
tb.tb_frame.f_code.co_filename.endswith(name)
+12 -12
View File
@@ -7,8 +7,9 @@ from uuid import UUID
from langchain_core.callbacks import BaseCallbackHandler
from langgraph._internal._constants import NS_SEP
from langgraph.constants import TAG_NOSTREAM
from langgraph.errors import GraphDrained, GraphInterrupt
from langgraph.pregel._messages import graph_namespace
from langgraph.pregel.protocol import StreamChunk
try:
@@ -91,12 +92,9 @@ class StreamToolCallHandler(BaseCallbackHandler, _StreamingCallbackHandler):
) -> tuple[str, ...] | None:
"""Resolve the namespace this tool call should emit at, or `None` to skip.
Mirrors `StreamMessagesHandler.on_chat_model_start`'s namespace
derivation: parses `langgraph_checkpoint_ns` (which ends with
the `node_name:task_id` of the calling node), drops that
trailing segment, and returns the containing subgraph's own
namespace. Returns `None` when the call should be silently
suppressed:
Mirrors `StreamMessagesHandler.on_chat_model_start`: the namespace
is that of the graph the tool runs in (`graph_namespace`). Returns
`None` when the call should be silently suppressed:
- `metadata` is missing — handler is attached to a context
without Pregel routing info.
@@ -109,11 +107,9 @@ class StreamToolCallHandler(BaseCallbackHandler, _StreamingCallbackHandler):
return None
if tags and TAG_NOSTREAM in tags:
return None
nskey = metadata.get("langgraph_checkpoint_ns")
if not nskey:
ns: tuple[str, ...] = ()
else:
ns = tuple(cast(str, nskey).split(NS_SEP))[:-1]
ns = (
graph_namespace(metadata) if metadata.get("langgraph_checkpoint_ns") else ()
)
if not self.subgraphs and len(ns) > 0 and ns != self.parent_ns:
return None
return ns
@@ -188,6 +184,10 @@ class StreamToolCallHandler(BaseCallbackHandler, _StreamingCallbackHandler):
return
ns, tool_call_id, token = info
self._reset_writer(token)
# An interrupted or drained tool is paused, not failed: it runs again
# on resume and reports its start and end then.
if isinstance(error, (GraphInterrupt, GraphDrained)):
return
self.stream(
(
ns,
+53 -50
View File
@@ -97,6 +97,7 @@ from langgraph._internal._runnable import (
Runnable,
RunnableLike,
RunnableSeq,
_is_stream_handler,
coerce_to_runnable,
)
from langgraph._internal._timeout import coerce_timeout_policy
@@ -203,11 +204,6 @@ from langgraph.types import (
from langgraph.typing import ContextT, InputT, OutputT, StateT
from langgraph.warnings import LangGraphDeprecatedSinceV10
try:
from langchain_core.tracers._streaming import _StreamingCallbackHandler
except ImportError:
_StreamingCallbackHandler = None # type: ignore
__all__ = ("NodeBuilder", "Pregel")
_WriteValue = Callable[[Input], Output] | Any
@@ -2880,6 +2876,20 @@ class Pregel(
for h in callback_manager.inheritable_handlers
if not isinstance(h, StreamMessagesHandlerV2)
]
if CONFIG_KEY_TASK_ID not in config[CONF]:
# Not run as a task (a child invoked with its own thread, say),
# this graph's namespace starts at the root, so a caller's
# handler would take this graph's events for its own graph's.
callback_manager.handlers = [
h
for h in callback_manager.handlers
if not _is_stream_mode_handler(h)
]
callback_manager.inheritable_handlers = [
h
for h in callback_manager.inheritable_handlers
if not _is_stream_mode_handler(h)
]
if "ls_integration" not in callback_manager.metadata:
callback_manager.add_metadata({"ls_integration": "langgraph"})
run_manager = callback_manager.on_chain_start(
@@ -2898,9 +2908,9 @@ class Pregel(
)
# set up subgraph checkpointing
config = self._own_checkpoint_config(config)
stream_handlers: list[StreamMessagesHandler | StreamToolCallHandler] = []
# set up messages stream mode
if "messages" in stream_modes:
ns_ = cast(str | None, config[CONF].get(CONFIG_KEY_CHECKPOINT_NS))
use_stream_messages_v2 = bool(
version == "v2" and config[CONF].get(CONFIG_KEY_STREAM_MESSAGES_V2)
)
@@ -2909,24 +2919,12 @@ class Pregel(
if use_stream_messages_v2
else StreamMessagesHandler
)
run_manager.inheritable_handlers.append(
messages_handler_cls(
stream.put,
subgraphs,
parent_ns=tuple(ns_.split(NS_SEP)) if ns_ else None,
)
)
stream_handlers.append(messages_handler_cls(stream.put, subgraphs))
# set up tools stream mode
if "tools" in stream_modes:
ns_tools = cast(str | None, config[CONF].get(CONFIG_KEY_CHECKPOINT_NS))
run_manager.inheritable_handlers.append(
StreamToolCallHandler(
stream.put,
subgraphs,
parent_ns=tuple(ns_tools.split(NS_SEP)) if ns_tools else None,
)
)
stream_handlers.append(StreamToolCallHandler(stream.put, subgraphs))
run_manager.inheritable_handlers.extend(stream_handlers)
# set up custom stream mode
if "custom" in stream_modes:
@@ -3009,6 +3007,10 @@ class Pregel(
cache_policy=self.cache_policy,
has_graph_lifecycle_callbacks=bool(graph_callback_manager.handlers),
) as loop:
# Known only now: the loop numbers the namespace of every
# subgraph a task runs after its first.
for handler in stream_handlers:
handler.parent_ns = loop.checkpoint_ns
emit_graph_lifecycle_events(loop)
# create runner
runner = PregelRunner(
@@ -3291,6 +3293,20 @@ class Pregel(
for h in callback_manager.inheritable_handlers
if not isinstance(h, StreamMessagesHandlerV2)
]
if CONFIG_KEY_TASK_ID not in config[CONF]:
# Not run as a task (a child invoked with its own thread, say),
# this graph's namespace starts at the root, so a caller's
# handler would take this graph's events for its own graph's.
callback_manager.handlers = [
h
for h in callback_manager.handlers
if not _is_stream_mode_handler(h)
]
callback_manager.inheritable_handlers = [
h
for h in callback_manager.inheritable_handlers
if not _is_stream_mode_handler(h)
]
if "ls_integration" not in callback_manager.metadata:
callback_manager.add_metadata({"ls_integration": "langgraph"})
run_manager = await callback_manager.on_chain_start(
@@ -3304,18 +3320,10 @@ class Pregel(
run_id=run_manager.run_id,
)
# if running from astream_log() run each proc with streaming
do_stream = (
next(
(
True
for h in run_manager.handlers
if isinstance(h, _StreamingCallbackHandler)
and not isinstance(h, StreamMessagesHandler)
),
False,
)
if _StreamingCallbackHandler is not None
else False
do_stream = any(
_is_stream_handler(h)
and not isinstance(h, (StreamMessagesHandler, StreamToolCallHandler))
for h in run_manager.handlers
)
if checkpointer is None and durability is not None:
warnings.warn(
@@ -3323,10 +3331,9 @@ class Pregel(
)
# set up subgraph checkpointing
config = self._own_checkpoint_config(config)
stream_handlers: list[StreamMessagesHandler | StreamToolCallHandler] = []
# set up messages stream mode
if "messages" in stream_modes:
# namespace can be None in a root level graph?
ns_ = cast(str | None, config[CONF].get(CONFIG_KEY_CHECKPOINT_NS))
use_stream_messages_v2 = bool(
version == "v2" and config[CONF].get(CONFIG_KEY_STREAM_MESSAGES_V2)
)
@@ -3335,24 +3342,12 @@ class Pregel(
if use_stream_messages_v2
else StreamMessagesHandler
)
run_manager.inheritable_handlers.append(
messages_handler_cls(
stream_put,
subgraphs,
parent_ns=tuple(ns_.split(NS_SEP)) if ns_ else None,
)
)
stream_handlers.append(messages_handler_cls(stream_put, subgraphs))
# set up tools stream mode
if "tools" in stream_modes:
ns_tools = cast(str | None, config[CONF].get(CONFIG_KEY_CHECKPOINT_NS))
run_manager.inheritable_handlers.append(
StreamToolCallHandler(
stream_put,
subgraphs,
parent_ns=tuple(ns_tools.split(NS_SEP)) if ns_tools else None,
)
)
stream_handlers.append(StreamToolCallHandler(stream_put, subgraphs))
run_manager.inheritable_handlers.extend(stream_handlers)
# set up custom stream mode
def stream_writer(c: Any) -> None:
@@ -3461,6 +3456,10 @@ class Pregel(
cache_policy=self.cache_policy,
has_graph_lifecycle_callbacks=bool(graph_callback_manager.handlers),
) as loop:
# Known only now: the loop numbers the namespace of every
# subgraph a task runs after its first.
for handler in stream_handlers:
handler.parent_ns = loop.checkpoint_ns
await aemit_graph_lifecycle_events(loop)
# create runner
runner = PregelRunner(
@@ -4458,6 +4457,10 @@ def _coerce_checkpoint_values(payload: Any, mapper: Callable[[Any], Any]) -> Non
payload["values"] = mapper(payload["values"])
def _is_stream_mode_handler(handler: object) -> bool:
return isinstance(handler, (StreamMessagesHandler, StreamToolCallHandler))
def _resolve_parent_ns(
graph_config: RunnableConfig | None, call_config: RunnableConfig | None
) -> tuple[str, ...]:
+10 -10
View File
@@ -847,6 +847,11 @@ class RemoteGraph(PregelProtocol):
# filter for what was actually requested
if mode not in requested:
continue
# subgraph events may have been requested only for the parent stream.
# messages are exempt: the server dedupes them across namespaces, so
# dropping them here would also drop the root-level message
if not subgraphs and NS_SEP in chunk.event and mode != "messages":
continue
if chunk.event.startswith("messages"):
chunk = chunk._replace(data=tuple(chunk.data))
@@ -861,11 +866,6 @@ class RemoteGraph(PregelProtocol):
)
yield {"type": mode, "ns": ns, "data": chunk.data, "interrupts": ints}
elif subgraphs:
if NS_SEP in chunk.event:
mode, ns_ = chunk.event.split(NS_SEP, 1)
ns = tuple(ns_.split(NS_SEP))
else:
mode, ns = chunk.event, ()
if req_single:
yield ns, chunk.data
else:
@@ -1002,6 +1002,11 @@ class RemoteGraph(PregelProtocol):
# filter for what was actually requested
if mode not in requested:
continue
# subgraph events may have been requested only for the parent stream.
# messages are exempt: the server dedupes them across namespaces, so
# dropping them here would also drop the root-level message
if not subgraphs and NS_SEP in chunk.event and mode != "messages":
continue
if chunk.event.startswith("messages"):
chunk = chunk._replace(data=tuple(chunk.data))
@@ -1016,11 +1021,6 @@ class RemoteGraph(PregelProtocol):
)
yield {"type": mode, "ns": ns, "data": chunk.data, "interrupts": ints}
elif subgraphs:
if NS_SEP in chunk.event:
mode, ns_ = chunk.event.split(NS_SEP, 1)
ns = tuple(ns_.split(NS_SEP))
else:
mode, ns = chunk.event, ()
if req_single:
yield ns, chunk.data
else:
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph"
version = "1.2.16"
version = "1.2.19"
description = "Building stateful, multi-actor applications with LLMs"
authors = []
requires-python = ">=3.10"
@@ -133,6 +133,11 @@
"ToolNode"
],
"name": "tools"
},
"metadata": {
"__tools": [
"search_api"
]
}
},
{
@@ -221,6 +226,11 @@
"ToolNode"
],
"name": "tools"
},
"metadata": {
"__tools": [
"search_api"
]
}
},
{
+21
View File
@@ -20,6 +20,7 @@ from langgraph.errors import EmptyChannelError, InvalidUpdateError
from langgraph.graph import START, StateGraph
from langgraph.graph.message import _messages_delta_reducer
from langgraph.graph.state import _get_channel
from langgraph.pregel._checkpoint import delta_channels_to_snapshot
from langgraph.types import Overwrite
pytestmark = pytest.mark.anyio
@@ -342,6 +343,26 @@ def test_delta_channel_checkpoint_returns_missing() -> None:
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("frequency", [None, 50, 1000])
def test_delta_channel_snapshot_frequency(frequency: int | None) -> None:
spec = (
DeltaChannel(_messages_delta_reducer)
if frequency is None
else DeltaChannel(_messages_delta_reducer, snapshot_frequency=frequency)
)
expected = 100 if frequency is None else frequency
assert spec.snapshot_frequency == expected
channel = spec.from_checkpoint(MISSING)
channel.update([HumanMessage(content="hi", id="h1")])
assert channel.copy().snapshot_frequency == expected
for updates in (expected - 1, expected):
assert delta_channels_to_snapshot(
{"messages": channel},
{"messages": (updates, updates)},
{"messages": 1},
) == ({"messages"} if updates == expected else set())
def test_delta_channel_snapshot_version_based() -> None:
"""Snapshots fire when a channel accumulates `snapshot_frequency` updates.
+33 -1
View File
@@ -1,11 +1,19 @@
from collections.abc import Callable
import pytest
from langchain_core.callbacks import AsyncCallbackManager, BaseCallbackHandler
from langgraph._internal._config import get_async_callback_manager_for_config
from langgraph._internal._config import (
DEFAULT_RECURSION_LIMIT,
get_async_callback_manager_for_config,
)
from langgraph.graph import StateGraph
from langgraph.pregel import Pregel
pytestmark = pytest.mark.anyio
EXPLICIT_DEFAULT_LIMIT = int(DEFAULT_RECURSION_LIMIT)
def test_new_async_manager_includes_tags() -> None:
config = {"callbacks": None}
@@ -114,3 +122,27 @@ async def test_with_config_tags_preserved_on_invoke() -> None:
await graph.ainvoke({}, {"tags": ["invoke"]})
assert "bound" in captured, "bound tag was dropped by ensure_config overwrite"
assert "invoke" in captured, "invoke-time tag not present"
@pytest.mark.parametrize(
"rebind",
[
lambda g: g.with_config(recursion_limit=EXPLICIT_DEFAULT_LIMIT),
lambda g: g.with_config({"recursion_limit": EXPLICIT_DEFAULT_LIMIT}),
lambda g: g.with_config(
{"recursion_limit": 1}, recursion_limit=EXPLICIT_DEFAULT_LIMIT
),
lambda g: g.with_config(recursion_limit=EXPLICIT_DEFAULT_LIMIT).with_config(
tags=["t"]
),
],
ids=["kwarg", "config", "kwarg_over_config", "kept_by_later_with_config"],
)
def test_with_config_applies_recursion_limit_equal_to_default(
rebind: Callable[[Pregel], Pregel],
) -> None:
builder = StateGraph(dict)
builder.add_node("node", lambda state: state)
builder.add_edge("__start__", "node")
graph = builder.compile().with_config(recursion_limit=EXPLICIT_DEFAULT_LIMIT + 1)
assert rebind(graph).config["recursion_limit"] == EXPLICIT_DEFAULT_LIMIT
+114
View File
@@ -4346,6 +4346,25 @@ def test_enum_node_names():
assert graph.invoke({"foo": "hello"}) == {"foo": "hello", "bar": "hello!"}
@pytest.mark.parametrize("stream_mode", ["tasks", "debug"])
def test_failing_task_result_is_streamed_before_the_error(stream_mode: str) -> None:
class State(TypedDict):
x: str
def failing(state: State) -> State:
raise ValueError("boom")
graph = StateGraph(State).add_node("failing", failing).add_edge(START, "failing")
errors = []
with pytest.raises(ValueError, match="boom"):
for chunk in graph.compile().stream({"x": ""}, stream_mode=stream_mode):
payload = chunk["payload"] if stream_mode == "debug" else chunk
if "error" in payload:
errors.append(payload["error"])
assert [type(error) for error in errors] == [ValueError]
def test_debug_retry(sync_checkpointer: BaseCheckpointSaver):
class State(TypedDict):
messages: Annotated[list[str], operator.add]
@@ -4410,6 +4429,52 @@ def test_debug_retry(sync_checkpointer: BaseCheckpointSaver):
assert stream_parent_conf == history_parent_conf
def test_interrupt_after_streams_the_checkpoint_it_pauses_on(
sync_checkpointer: BaseCheckpointSaver,
) -> None:
class State(TypedDict):
x: int
graph = (
StateGraph(State)
.add_node("one", lambda state: {"x": 1})
.add_node("two", lambda state: {"x": 2})
.add_edge(START, "one")
.add_edge("one", "two")
.compile(checkpointer=sync_checkpointer, interrupt_after=["one"])
)
config = {"configurable": {"thread_id": "1"}}
checkpoints = list(graph.stream({"x": 0}, config, stream_mode="checkpoints"))
state = graph.get_state(config)
assert checkpoints[-1]["config"] == state.config
assert checkpoints[-1]["next"] == ["two"]
def test_recursion_limit_streams_the_last_checkpoint_saved(
sync_checkpointer: BaseCheckpointSaver,
) -> None:
class State(TypedDict):
x: int
graph = (
StateGraph(State)
.add_node("loop", lambda state: {"x": state["x"] + 1})
.add_edge(START, "loop")
.add_edge("loop", "loop")
.compile(checkpointer=sync_checkpointer)
)
config = {"configurable": {"thread_id": "1"}, "recursion_limit": 3}
checkpoints = []
with pytest.raises(GraphRecursionError):
for chunk in graph.stream({"x": 0}, config, stream_mode="checkpoints"):
checkpoints.append(chunk)
assert checkpoints[-1]["config"] == graph.get_state(config).config
def test_debug_subgraphs(
sync_checkpointer: BaseCheckpointSaver, durability: Durability
):
@@ -6880,6 +6945,22 @@ def test_named_tasks_functional() -> None:
]
def test_invoke_tags_reach_a_node_three_subgraphs_deep_once() -> None:
class State(TypedDict):
copies: int
def leaf(state: State, config: RunnableConfig) -> State:
return {"copies": config["tags"].count("my-tag")}
graph = StateGraph(State).add_node("leaf", leaf).add_edge(START, "leaf").compile()
for _ in range(3):
graph = (
StateGraph(State).add_node("sub", graph).add_edge(START, "sub").compile()
)
assert graph.invoke({"copies": 0}, {"tags": ["my-tag"]}) == {"copies": 1}
def test_tags_stream_mode_messages() -> None:
model = GenericFakeChatModel(messages=iter(["foo"]), tags=["meow"])
graph = (
@@ -8869,6 +8950,39 @@ def test_subgraph_streaming_sync() -> None:
assert result["num_chunks"] == 9
def test_subgraph_streaming_node_output_messages_sync() -> None:
"""Messages returned by a node (not streamed by an LLM) are emitted when a
graph is explicitly streamed with `stream_mode="messages"` inside a node."""
def respond(state: MessagesState) -> dict:
return {"messages": [AIMessage("hi there", id="ai1")]}
subgraph = (
StateGraph(MessagesState)
.add_node("respond", respond)
.add_edge(START, "respond")
.compile()
)
msgs = {"messages": [("user", "hello")]}
top_level = [msg for msg, _ in subgraph.stream(msgs, stream_mode="messages")]
def parent_node(state: MessagesState, config: RunnableConfig) -> dict:
nonlocal in_node
in_node = [
msg for msg, _ in subgraph.stream(msgs, config, stream_mode="messages")
]
return {}
in_node: list = []
StateGraph(MessagesState).add_node("parent", parent_node).add_edge(
START, "parent"
).compile().invoke(msgs)
assert [m.content for m in top_level] == ["hi there"]
assert [m.content for m in in_node] == ["hi there"]
def test_get_graph_nonterminal_last_step_source(snapshot: SnapshotAssertion) -> None:
class State(TypedDict):
messages: list[str]
+200
View File
@@ -5809,6 +5809,60 @@ async def test_store_injected_async(
) # still overwriting the same one
@pytest.mark.parametrize("stream_mode", ["tasks", "debug"])
async def test_failing_task_result_is_streamed_before_the_error(
stream_mode: str,
) -> None:
class State(TypedDict):
x: str
async def failing(state: State) -> State:
raise ValueError("boom")
graph = StateGraph(State).add_node("failing", failing).add_edge(START, "failing")
errors = []
with pytest.raises(ValueError, match="boom"):
async for chunk in graph.compile().astream({"x": ""}, stream_mode=stream_mode):
payload = chunk["payload"] if stream_mode == "debug" else chunk
if "error" in payload:
errors.append(payload["error"])
assert [type(error) for error in errors] == [ValueError]
@pytest.mark.parametrize(
("parallel", "stream_mode"), [(False, "custom"), (True, "values")]
)
async def test_graph_error_handler_recovers_on_the_executor_path(
parallel: bool, stream_mode: str
) -> None:
class State(TypedDict):
x: str
async def failing(state: State) -> State:
raise ValueError("boom")
async def handler(state: State) -> Command:
return Command(update={"x": "handled"}, goto=END)
builder = (
StateGraph(State)
.add_node("failing", failing, error_handler=handler)
.add_edge(START, "failing")
)
if parallel:
builder.add_node("sibling", lambda state: {}).add_edge(START, "sibling")
chunks = [
chunk
async for chunk in builder.compile().astream(
{"x": ""}, stream_mode=[stream_mode, "values"]
)
]
assert [c[-1] for c in chunks if c[0] == "values"][-1] == {"x": "handled"}
async def test_debug_retry(async_checkpointer: BaseCheckpointSaver):
class State(TypedDict):
messages: Annotated[list[str], operator.add]
@@ -5875,6 +5929,55 @@ async def test_debug_retry(async_checkpointer: BaseCheckpointSaver):
assert stream_parent_conf == history_parent_conf
async def test_interrupt_after_streams_the_checkpoint_it_pauses_on(
async_checkpointer: BaseCheckpointSaver,
) -> None:
class State(TypedDict):
x: int
graph = (
StateGraph(State)
.add_node("one", lambda state: {"x": 1})
.add_node("two", lambda state: {"x": 2})
.add_edge(START, "one")
.add_edge("one", "two")
.compile(checkpointer=async_checkpointer, interrupt_after=["one"])
)
config = {"configurable": {"thread_id": "1"}}
checkpoints = [
chunk
async for chunk in graph.astream({"x": 0}, config, stream_mode="checkpoints")
]
state = await graph.aget_state(config)
assert checkpoints[-1]["config"] == state.config
assert checkpoints[-1]["next"] == ["two"]
async def test_recursion_limit_streams_the_last_checkpoint_saved(
async_checkpointer: BaseCheckpointSaver,
) -> None:
class State(TypedDict):
x: int
graph = (
StateGraph(State)
.add_node("loop", lambda state: {"x": state["x"] + 1})
.add_edge(START, "loop")
.add_edge("loop", "loop")
.compile(checkpointer=async_checkpointer)
)
config = {"configurable": {"thread_id": "1"}, "recursion_limit": 3}
checkpoints = []
with pytest.raises(GraphRecursionError):
async for chunk in graph.astream({"x": 0}, config, stream_mode="checkpoints"):
checkpoints.append(chunk)
assert checkpoints[-1]["config"] == (await graph.aget_state(config)).config
async def test_debug_subgraphs(
async_checkpointer: BaseCheckpointSaver, durability: Durability
):
@@ -6731,6 +6834,31 @@ async def test_multiple_updates() -> None:
]
@NEEDS_CONTEXTVARS
async def test_task_in_subgraph_node_returns_its_value_with_tools_mode() -> None:
class State(TypedDict):
out: int | None
@task
async def double(x: int) -> int:
return x * 2
async def node(state: State) -> State:
return {"out": await double(21)}
inner = StateGraph(State).add_node("node", node).add_edge(START, "node").compile()
graph = (
StateGraph(State).add_node("inner", inner).add_edge(START, "inner").compile()
)
chunks = [
chunk
async for chunk in graph.astream({"out": None}, stream_mode=["values", "tools"])
]
assert chunks[-1] == ("values", {"out": 42})
@NEEDS_CONTEXTVARS
async def test_falsy_return_from_task(async_checkpointer: BaseCheckpointSaver) -> None:
"""Test with a falsy return from a task."""
@@ -7495,6 +7623,40 @@ async def test_entrypoint_from_async_generator() -> None:
yield "b"
@NEEDS_CONTEXTVARS
@pytest.mark.parametrize("timeout", [None, 30])
@pytest.mark.parametrize("version", ["v1", "v2"])
async def test_awaited_task_returns_its_value_under_astream_events(
version: Literal["v1", "v2"], timeout: int | None
) -> None:
class State(TypedDict):
doubled: int
zeroed: int
@task(timeout=timeout)
async def double(x: int) -> int:
return x * 2
@task(timeout=timeout)
async def zero() -> int:
return 0
async def node(state: State) -> State:
return {"doubled": await double(21), "zeroed": await zero()}
graph = StateGraph(State).add_node("node", node).add_edge(START, "node").compile()
outputs = [
event["data"]["output"]
async for event in graph.astream_events(
{"doubled": 0, "zeroed": 1}, version=version
)
if event["event"] == "on_chain_end" and event["name"] == "node"
]
assert outputs == [{"doubled": 42, "zeroed": 0}]
@NEEDS_CONTEXTVARS
async def test_named_tasks_functional() -> None:
class Foo:
@@ -9341,6 +9503,44 @@ async def test_subgraph_streaming_async() -> None:
assert result["num_chunks"] == 9
async def test_subgraph_streaming_node_output_messages_async() -> None:
"""Messages returned by a node (not streamed by an LLM) are emitted when a
graph is explicitly streamed with `stream_mode="messages"` inside a node."""
async def respond(state: MessagesState) -> dict:
return {"messages": [AIMessage("hi there", id="ai1")]}
subgraph = (
StateGraph(MessagesState)
.add_node("respond", respond)
.add_edge(START, "respond")
.compile()
)
msgs = {"messages": [("user", "hello")]}
top_level = [msg async for msg, _ in subgraph.astream(msgs, stream_mode="messages")]
async def parent_node(state: MessagesState, config: RunnableConfig) -> dict:
nonlocal in_node
in_node = [
msg
async for msg, _ in subgraph.astream(msgs, config, stream_mode="messages")
]
return {}
in_node: list = []
await (
StateGraph(MessagesState)
.add_node("parent", parent_node)
.add_edge(START, "parent")
.compile()
.ainvoke(msgs)
)
assert [m.content for m in top_level] == ["hi there"]
assert [m.content for m in in_node] == ["hi there"]
@NEEDS_CONTEXTVARS
async def test_null_resume_disallowed_with_multiple_interrupts(
async_checkpointer: BaseCheckpointSaver,
+127
View File
@@ -16,9 +16,11 @@ from langgraph_sdk.schema import StreamPart
from pydantic import BaseModel
from typing_extensions import TypedDict
from langgraph._internal._constants import CONFIG_KEY_STREAM
from langgraph.errors import GraphInterrupt
from langgraph.graph import END, START, MessagesState, StateGraph, add_messages
from langgraph.pregel import Pregel
from langgraph.pregel.protocol import StreamProtocol
from langgraph.pregel.remote import RemoteGraph
from langgraph.types import Interrupt, StateSnapshot
from tests.any_str import AnyStr
@@ -819,6 +821,131 @@ async def test_astream():
]
_CALLER_CONFIG: RunnableConfig = {
"configurable": {"thread_id": "thread_1", "checkpoint_ns": "outer:123"}
}
_SUBGRAPH_PARTS = [
StreamPart(event="values|sub:abc", data={"chunk": "inner"}),
StreamPart(event="values", data={"chunk": "root"}),
]
_CALLER_PREFIXED = [
(("outer:123", "sub:abc"), {"chunk": "inner"}),
(("outer:123",), {"chunk": "root"}),
]
def test_stream_subgraphs_keeps_caller_namespace():
mock_sync_client = MagicMock()
mock_sync_client.runs.stream.return_value = _SUBGRAPH_PARTS
remote_pregel = RemoteGraph("test_graph_id", sync_client=mock_sync_client)
stream_parts = list(
remote_pregel.stream(
{"input": "data"}, _CALLER_CONFIG, stream_mode="values", subgraphs=True
)
)
assert stream_parts == _CALLER_PREFIXED
async def test_astream_subgraphs_keeps_caller_namespace():
mock_async_client = MagicMock()
async_iter = MagicMock()
async_iter.__aiter__.return_value = _SUBGRAPH_PARTS
mock_async_client.runs.stream.return_value = async_iter
remote_pregel = RemoteGraph("test_graph_id", client=mock_async_client)
stream_parts = [
part
async for part in remote_pregel.astream(
{"input": "data"}, _CALLER_CONFIG, stream_mode="values", subgraphs=True
)
]
assert stream_parts == _CALLER_PREFIXED
# Subgraph events requested only on behalf of a streaming parent graph
_PARENT_STREAMED_PARTS = [
StreamPart(event="updates|sub:abc", data={"inner": {"chunk": "inner"}}),
StreamPart(event="messages|sub:abc", data=[{"content": "token"}, {}]),
StreamPart(event="updates", data={"sub": {"chunk": "root"}}),
]
def _parent_streamed_config(parent_parts: list) -> RunnableConfig:
return {
"configurable": {
"thread_id": "thread_1",
"checkpoint_ns": "outer:123",
CONFIG_KEY_STREAM: StreamProtocol(
parent_parts.append, {"updates", "messages"}
),
}
}
def test_stream_without_subgraphs_skips_parent_requested_subgraph_events():
mock_sync_client = MagicMock()
mock_sync_client.runs.stream.return_value = _PARENT_STREAMED_PARTS
remote_pregel = RemoteGraph("test_graph_id", sync_client=mock_sync_client)
for stream_mode, expected in [
("updates", [{"sub": {"chunk": "root"}}]),
# messages are not filtered: the server dedupes them across namespaces
("messages", [({"content": "token"}, {})]),
]:
parent_parts: list = []
stream_parts = list(
remote_pregel.stream(
{"input": "data"},
_parent_streamed_config(parent_parts),
stream_mode=stream_mode,
)
)
assert stream_parts == expected
# subgraph events are still requested and forwarded to the parent
assert mock_sync_client.runs.stream.call_args.kwargs["stream_subgraphs"]
assert [ns for ns, _, _ in parent_parts] == [
("outer:123", "sub:abc"),
("outer:123", "sub:abc"),
("outer:123",),
]
async def test_astream_without_subgraphs_skips_parent_requested_subgraph_events():
mock_async_client = MagicMock()
async_iter = MagicMock()
async_iter.__aiter__.return_value = _PARENT_STREAMED_PARTS
mock_async_client.runs.stream.return_value = async_iter
remote_pregel = RemoteGraph("test_graph_id", client=mock_async_client)
for stream_mode, expected in [
("updates", [{"sub": {"chunk": "root"}}]),
# messages are not filtered: the server dedupes them across namespaces
("messages", [({"content": "token"}, {})]),
]:
parent_parts: list = []
stream_parts = [
part
async for part in remote_pregel.astream(
{"input": "data"},
_parent_streamed_config(parent_parts),
stream_mode=stream_mode,
)
]
assert stream_parts == expected
# subgraph events are still requested and forwarded to the parent
assert mock_async_client.runs.stream.call_args.kwargs["stream_subgraphs"]
assert [ns for ns, _, _ in parent_parts] == [
("outer:123", "sub:abc"),
("outer:123", "sub:abc"),
("outer:123",),
]
def test_invoke():
# set up test
mock_sync_client = MagicMock()
+41
View File
@@ -2097,6 +2097,47 @@ def test_graph_error_handler_can_route_with_command():
assert result["foo"] == "handled_next"
# Each case runs the failing node on the executor path instead of the
# single-task fast path.
@pytest.mark.parametrize(
("parallel", "stream_mode", "subgraphs"),
[
(False, "custom", False),
(False, "messages", False),
(False, "values", True),
(True, "values", False),
],
)
def test_graph_error_handler_recovers_on_the_executor_path(
parallel: bool, stream_mode: str, subgraphs: bool
) -> None:
class State(TypedDict):
x: str
def failing(state: State) -> State:
raise ValueError("boom")
def handler(state: State) -> Command:
return Command(update={"x": "handled"}, goto=END)
builder = (
StateGraph(State)
.add_node("failing", failing, error_handler=handler)
.add_edge(START, "failing")
)
if parallel:
builder.add_node("sibling", lambda state: {}).add_edge(START, "sibling")
graph = builder.compile()
chunks = list(
graph.stream(
{"x": ""}, stream_mode=[stream_mode, "values"], subgraphs=subgraphs
)
)
assert [c[-1] for c in chunks if c[-2] == "values"][-1] == {"x": "handled"}
def test_graph_error_handler_failure_fails_run():
class State(TypedDict):
foo: str
@@ -0,0 +1,308 @@
import sys
from typing import Any
import pytest
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
from langchain_core.messages import AIMessage
from langchain_core.tools import tool
from langgraph.func import entrypoint, task
from langgraph.graph import START, MessagesState, StateGraph
from langgraph.graph.message import push_message
from langgraph.graph.state import CompiledStateGraph
pytestmark = pytest.mark.anyio
NEEDS_CONTEXTVARS = pytest.mark.skipif(
sys.version_info < (3, 11),
reason="Python 3.11+ is required for async contextvars support",
)
@tool
def echo(text: str) -> str:
"""Echo the text."""
return text
def _one_node_graph(node: Any) -> CompiledStateGraph:
return (
StateGraph(MessagesState)
.add_node(node)
.add_edge(START, node.__name__)
.compile()
)
def _chat_and_echo(state: MessagesState) -> dict:
echo.invoke({"text": "done"})
return {"messages": [GenericFakeChatModel(messages=iter(["one two"])).invoke("hi")]}
async def _achat_and_echo(state: MessagesState) -> dict:
await echo.ainvoke({"text": "done"})
model = GenericFakeChatModel(messages=iter(["one two"]))
return {"messages": [await model.ainvoke("hi")]}
def _segment_names(ns: tuple[str, ...]) -> tuple[str, ...]:
return tuple(segment.split(":")[0] for segment in ns)
def _summary(part: dict) -> tuple[tuple[str, ...], str]:
if part["type"] == "messages":
return _segment_names(part["ns"]), part["data"][0].content
return _segment_names(part["ns"]), part["data"]["event"]
_CHAT_AND_ECHO_EVENTS = [
((), "tool-started"),
((), "tool-finished"),
((), "one"),
((), " "),
((), "two"),
]
_CHILD_CONFIGS = pytest.mark.parametrize(
"child_config",
[
None,
{"configurable": {"thread_id": "child"}},
{"configurable": {"thread_id": "parent"}},
],
ids=["no_config", "own_thread", "caller_thread"],
)
_TASK_CALLERS = pytest.mark.parametrize(
"nested", [False, True], ids=["from_entrypoint", "from_task_in_task"]
)
@_TASK_CALLERS
@pytest.mark.parametrize("subgraphs", [False, True])
def test_task_events_stream_under_the_graph_namespace(
nested: bool, subgraphs: bool
) -> None:
@task
def chat_and_echo() -> None:
_chat_and_echo({"messages": []})
@task
def outer() -> None:
chat_and_echo().result()
@entrypoint()
def workflow(inputs: dict) -> None:
(outer() if nested else chat_and_echo()).result()
parts = workflow.stream(
{}, stream_mode=["messages", "tools"], subgraphs=subgraphs, version="v2"
)
assert [_summary(part) for part in parts] == _CHAT_AND_ECHO_EVENTS
@NEEDS_CONTEXTVARS
@_TASK_CALLERS
@pytest.mark.parametrize("subgraphs", [False, True])
async def test_task_events_stream_under_the_graph_namespace_async(
nested: bool, subgraphs: bool
) -> None:
@task
async def chat_and_echo() -> None:
await _achat_and_echo({"messages": []})
@task
async def outer() -> None:
await chat_and_echo()
@entrypoint()
async def workflow(inputs: dict) -> None:
await (outer() if nested else chat_and_echo())
parts = workflow.astream(
{}, stream_mode=["messages", "tools"], subgraphs=subgraphs, version="v2"
)
assert [_summary(part) async for part in parts] == _CHAT_AND_ECHO_EVENTS
def test_task_events_stream_under_the_namespace_of_their_subgraph() -> None:
@task
def chat_and_echo() -> None:
_chat_and_echo({"messages": []})
def node(state: MessagesState) -> dict:
chat_and_echo().result()
return {}
sub = _one_node_graph(node)
parent = StateGraph(MessagesState).add_node("sub", sub).add_edge(START, "sub")
parts = parent.compile().stream(
{"messages": []},
stream_mode=["messages", "tools"],
subgraphs=True,
version="v2",
)
assert [_summary(part) for part in parts] == [
(("sub",), event) for _, event in _CHAT_AND_ECHO_EVENTS
]
@_CHILD_CONFIGS
def test_child_graph_streams_only_to_the_node_streaming_it(
child_config: dict | None,
) -> None:
child = _one_node_graph(_chat_and_echo)
received = []
def node(state: MessagesState) -> dict:
received.extend(
child.stream(
{"messages": []}, child_config, stream_mode=["messages", "tools"]
)
)
return {}
parts = _one_node_graph(node).stream(
{"messages": []},
{"configurable": {"thread_id": "parent"}},
stream_mode=["messages", "tools"],
)
assert list(parts) == []
assert len(received) == len(_CHAT_AND_ECHO_EVENTS)
@NEEDS_CONTEXTVARS
@_CHILD_CONFIGS
async def test_child_graph_streams_only_to_the_node_streaming_it_async(
child_config: dict | None,
) -> None:
child = _one_node_graph(_achat_and_echo)
received = []
async def node(state: MessagesState) -> dict:
stream = child.astream(
{"messages": []}, child_config, stream_mode=["messages", "tools"]
)
received.extend([part async for part in stream])
return {}
parts = _one_node_graph(node).astream(
{"messages": []},
{"configurable": {"thread_id": "parent"}},
stream_mode=["messages", "tools"],
)
assert [part async for part in parts] == []
assert len(received) == len(_CHAT_AND_ECHO_EVENTS)
@pytest.mark.parametrize(
"child_config",
[
{"configurable": {"thread_id": "child"}},
{"configurable": {"thread_id": "parent"}},
],
ids=["own_thread", "caller_thread"],
)
def test_child_graph_on_its_own_thread_skips_a_caller_streaming_subgraphs(
child_config: dict,
) -> None:
child = _one_node_graph(_chat_and_echo)
def node(state: MessagesState) -> dict:
child.invoke({"messages": []}, child_config)
return {}
parts = _one_node_graph(node).stream(
{"messages": []},
{"configurable": {"thread_id": "parent"}},
stream_mode=["messages", "tools"],
subgraphs=True,
)
assert list(parts) == []
def test_node_gets_the_events_of_every_subgraph_it_streams() -> None:
sub = _one_node_graph(_chat_and_echo)
counts = []
def node(state: MessagesState) -> dict:
for _ in range(2):
stream = sub.stream({"messages": []}, stream_mode=["messages", "tools"])
counts.append(len(list(stream)))
return {}
_one_node_graph(node).invoke({"messages": []})
assert counts == [len(_CHAT_AND_ECHO_EVENTS)] * 2
@NEEDS_CONTEXTVARS
async def test_node_gets_the_events_of_every_subgraph_it_streams_async() -> None:
sub = _one_node_graph(_achat_and_echo)
counts = []
async def node(state: MessagesState) -> dict:
for _ in range(2):
stream = sub.astream({"messages": []}, stream_mode=["messages", "tools"])
counts.append(len([part async for part in stream]))
return {}
await _one_node_graph(node).ainvoke({"messages": []})
assert counts == [len(_CHAT_AND_ECHO_EVENTS)] * 2
def _push(state: MessagesState) -> dict:
push_message(AIMessage(id="pushed", content="hi"), state_key=None)
return {}
def _namespaces(graph: CompiledStateGraph, subgraphs: bool) -> list[tuple[str, ...]]:
parts = graph.stream(
{"messages": []}, stream_mode="messages", subgraphs=subgraphs, version="v2"
)
return [_segment_names(part["ns"]) for part in parts]
def _streams_pushing_graph(received: list) -> Any:
pushing = _one_node_graph(_push)
def node(state: MessagesState) -> dict:
for message, _ in pushing.stream({"messages": []}, stream_mode="messages"):
received.append(message.id)
return {}
return node
def test_pushed_message_streams_under_the_namespace_of_its_graph() -> None:
pushing = _one_node_graph(_push)
parent = StateGraph(MessagesState).add_node("sub", pushing).add_edge(START, "sub")
assert _namespaces(pushing, subgraphs=True) == [()]
assert _namespaces(parent.compile(), subgraphs=False) == []
assert _namespaces(parent.compile(), subgraphs=True) == [("sub",)]
def test_pushed_message_streams_to_the_node_that_streams_its_graph() -> None:
received: list[str] = []
graph = _one_node_graph(_streams_pushing_graph(received))
assert _namespaces(graph, subgraphs=False) == []
assert received == ["pushed"]
def test_pushed_message_streams_to_every_stream_that_takes_it() -> None:
received: list[str] = []
graph = _one_node_graph(_streams_pushing_graph(received))
assert _namespaces(graph, subgraphs=True) == [("node",)]
assert received == ["pushed"]
@@ -13,13 +13,17 @@ from typing import Annotated, Any
import pytest
from langchain_core.messages import AIMessage
from langchain_core.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.prebuilt import ToolNode, ToolRuntime
from typing_extensions import TypedDict
from langgraph.constants import END, START
from langgraph.errors import GraphDrained
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.pregel._tools import _tool_call_writer
from langgraph.runtime import RunControl
from langgraph.types import Command, interrupt
class _State(TypedDict):
@@ -54,14 +58,14 @@ def _caller_async(tool_name: str, tool_args: dict[str, Any], tc_id: str = "tc1")
return caller
def _build_graph(caller, tools) -> Any:
def _build_graph(caller, tools, checkpointer=None) -> Any:
sg = StateGraph(_State)
sg.add_node("caller", caller)
sg.add_node("tools", ToolNode(tools))
sg.add_edge(START, "caller")
sg.add_edge("caller", "tools")
sg.add_edge("tools", END)
return sg.compile()
return sg.compile(checkpointer=checkpointer)
def _tool_events(stream) -> list[tuple[tuple[str, ...], dict]]:
@@ -146,6 +150,105 @@ class TestSyncGraphSyncTool:
assert kinds == ["tool-started", "tool-error"]
assert events[1][1]["message"] == "nope"
def test_interrupt_in_tool_is_not_a_tool_error(self) -> None:
@tool
def ask() -> str:
"""asks."""
return interrupt("ok?")
graph = _build_graph(_caller_sync("ask", {}), [ask], InMemorySaver())
config = {"configurable": {"thread_id": "1"}}
paused = _tool_events(
graph.stream(
{"messages": []}, config, stream_mode=["tools"], subgraphs=True
)
)
resumed = _tool_events(
graph.stream(
Command(resume="yes"), config, stream_mode=["tools"], subgraphs=True
)
)
assert [p["event"] for _, p in paused] == ["tool-started"]
assert [(p["event"], p["tool_call_id"]) for _, p in resumed] == [
("tool-started", "tc1"),
("tool-finished", "tc1"),
]
def test_tool_handing_off_to_the_parent_still_ends_its_call(self) -> None:
class State(TypedDict):
messages: Annotated[list, add_messages]
done: bool
def hand_off(state: State) -> Command:
return Command(graph=Command.PARENT, goto="finish", update={"done": True})
inner = StateGraph(State).add_node("hand_off", hand_off)
inner = inner.add_edge(START, "hand_off").compile()
@tool
def handoff() -> str:
"""hands off."""
inner.invoke({"messages": [], "done": False})
return "unreachable"
graph = (
StateGraph(State)
.add_node("caller", _caller_sync("handoff", {}))
.add_node("tools", ToolNode([handoff]))
.add_node("finish", lambda state: {"done": True})
.add_edge(START, "caller")
.add_edge("caller", "tools")
.add_edge("tools", END)
.add_edge("finish", END)
.compile()
)
events = [
payload["event"]
for _, payload in _tool_events(
graph.stream(
{"messages": [], "done": False},
stream_mode=["tools"],
subgraphs=True,
)
)
]
assert events[0] == "tool-started"
assert events[-1] == "tool-error", events
def test_drained_tool_is_not_a_tool_error(self) -> None:
control = RunControl()
def request_drain(state: _State) -> dict:
control.request_drain()
return {}
inner = (
StateGraph(_State)
.add_node("request_drain", request_drain)
.add_node("after", lambda state: {})
.add_edge(START, "request_drain")
.add_edge("request_drain", "after")
.compile()
)
@tool
def drained() -> str:
"""drains."""
inner.invoke({"messages": []}, control=control)
return "unreachable"
graph = _build_graph(_caller_sync("drained", {}), [drained])
chunks: list = []
with pytest.raises(GraphDrained):
for chunk in graph.stream(
{"messages": []}, stream_mode=["tools"], subgraphs=True
):
chunks.append(chunk)
assert [p["event"] for _, p in _tool_events(chunks)] == ["tool-started"]
def test_writer_unset_outside_tool(self) -> None:
# Outside any tool body the ContextVar that ToolRuntime reads
# is unset — emitting from there would be a no-op.
@@ -196,6 +299,25 @@ class TestAsyncGraphAsyncTool:
assert kinds == ["tool-started", "tool-output-delta", "tool-finished"]
assert events[1][1]["delta"] == "hi"
@pytest.mark.anyio
async def test_interrupt_in_async_tool_is_not_a_tool_error(self) -> None:
@tool
async def aask() -> str:
"""asks."""
return interrupt("ok?")
graph = _build_graph(_caller_async("aask", {}), [aask], InMemorySaver())
config = {"configurable": {"thread_id": "1"}}
events = []
for graph_input in ({"messages": []}, Command(resume="yes")):
async for ns, mode, payload in graph.astream(
graph_input, config, stream_mode=["tools"], subgraphs=True
):
if mode == "tools":
events.append(payload["event"])
assert events == ["tool-started", "tool-started", "tool-finished"]
class TestConcurrentToolCalls:
def test_parallel_tool_calls_do_not_bleed(self) -> None:
+190 -1
View File
@@ -1,11 +1,24 @@
"""End-to-end tests for `TracePolicy` input processing on node trace runs."""
import asyncio
from collections.abc import AsyncIterator, Iterator
from typing import Any
from uuid import UUID
import pytest
from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.language_models.fake_chat_models import FakeMessagesListChatModel
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.runnables import RunnableConfig
from typing_extensions import TypedDict
from langgraph.graph import END, START, StateGraph
from langgraph._internal._runnable import (
ASYNCIO_ACCEPTS_CONTEXT,
RunnableCallable,
RunnableSeq,
)
from langgraph.graph import END, START, MessagesState, StateGraph
from langgraph.graph.state import CompiledStateGraph
from langgraph.types import TracePolicy
from tests.fake_tracer import FakeTracer, Run
@@ -147,3 +160,179 @@ async def test_trace_policy_transforms_recorded_inputs_async() -> None:
run = _node_run(tracer, "n")
assert run.inputs == {"scrubbed_in": True}
assert run.outputs == {"value": 6}
_RECORDING_POLICY = TracePolicy(
process_inputs=lambda _: {"recorded": "inputs"},
process_outputs=lambda _: {"recorded": "outputs"},
)
def _chat_graph() -> CompiledStateGraph:
model = FakeMessagesListChatModel(
responses=[AIMessage("model reply", id="ai-model")]
)
def chat(state: MessagesState) -> dict[str, Any]:
reply = model.invoke(state["messages"])
return {
"messages": [
*state["messages"],
reply,
AIMessage("from node", id="ai-node"),
]
}
return (
StateGraph(MessagesState)
.add_node("chat", chat, trace_policy=_RECORDING_POLICY)
.add_edge(START, "chat")
.compile()
)
def _assert_scrubbed_node_run(tracer: FakeTracer, *, model_is_child: bool) -> None:
run = _node_run(tracer, "chat")
assert run.inputs == {"recorded": "inputs"}
assert run.outputs == {"recorded": "outputs"}
if model_is_child:
assert [c.name for c in run.child_runs] == ["FakeMessagesListChatModel"]
def test_trace_policy_scrubs_tracer_not_messages_stream() -> None:
tracer = FakeTracer()
streamed = [
message.content
for message, _ in _chat_graph().stream(
{"messages": [HumanMessage("hello", id="h-1")]},
{"callbacks": [tracer]},
stream_mode="messages",
)
]
assert streamed == ["model reply", "from node"]
_assert_scrubbed_node_run(tracer, model_is_child=True)
@pytest.mark.anyio
async def test_trace_policy_scrubs_tracer_not_messages_stream_async() -> None:
tracer = FakeTracer()
streamed = [
message.content
async for message, _ in _chat_graph().astream(
{"messages": [HumanMessage("hello", id="h-1")]},
{"callbacks": [tracer]},
stream_mode="messages",
)
]
assert streamed == ["model reply", "from node"]
_assert_scrubbed_node_run(tracer, model_is_child=ASYNCIO_ACCEPTS_CONTEXT)
@pytest.mark.anyio
async def test_trace_policy_scrubs_tracer_not_astream_events() -> None:
tracer = FakeTracer()
chat_events = [
event
async for event in _chat_graph().astream_events(
{"messages": [HumanMessage("hello", id="h-1")]},
{"callbacks": [tracer]},
version="v2",
)
if event["name"] == "chat"
and event["event"] in ("on_chain_start", "on_chain_end")
]
assert [e["event"] for e in chat_events] == ["on_chain_start", "on_chain_end"]
start, end = (e["data"] for e in chat_events)
assert [m.content for m in start["input"]["messages"]] == ["hello"]
assert [m.content for m in end["output"]["messages"]] == [
"hello",
"model reply",
"from node",
]
_assert_scrubbed_node_run(tracer, model_is_child=ASYNCIO_ACCEPTS_CONTEXT)
class _HandlerWithTapMethods(BaseCallbackHandler):
def __init__(self) -> None:
self.node_payloads: list[Any] = []
self.node_run_ids: set[UUID] = set()
def _record(self, run_id: UUID, payload: Any) -> None:
if run_id in self.node_run_ids:
self.node_payloads.append(payload)
def tap_output_iter(self, run_id: UUID, output: Iterator[Any]) -> Iterator[Any]:
for chunk in output:
self._record(run_id, chunk)
yield chunk
async def tap_output_aiter(
self, run_id: UUID, output: AsyncIterator[Any]
) -> AsyncIterator[Any]:
async for chunk in output:
self._record(run_id, chunk)
yield chunk
def on_chain_start(
self, serialized: Any, inputs: Any, *, run_id: UUID, **kwargs: Any
) -> None:
if kwargs.get("name") == "n":
self.node_run_ids.add(run_id)
self._record(run_id, inputs)
def on_chain_end(self, outputs: Any, *, run_id: UUID, **kwargs: Any) -> None:
self._record(run_id, outputs)
@pytest.mark.anyio
@pytest.mark.parametrize("via_events", [False, True], ids=["ainvoke", "astream_events"])
async def test_trace_policy_reaches_tracers_but_not_custom_handlers(
via_events: bool,
) -> None:
graph = (
StateGraph(State)
.add_node("n", _incr, trace_policy=_RECORDING_POLICY)
.add_edge(START, "n")
.add_edge("n", END)
.compile()
)
handler = _HandlerWithTapMethods()
tracer = FakeTracer()
config: RunnableConfig = {"callbacks": [handler, tracer]}
if via_events:
async for _ in graph.astream_events({"value": 1}, config, version="v2"):
pass
else:
assert await graph.ainvoke({"value": 1}, config) == {"value": 2}
assert handler.node_payloads == [{"value": 1}, {"value": 2}]
run = _node_run(tracer, "n")
assert (run.inputs, run.outputs) == (
{"recorded": "inputs"},
{"recorded": "outputs"},
)
@pytest.mark.anyio
async def test_trace_policy_cancel_at_node_end_still_closes_tracer_run() -> None:
def cancel_own_task(value: Any) -> Any:
asyncio.current_task().cancel()
return value
tracer = FakeTracer()
seq = RunnableSeq(
RunnableCallable(lambda x: x, trace=False),
RunnableCallable(cancel_own_task, trace=False),
trace_policy=_RECORDING_POLICY,
)
with pytest.raises(asyncio.CancelledError):
await asyncio.create_task(seq.ainvoke({}, {"callbacks": [tracer]}))
for _ in range(100):
if tracer.runs:
break
await asyncio.sleep(0.01)
assert [r.outputs for r in tracer.runs] == [{"recorded": "outputs"}]
+36
View File
@@ -1,4 +1,6 @@
import copy
import functools
import pickle
import sys
import uuid
from collections.abc import Callable
@@ -25,6 +27,7 @@ from pydantic import BaseModel, Field
from typing_extensions import NotRequired, Required, TypedDict
from langgraph._internal._config import (
DEFAULT_RECURSION_LIMIT,
_is_not_empty,
_merge_callbacks,
ensure_config,
@@ -620,6 +623,30 @@ def test_merge_configs_merges_metadata_lc_versions() -> None:
assert merged["metadata"]["lc_agent_name"] == "agent"
@pytest.mark.parametrize(
"rebuild",
[
lambda config: config,
copy.deepcopy,
lambda config: pickle.loads(pickle.dumps(config)),
],
ids=["as_is", "deepcopy", "pickle"],
)
def test_merge_configs_skips_the_injected_default_recursion_limit(
rebuild: Callable[[RunnableConfig], RunnableConfig],
) -> None:
merged = merge_configs({"recursion_limit": 50}, rebuild(ensure_config()))
assert merged["recursion_limit"] == 50
def test_merge_configs_applies_an_explicit_recursion_limit_equal_to_default() -> None:
explicit_default = int(DEFAULT_RECURSION_LIMIT)
merged = merge_configs(
{"recursion_limit": 50}, {"recursion_limit": explicit_default}
)
assert merged["recursion_limit"] == explicit_default
def test_ensure_config_merges_metadata_lc_versions() -> None:
a = {
"metadata": {
@@ -772,6 +799,15 @@ def test_ensure_config_tags_concat_preserves_order_and_duplicates() -> None:
assert merged["tags"] == ["shared", "alpha", "shared", "beta"]
def test_ensure_config_skips_tags_inherited_from_ambient() -> None:
token = var_child_runnable_config.set({"tags": ["inherited"]})
try:
merged = ensure_config({"tags": ["inherited", "own"]})
finally:
var_child_runnable_config.reset(token)
assert merged["tags"] == ["inherited", "own"]
def test_ensure_config_merges_callbacks_across_configs() -> None:
a_cb = _TrackingCB("a")
b_cb = _TrackingCB("b")
+90 -82
View File
@@ -1,5 +1,5 @@
version = 1
revision = 3
revision = 5
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.15'",
@@ -931,18 +931,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" },
]
[[package]]
name = "importlib-metadata"
version = "8.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" },
]
[[package]]
name = "iniconfig"
version = "2.3.0"
@@ -1451,7 +1439,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.16"
version = "1.2.19"
source = { editable = "." }
dependencies = [
{ name = "langchain-core" },
@@ -1637,7 +1625,7 @@ wheels = [
[[package]]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -1734,7 +1722,7 @@ test = [
[[package]]
name = "langgraph-checkpoint-sqlite"
version = "3.1.2"
version = "3.1.3"
source = { editable = "../checkpoint-sqlite" }
dependencies = [
{ name = "aiosqlite" },
@@ -2111,7 +2099,7 @@ wheels = [
[[package]]
name = "nbconvert"
version = "7.17.1"
version = "7.17.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "beautifulsoup4" },
@@ -2129,9 +2117,9 @@ dependencies = [
{ name = "pygments" },
{ name = "traitlets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/01/b1/708e53fe2e429c103c6e6e159106bcf0357ac41aa4c28772bd8402339051/nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2", size = 865311, upload-time = "2026-04-08T00:44:14.914Z" }
sdist = { url = "https://files.pythonhosted.org/packages/53/6a/252c7620305a9661205aed7a2a4b3592f5207bfca56ad11b7dea2c5ec2f6/nbconvert-7.17.2.tar.gz", hash = "sha256:344a218e8595117ce37396ed025d994f146d076226ebac59255a2fa085ab24d8", size = 865989, upload-time = "2026-10-12T09:11:19.583Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/67/f8/bb0a9d5f46819c821dc1f004aa2cc29b1d91453297dbf5ff20470f00f193/nbconvert-7.17.1-py3-none-any.whl", hash = "sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8", size = 261927, upload-time = "2026-04-08T00:44:12.845Z" },
{ url = "https://files.pythonhosted.org/packages/81/fd/a8d457af68848a8e5c12dd1d84ff35c415db471af78280e0703db4efbb12/nbconvert-7.17.2-py3-none-any.whl", hash = "sha256:6a143c11c00bf831604da4b1be4347d36ea52367057eb920135d69bc2862fed2", size = 262094, upload-time = "2026-10-12T09:11:17.215Z" },
]
[[package]]
@@ -2189,84 +2177,114 @@ wheels = [
[[package]]
name = "opentelemetry-api"
version = "1.39.1"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" }
sdist = { url = "https://files.pythonhosted.org/packages/2e/02/6e0ae9cc61bd3169d401077b507b3ebc344745171e1051ab430be012dcd9/opentelemetry_api-1.45.1.tar.gz", hash = "sha256:aa38ed19bcc084ba42782a73255b3582283eced7ad6dddbd6695189e69adfb75", size = 72804, upload-time = "2026-10-06T17:32:58.133Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" },
{ url = "https://files.pythonhosted.org/packages/1e/41/f7dcf80b81ee8e71c1a2b59f14208bc723edbd89ed027a73b175abf6348e/opentelemetry_api-1.45.1-py3-none-any.whl", hash = "sha256:b31553efa588ae44bc306f863c785c5333a9ecc091248c6ee68b4b6c87fdedfb", size = 60256, upload-time = "2026-10-06T17:32:33.506Z" },
]
[[package]]
name = "opentelemetry-exporter-http-transport"
version = "0.66b1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
]
sdist = { url = "https://files.pythonhosted.org/packages/62/0c/e3ebdb4b507f66afcc905e6885a4946969bd75b45988492643356fbbdc63/opentelemetry_exporter_http_transport-0.66b1.tar.gz", hash = "sha256:443080203bf52586ce0b2ad901e8951c61833eab1aa539ae6f1f16fe9e8e7952", size = 11693, upload-time = "2026-10-06T17:32:59.65Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/69/6af86ff66492b481c6a4c05dcfd68beb47ed8ba046440a26a2aac76b95c7/opentelemetry_exporter_http_transport-0.66b1-py3-none-any.whl", hash = "sha256:2f95404bdee7f9d2d529c7de56c7bd86d014d774d8fbf137810e0167f8a492bf", size = 12155, upload-time = "2026-10-06T17:32:35.454Z" },
]
[package.optional-dependencies]
requests = [
{ name = "requests" },
]
[[package]]
name = "opentelemetry-exporter-otlp-common"
version = "0.66b1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-sdk" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cb/19/41de712173f43057e4532d42ece7d0c6d4210d353e5752433cb14987643f/opentelemetry_exporter_otlp_common-0.66b1.tar.gz", hash = "sha256:6b1403487a2185ac1feb45fd5546fdf8630ce71c36bcefaadf51e2130e9e23f9", size = 14325, upload-time = "2026-10-06T17:33:01.725Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fc/39/8c23d67665c762aa51840fa06f86e902e8f6f1693bc8d7e3d98cd6e2f753/opentelemetry_exporter_otlp_common-0.66b1-py3-none-any.whl", hash = "sha256:00ff8592c3a7cb729ff3fdc7ffa12372c243bdf2163e80c180994d0c7bd83ee9", size = 12385, upload-time = "2026-10-06T17:32:38.177Z" },
]
[[package]]
name = "opentelemetry-exporter-otlp-proto-common"
version = "1.39.1"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-proto" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409, upload-time = "2025-12-11T13:32:40.885Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c1/8e/65e85e5137991a3c493b11682151d198638a5bc1dd4b4c5f67e013c57d7c/opentelemetry_exporter_otlp_proto_common-1.45.1.tar.gz", hash = "sha256:2e4adcc3a67bcf57804fc49514f0ef64974ca7590aa3491da389852b4a0628f6", size = 18873, upload-time = "2026-10-06T17:33:04.471Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366, upload-time = "2025-12-11T13:32:20.2Z" },
{ url = "https://files.pythonhosted.org/packages/84/aa/92f225d353904e7f70b8b3e3c1b02db0cf56f744c2e83c581dc372e78873/opentelemetry_exporter_otlp_proto_common-1.45.1-py3-none-any.whl", hash = "sha256:2f446183ae7047b036226f1d846c41a834b0e8755ad13b51a51dd38952eb466c", size = 15393, upload-time = "2026-10-06T17:32:41.911Z" },
]
[[package]]
name = "opentelemetry-exporter-otlp-proto-http"
version = "1.39.1"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "googleapis-common-protos" },
{ name = "opentelemetry-api" },
{ name = "opentelemetry-exporter-http-transport", extra = ["requests"] },
{ name = "opentelemetry-exporter-otlp-common" },
{ name = "opentelemetry-exporter-otlp-proto-common" },
{ name = "opentelemetry-proto" },
{ name = "opentelemetry-sdk" },
{ name = "requests" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/80/04/2a08fa9c0214ae38880df01e8bfae12b067ec0793446578575e5080d6545/opentelemetry_exporter_otlp_proto_http-1.39.1.tar.gz", hash = "sha256:31bdab9745c709ce90a49a0624c2bd445d31a28ba34275951a6a362d16a0b9cb", size = 17288, upload-time = "2025-12-11T13:32:42.029Z" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/17/26487707ea4caa97b17e6e4b5fa72133a53512ffa2f5cf7a49ef284b29cb/opentelemetry_exporter_otlp_proto_http-1.45.1.tar.gz", hash = "sha256:45c218405ce3fd879596924b1874bf9a8f6880206d61065c5a912c8e5c297fb7", size = 28839, upload-time = "2026-10-06T17:33:05.713Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/95/f1/b27d3e2e003cd9a3592c43d099d2ed8d0a947c15281bf8463a256db0b46c/opentelemetry_exporter_otlp_proto_http-1.39.1-py3-none-any.whl", hash = "sha256:d9f5207183dd752a412c4cd564ca8875ececba13be6e9c6c370ffb752fd59985", size = 19641, upload-time = "2025-12-11T13:32:22.248Z" },
{ url = "https://files.pythonhosted.org/packages/aa/1f/517eaa0187ba106a9da97160ce2add3a371812681dc440930b267f714e42/opentelemetry_exporter_otlp_proto_http-1.45.1-py3-none-any.whl", hash = "sha256:24a97cf3753c7fb52fad44a696e452ff371686339e2acf3309e2eda3d0230700", size = 22180, upload-time = "2026-10-06T17:32:43.946Z" },
]
[[package]]
name = "opentelemetry-proto"
version = "1.39.1"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "protobuf" },
]
sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" }
sdist = { url = "https://files.pythonhosted.org/packages/4b/7f/15f014fb195da6c2dbb6c71399b8e76824878718e94de6454038488eed28/opentelemetry_proto-1.45.1.tar.gz", hash = "sha256:79e0fb95e4616691a469439238aa9224d75779b3e108e895d1aa125ab29ca77c", size = 46488, upload-time = "2026-10-06T17:33:11.49Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" },
{ url = "https://files.pythonhosted.org/packages/ab/9a/42ec8180a769516ae757e893b69736826efceac7332553915b4528a91c6d/opentelemetry_proto-1.45.1-py3-none-any.whl", hash = "sha256:f38e2a8413053c180cd3d2637fbb279673ec2f6a6e09c995aafa2f452c52b46e", size = 72488, upload-time = "2026-10-06T17:32:53.057Z" },
]
[[package]]
name = "opentelemetry-sdk"
version = "1.39.1"
version = "1.45.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "opentelemetry-semantic-conventions" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/79/7392e21a1c8f0c61d90b223e31c7e48cb9d452e91a6b820ad24cca5f23c4/opentelemetry_sdk-1.45.1.tar.gz", hash = "sha256:63d24a6ca645019a631e6a51999c73e93adcac1196ca640b8ae78a7cc4762bf3", size = 218324, upload-time = "2026-10-06T17:33:13.26Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" },
{ url = "https://files.pythonhosted.org/packages/95/3c/87c42b4bd6dd297536f04cd9383d212ac557ecd49f2cbdcd46da1c9ef5c8/opentelemetry_sdk-1.45.1-py3-none-any.whl", hash = "sha256:c604c11dc429810812348989115fa44bd558772a3d7442afc43d024f2c250ca4", size = 140063, upload-time = "2026-10-06T17:32:55.04Z" },
]
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.60b1"
version = "0.66b1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" }
sdist = { url = "https://files.pythonhosted.org/packages/46/e4/dbbfb2a010c4db2224a5114638acede6fe563d33cc20fb1752cebcbe6298/opentelemetry_semantic_conventions-0.66b1.tar.gz", hash = "sha256:497ca63bf383723411e8eaf60c8779e9877633c936bb641080adab59d0eb6ec8", size = 150250, upload-time = "2026-10-06T17:33:14.073Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" },
{ url = "https://files.pythonhosted.org/packages/bc/14/67f8aa798857f8cf686f515bf93d9bb877ce952ddc8efae0fa25b45ce0d6/opentelemetry_semantic_conventions-0.66b1-py3-none-any.whl", hash = "sha256:d4cddeb4315490b35213f55e2bdc9ac54bb1e4d318927475bed62b35545e581b", size = 206279, upload-time = "2026-10-06T17:32:56.103Z" },
]
[[package]]
@@ -2681,37 +2699,36 @@ wheels = [
[[package]]
name = "pycryptodome"
version = "3.23.0"
version = "4.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" }
sdist = { url = "https://files.pythonhosted.org/packages/33/c7/f9374c8939560ab22db1dd9dbe215b62d20f32634aeb7f259dc03c807b44/pycryptodome-4.2.0.tar.gz", hash = "sha256:21d7fdba239d6ef863d3d08388801c683453a3bb4897c2b27f27847aad89bdf7", size = 1750332, upload-time = "2026-10-24T10:24:42.016Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" },
{ url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" },
{ url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" },
{ url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" },
{ url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" },
{ url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" },
{ url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" },
{ url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" },
{ url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" },
{ url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" },
{ url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" },
{ url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" },
{ url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" },
{ url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" },
{ url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" },
{ url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" },
{ url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" },
{ url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" },
{ url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" },
{ url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" },
{ url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" },
{ url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" },
{ url = "https://files.pythonhosted.org/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886, upload-time = "2025-05-17T17:21:20.614Z" },
{ url = "https://files.pythonhosted.org/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151, upload-time = "2025-05-17T17:21:22.666Z" },
{ url = "https://files.pythonhosted.org/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461, upload-time = "2025-05-17T17:21:25.225Z" },
{ url = "https://files.pythonhosted.org/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440, upload-time = "2025-05-17T17:21:27.991Z" },
{ url = "https://files.pythonhosted.org/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005, upload-time = "2025-05-17T17:21:31.37Z" },
{ url = "https://files.pythonhosted.org/packages/fb/05/61028b65913438a091ed3a2ec55bb50843e674b6ae14bccf196bbed652a2/pycryptodome-4.2.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:4b58c4fd5615a788e0df6793d578e565b19d75a348c0ba02d469b0c38bbf0332", size = 1684620, upload-time = "2026-10-24T10:23:50.473Z" },
{ url = "https://files.pythonhosted.org/packages/cd/73/0930fd56dd5eb856ecc33ec48845def52a1d25a9cd6b810a4c3ef3633cf4/pycryptodome-4.2.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:42ab554efcc14928f1db42deac9c84989fd49e289a16aacbbee709fe2eb838cc", size = 1520423, upload-time = "2026-10-24T10:23:52.505Z" },
{ url = "https://files.pythonhosted.org/packages/19/ba/4b931de42881053a6d818b9ee70f6dde3dfe1cfcf78527d45db3f99d8b47/pycryptodome-4.2.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b4ed1a34d2d667ec165beb15313a759bcd8e704e2743f9942491c5a88a2d6ca5", size = 2296340, upload-time = "2026-10-24T10:23:54.294Z" },
{ url = "https://files.pythonhosted.org/packages/d4/63/68fc2a01d621c2ff389ea261e731e8f1abe6aba89992681bbcdb19fcca43/pycryptodome-4.2.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1482a7d9ceed7beadbb0853f83d4d44406a9f9fa8ee596d637618f095869e14", size = 2987589, upload-time = "2026-10-24T10:23:56.547Z" },
{ url = "https://files.pythonhosted.org/packages/f5/63/69e211459f4625c89bb47a7ba4527a6cc3276c9bbd79ae871a2d0b05c021/pycryptodome-4.2.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:dceadb6f478b0a687d129f1b58220e0587fb17a31b2502ae7a1a4234bc304ee1", size = 2322482, upload-time = "2026-10-24T10:23:58.538Z" },
{ url = "https://files.pythonhosted.org/packages/4a/65/21a4ed3ae5af153c57861d7f987c81fca5bf02d49b76f34c545f8eb6b95f/pycryptodome-4.2.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8217736d5ac9b7fef49d1d2b292d5af4e8300298ef4926742aaec49b2bf6505b", size = 3548352, upload-time = "2026-10-24T10:24:01.185Z" },
{ url = "https://files.pythonhosted.org/packages/4a/a4/9a7e4e8896cfd8131978d31e39c930fb114f6a63751f39d2069b965dad45/pycryptodome-4.2.0-cp315-cp315t-win32.whl", hash = "sha256:b717e36cf14cffe463ff4e7d423a0c9fd6f83b40d207388e0f40dd0a3d4018bc", size = 1306386, upload-time = "2026-10-24T10:24:03.047Z" },
{ url = "https://files.pythonhosted.org/packages/af/6c/e1a78019b7db83e2cfc1c98cea4a7a0afbb6b233514b9dfee2bad7443bd9/pycryptodome-4.2.0-cp315-cp315t-win_amd64.whl", hash = "sha256:585f0a792cdc7fb381ca0d1fac4e20f03fd99bb78cf2733eaab59f62c77ccf88", size = 1688319, upload-time = "2026-10-24T10:24:04.963Z" },
{ url = "https://files.pythonhosted.org/packages/c6/96/7cb051ba325141b33bc7e6ef6152499cf74b74606c5bb4c8c72411e8afdc/pycryptodome-4.2.0-cp315-cp315t-win_arm64.whl", hash = "sha256:831ae8ce070e990d4fcb913c49c7fb0181ea7ab2691fc58dcb9d41fdb5384d85", size = 1330914, upload-time = "2026-10-24T10:24:06.934Z" },
{ url = "https://files.pythonhosted.org/packages/41/4c/954a9a1451f7bcc89d66db39bf6499ef5701e8ff35b6c898c659a7ec8546/pycryptodome-4.2.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e430168f767ecec60c1f850df4abbd0b9f94786b9cc8041a00e976f6d5190c6", size = 1680663, upload-time = "2026-10-24T10:24:08.982Z" },
{ url = "https://files.pythonhosted.org/packages/6e/84/23f8013dce07cbc8c8b9ed878972294230a1edc49a6b64d271c77c878c29/pycryptodome-4.2.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aea22120791b4534a27e39fc01d91265a249ba72dbd682a8a5c1adee475d5483", size = 1513323, upload-time = "2026-10-24T10:24:11.076Z" },
{ url = "https://files.pythonhosted.org/packages/1a/82/693940a8e4a20c3db91050480429daf39c818be3c7ec59f992ded4567ce6/pycryptodome-4.2.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f6f30a457bd83a3074747d11bcb95375397a2e9b9a6b9179ab001e0cdd8ed0c5", size = 2292885, upload-time = "2026-10-24T10:24:13.486Z" },
{ url = "https://files.pythonhosted.org/packages/18/88/33df6b1176b63aed427745696d88ed38f7877104cf0f6004ee9ad600f897/pycryptodome-4.2.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:76777686de3a354c41710727988c2284448d5db9bd82e5ddb9a9b6710d17d9df", size = 2976488, upload-time = "2026-10-24T10:24:16.139Z" },
{ url = "https://files.pythonhosted.org/packages/b1/c6/c1c137d6e3284409ca5b2cfd7db3621167c316f9c314a96274af69a66fbb/pycryptodome-4.2.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c5bc41a418451616cd0ec3d765ba8539416d7429d7cd633ffb603a5a22355e70", size = 2313324, upload-time = "2026-10-24T10:24:18.302Z" },
{ url = "https://files.pythonhosted.org/packages/ef/26/b7752cffc8200040835ad24dc17c9de6376ac27612c8bf943766cb1b3c36/pycryptodome-4.2.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2c5b1e9058687daca3aaaff23eeb1c1853c8251986f0de7a950235e8bc0cc8b7", size = 3537773, upload-time = "2026-10-24T10:24:20.482Z" },
{ url = "https://files.pythonhosted.org/packages/df/1b/34bec1416507cda23b5dfa33c0bca936fd4ac8ed4d3269803477b897649d/pycryptodome-4.2.0-cp39-abi3-win32.whl", hash = "sha256:bb7659f87d7aa69105e1be12764f9cf1908287eb424b242186f5ac3c192ed612", size = 1292974, upload-time = "2026-10-24T10:24:22.422Z" },
{ url = "https://files.pythonhosted.org/packages/03/5b/a4a31e6e1aabd5a26da77c7960e7fcce4fca9132e51868f1bba10bf1e932/pycryptodome-4.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:3282c9fd744a16594e51e1a9fbfe4d658b6b0aaa1f050926e6d591ac7494f78b", size = 1658684, upload-time = "2026-10-24T10:24:24.009Z" },
{ url = "https://files.pythonhosted.org/packages/2b/81/bddb0637fa85a03d00eea2f3514c5aa8d7b9c4921ef5e0c125b4ce662a8c/pycryptodome-4.2.0-cp39-abi3-win_arm64.whl", hash = "sha256:34ec11258967efec61d92a83a63a050ab7a7c31dc3806412f6334b8aef47e2d9", size = 1305591, upload-time = "2026-10-24T10:24:25.993Z" },
{ url = "https://files.pythonhosted.org/packages/df/a1/5460bbb750121f71abacaaf25f0cb1df67be7fe13c70cf6d7c2b8ad54fc0/pycryptodome-4.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ea7dbf6b8ce0f0d2c0e17c1226c74d07c01499db7704797bdbb6cb9ec511ccb", size = 1472158, upload-time = "2026-10-24T10:24:27.92Z" },
{ url = "https://files.pythonhosted.org/packages/e2/65/27ac8fb15f0ffb70b72a0413291637de46d29b075804221703273c3ca6d1/pycryptodome-4.2.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0106af8684e1d73c93bc39a2f1cb4ae2f2636d1f80e6291caddcc37b4efc5dc1", size = 1247107, upload-time = "2026-10-24T10:24:29.672Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7d/6b3ed6cd8626007f6a6b13616b08150bc0bb936a0b84c310e59558ed5136/pycryptodome-4.2.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4deb46e0d0a540b1f047cb6f430249692da15c21edd75f5e6278314089fb859", size = 1426092, upload-time = "2026-10-24T10:24:31.627Z" },
{ url = "https://files.pythonhosted.org/packages/73/45/5adf4dbb1eab1da6291d25f777d1d34f745c7f2993bf2d4d3b0d9abb87c6/pycryptodome-4.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e094873fe87ae4d546ef393cec7ea1e1cf8e0684f649612b19352afa67eb8bdb", size = 1662257, upload-time = "2026-10-24T10:24:33.373Z" },
{ url = "https://files.pythonhosted.org/packages/57/88/fef93a9ecbda783a312b15a8823fa1a9a4ec3474685e756584af595457aa/pycryptodome-4.2.0-pp311-pypy311_pp80-macosx_10_15_x86_64.whl", hash = "sha256:30f418cd1f4e776bde6c5df1fcd90ddfe74dfc8f679286a82f17ec1d4018cce0", size = 1467319, upload-time = "2026-10-24T10:24:35.108Z" },
{ url = "https://files.pythonhosted.org/packages/78/65/cb53934c9f8d5c41dfcfaddb0d98103f4dd6670af1ffdbc2582bcdd99382/pycryptodome-4.2.0-pp311-pypy311_pp80-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20cb4a489f7a5150a882901884b84ee0df77a47b9a6eb6e2dd8e98997b7b826f", size = 1247105, upload-time = "2026-10-24T10:24:36.859Z" },
{ url = "https://files.pythonhosted.org/packages/11/38/5bba7fd24beb6bc7a82fdd155f2df39f51c8ad16143fc99d3a1ccc7c76b4/pycryptodome-4.2.0-pp311-pypy311_pp80-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f104dc1a38aa1172a166ebe3716f2666f785bfa015b7ca13bb808c12c902e455", size = 1426092, upload-time = "2026-10-24T10:24:38.584Z" },
{ url = "https://files.pythonhosted.org/packages/38/cf/e70a8cb1ab184b59b2d02dfd403965a0c6419571d8520463944bcb0ca8a0/pycryptodome-4.2.0-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:a7c6857077255f5f4e6c34e6325802ba1bb59960133c0a5e584d9371dbdf3dea", size = 1662214, upload-time = "2026-10-24T10:24:40.139Z" },
]
[[package]]
@@ -3401,11 +3418,11 @@ wheels = [
[[package]]
name = "setuptools"
version = "83.0.0"
version = "84.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" }
sdist = { url = "https://files.pythonhosted.org/packages/73/eb/7e5f628ee2eb168fecbf8026de7856b2543d6d609cc85f086b9a91ac93d6/setuptools-84.1.0.tar.gz", hash = "sha256:23657205b63d4d31395ab917815247f694aafad28cd21316bf5fd45a51048ea0", size = 1171783, upload-time = "2026-10-19T20:11:43.489Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" },
{ url = "https://files.pythonhosted.org/packages/18/02/b4bbb90f8f29e334898ff45c6d9b1fffaa6f378017052b3023a06262b5f7/setuptools-84.1.0-py3-none-any.whl", hash = "sha256:5231689c6ac8156cd155cfb98c3fd6fb252c3e7e9bd3d78b64d3c55f132624c9", size = 819380, upload-time = "2026-10-19T20:11:41.684Z" },
]
[[package]]
@@ -3651,14 +3668,14 @@ wheels = [
[[package]]
name = "types-requests"
version = "2.33.0.20260906"
version = "2.33.0.20261006"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c0/18/4c2c0290953f8b3b9612adfcb07b57f144ade3ad32a76764fca42b77c5f3/types_requests-2.33.0.20260906.tar.gz", hash = "sha256:76ab8a0fb736744a0c3deee7aa57b2927e301f078d9e61f5391b3e92002416b9", size = 25263, upload-time = "2026-09-06T06:35:47.707Z" }
sdist = { url = "https://files.pythonhosted.org/packages/57/15/9b7e2e2e7c87d01366185b198b3febc6bc0c973f2bf21a62da4ab7d3495e/types_requests-2.33.0.20261006.tar.gz", hash = "sha256:0652999e9306aea345f40732d58fa49a7f6cade6a0d74d92119c5c8d82eddaf0", size = 25316, upload-time = "2026-10-06T08:15:57.782Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/60/4c/51ec821d22a45b4162fa3f3e9e94ea4c0f8c49e82363b10955baa5438391/types_requests-2.33.0.20260906-py3-none-any.whl", hash = "sha256:9f53622652bd921ead7a54d665be1b8d518165c8b51cc9d68d944cd6b6bfa8fb", size = 21461, upload-time = "2026-09-06T06:35:45.856Z" },
{ url = "https://files.pythonhosted.org/packages/4d/72/b82789207b3d360ce9f5a4372c790c52cc3dd928aeb7d4faeec652b651b2/types_requests-2.33.0.20261006-py3-none-any.whl", hash = "sha256:26cc8146505cab33cda9737991929e4144c559bebe05078ccc6998f27c4ca2c1", size = 21445, upload-time = "2026-10-06T08:15:56.658Z" },
]
[[package]]
@@ -4239,15 +4256,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7d/f3/1ac078fc8fceadcf066469acecacb35d2821cbfaf7d6fc5ac2107c7a314d/xxhash-4.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fac4832b638000106207bc44e44b9616a6a416aaee56c62b01d61f3705e49f58", size = 37252, upload-time = "2026-08-17T08:24:06.652Z" },
]
[[package]]
name = "zipp"
version = "3.23.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
]
[[package]]
name = "zstandard"
version = "0.25.0"
+3 -3
View File
@@ -286,7 +286,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.16"
version = "1.2.19"
source = { editable = "../langgraph" }
dependencies = [
{ name = "langchain-core" },
@@ -370,7 +370,7 @@ test = [
[[package]]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -467,7 +467,7 @@ test = [
[[package]]
name = "langgraph-checkpoint-sqlite"
version = "3.1.2"
version = "3.1.3"
source = { editable = "../checkpoint-sqlite" }
dependencies = [
{ name = "aiosqlite" },
+17 -2
View File
@@ -1,6 +1,21 @@
# Changelog
## Unreleased
## 0.4.12
### Removed
- **`PrioritySettings.fairness_weight`** — removed. The server stores only
`priority` and `fairness_group` on the thread; it ignores a
`fairness_weight` and logs a warning. Remove it from your callback.
### Changed
- **`PrioritySettings.priority`** — now says -5 runs first and 5 runs last,
matching how the server orders runs. 0.4.9 to 0.4.11 said the reverse, and
their example gave premium users 3, which runs them after everyone at the
default 0. Check any callback written from those docs.
## 0.4.11 and earlier
### Added
@@ -38,7 +53,7 @@
and name the object in the `priority` section of `langgraph.json`. The
server calls the function with a `PriorityContext` at the first run on a
thread with no settings and stores the `PrioritySettings` it returns
(`priority`, `fairness_group`, `fairness_weight`) on the thread. Requires
(`priority`, `fairness_group`) on the thread. Requires
`langgraph-api` 0.18.0 or later and a `langgraph-cli` that supports the
section; older versions of either ignore it.
+1 -1
View File
@@ -5,7 +5,7 @@ from langgraph_sdk.encryption.types import DecryptResult, EncryptionContext
from langgraph_sdk.priority import Priority
from langgraph_sdk.priority.types import PriorityContext, PrioritySettings
__version__ = "0.4.9"
__version__ = "0.4.11"
__all__ = [
"Auth",
+148 -59
View File
@@ -14,9 +14,16 @@ import orjson
from langgraph_sdk._shared.utilities import (
_orjson_default,
_reconnect_delay,
_run_check_headers,
_run_url_from_stream_location,
_validate_reconnect_location,
)
from langgraph_sdk.errors import _araise_for_status_typed
from langgraph_sdk.errors import (
InternalServerError,
RateLimitError,
_araise_for_status_typed,
)
from langgraph_sdk.schema import QueryParamTypes, StreamPart
from langgraph_sdk.sse import SSEDecoder, aiter_lines_raw
@@ -146,7 +153,8 @@ class HttpClient:
on_response: Callable[[httpx.Response], None] | None = None,
reconnect_limit: int = 5,
) -> Any:
"""Send a request that automatically reconnects to Location header."""
"""Send a request that reconnects to its Location header if the response is
cut off, with backoff between attempts."""
request_headers, content = await _aencode_json(json)
if headers:
request_headers.update(headers)
@@ -176,13 +184,27 @@ class HttpClient:
stacklevel=2,
)
await r.aclose()
return await self.request_reconnect(
loc,
"GET",
headers=request_headers,
# don't pass on_response so it's only called once
reconnect_limit=reconnect_limit - 1,
)
for attempt in range(reconnect_limit):
await asyncio.sleep(_reconnect_delay(attempt, base=1.0, cap=5.0))
# A restart or a deploy looks like a refused connection, a
# dropped body, a 429 or a 5xx; other errors won't go away.
try:
return await self.request_reconnect(
loc,
"GET",
headers=headers,
# don't pass on_response so it's only called once
reconnect_limit=0,
)
except httpx.HTTPStatusError as err:
status = err.response.status_code
if attempt == reconnect_limit - 1 or (
status != 429 and status < 500
):
raise
except httpx.TransportError:
if attempt == reconnect_limit - 1:
raise
async def stream(
self,
@@ -212,77 +234,119 @@ class HttpClient:
reconnect_path: str | None = None
reconnect_attempts = 0
max_reconnect_attempts = 5
run_ended = False
while True:
current_headers = dict(
request_headers if reconnect_path is None else reconnect_headers
)
if last_event_id is not None:
current_headers = {
key: value
for key, value in current_headers.items()
if key.lower() != "last-event-id"
}
if last_event_id:
current_headers["Last-Event-ID"] = last_event_id
if reconnect_path is not None and not any(
key.lower() == "last-event-id" and value
for key, value in current_headers.items()
):
warnings.warn(
"Reconnecting without a Last-Event-ID, so events sent while "
"disconnected are not replayed. Only runs created with "
"stream_resumable=True send the event IDs a reconnect resumes "
"from.",
stacklevel=2,
)
current_method = method if reconnect_path is None else "GET"
current_content = content if reconnect_path is None else None
current_params = params if reconnect_path is None else None
retry = False
async with self.client.stream(
current_method,
reconnect_path or path,
headers=current_headers,
content=current_content,
params=current_params,
) as res:
if reconnect_path is None and on_response:
on_response(res)
# check status
await _araise_for_status_typed(res)
# check content type
content_type = res.headers.get("content-type", "").partition(";")[0]
if "text/event-stream" not in content_type:
raise httpx.TransportError(
"Expected response header Content-Type to contain 'text/event-stream', "
f"got {content_type!r}"
)
reconnect_cause: httpx.HTTPError | None = None
try:
async with self.client.stream(
current_method,
reconnect_path or path,
headers=current_headers,
content=current_content,
params=current_params,
) as res:
if reconnect_path is None and on_response:
on_response(res)
# check status
await _araise_for_status_typed(res)
# check content type
content_type = res.headers.get("content-type", "").partition(";")[0]
if "text/event-stream" not in content_type:
raise httpx.TransportError(
"Expected response header Content-Type to contain 'text/event-stream', "
f"got {content_type!r}"
)
reconnect_location = res.headers.get("location")
if reconnect_location:
_validate_reconnect_location(
self.client.base_url, reconnect_location
)
reconnect_path = reconnect_location
reconnect_location = res.headers.get("location")
if reconnect_location:
_validate_reconnect_location(
self.client.base_url, reconnect_location
)
reconnect_path = reconnect_location
# parse SSE
decoder = SSEDecoder()
try:
async for line in aiter_lines_raw(res):
sse = decoder.decode(line=cast("bytes", line).rstrip(b"\n"))
if sse is not None:
# parse SSE
decoder = SSEDecoder(last_event_id=last_event_id)
try:
async for line in aiter_lines_raw(res):
sse = decoder.decode(line=cast("bytes", line).rstrip(b"\n"))
if sse is not None:
if decoder.last_event_id is not None:
last_event_id = decoder.last_event_id
if sse.event or decoder.has_data:
# Progress since the last drop earns a fresh
# set of reconnect attempts.
reconnect_attempts = 0
run_ended |= sse.event in ("end", "error")
yield sse
except httpx.HTTPError as err:
# httpx.TransportError inherits from HTTPError, so transient
# disconnects during streaming land here.
if reconnect_path is None:
raise
reconnect_cause = err
else:
if sse := decoder.decode(b""):
if decoder.last_event_id is not None:
last_event_id = decoder.last_event_id
if sse.event or sse.data is not None:
if sse.event or decoder.has_data:
# decoder.decode(b"") flushes the in-flight event and may
# return an empty placeholder when there is no pending
# message. Skip these no-op events so the stream doesn't
# emit a trailing blank item after reconnects.
run_ended |= sse.event in ("end", "error")
yield sse
except httpx.HTTPError:
# httpx.TransportError inherits from HTTPError, so transient
# disconnects during streaming land here.
if reconnect_path is None:
raise
retry = True
else:
if sse := decoder.decode(b""):
if decoder.last_event_id is not None:
last_event_id = decoder.last_event_id
if sse.event or sse.data is not None:
# decoder.decode(b"") flushes the in-flight event and may
# return an empty placeholder when there is no pending
# message. Skip these no-op events so the stream doesn't
# emit a trailing blank item after reconnects.
yield sse
if retry:
except (httpx.TransportError, RateLimitError, InternalServerError) as err:
# A reconnect that can't open (connection refused, 429, 5xx or a
# non-SSE body) usually means the server is restarting or
# mid-deploy, so retry it like a drop. Before the server sends a
# Location there is nothing to reconnect to, so raise.
if reconnect_path is None:
raise
reconnect_cause = err
# A run's stream closes cleanly both when the run ends and when a
# proxy timeout or a server restart cuts it. Without an `end` or
# `error` event the two look the same, so ask the run.
if reconnect_cause is None and reconnect_path is not None and not run_ended:
reconnect_cause = await _acheck_run(
self.client, reconnect_path, reconnect_headers
)
if reconnect_cause is not None:
reconnect_attempts += 1
if reconnect_attempts > max_reconnect_attempts:
raise httpx.TransportError(
"Exceeded maximum SSE reconnection attempts"
)
) from reconnect_cause
await asyncio.sleep(
_reconnect_delay(reconnect_attempts - 1, base=1.0, cap=5.0)
)
continue
break
@@ -310,3 +374,28 @@ async def _adecode_json(r: httpx.Response) -> Any:
if body
else None
)
async def _acheck_run(
client: httpx.AsyncClient, stream_location: str, headers: Mapping[str, str]
) -> httpx.HTTPError | None:
run_url = _run_url_from_stream_location(stream_location)
if run_url is None:
return None
try:
r = await client.get(run_url, headers=_run_check_headers(headers))
if r.status_code == 429 or r.status_code >= 500:
await _araise_for_status_typed(r)
status = (await _adecode_json(r))["status"] if r.is_success else None
except (httpx.TransportError, RateLimitError, InternalServerError) as err:
# The restart that closed the stream can fail the check too, so retry
# it like a drop.
return err
except Exception:
# Anything else must not turn a normal end of stream into an error.
return None
if status in ("pending", "running"):
return httpx.TransportError(
"Stream closed while the run was still pending or running"
)
return None
+10
View File
@@ -99,6 +99,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v1"] = "v1",
) -> AsyncIterator[StreamPart]: ...
@@ -131,6 +132,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v2"],
) -> AsyncIterator[StreamPartV2]: ...
@@ -147,6 +149,7 @@ class RunsClient:
stream_resumable: bool = False,
metadata: Mapping[str, Any] | None = None,
config: Config | None = None,
context: Context | None = None,
checkpoint_during: bool | None = None,
interrupt_before: All | Sequence[str] | None = None,
interrupt_after: All | Sequence[str] | None = None,
@@ -160,6 +163,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v1"] = "v1",
) -> AsyncIterator[StreamPart]: ...
@@ -176,6 +180,7 @@ class RunsClient:
stream_resumable: bool = False,
metadata: Mapping[str, Any] | None = None,
config: Config | None = None,
context: Context | None = None,
checkpoint_during: bool | None = None,
interrupt_before: All | Sequence[str] | None = None,
interrupt_after: All | Sequence[str] | None = None,
@@ -189,6 +194,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v2"],
) -> AsyncIterator[StreamPartV2]: ...
@@ -385,6 +391,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> Run: ...
@overload
@@ -414,6 +421,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> Run: ...
async def create(
@@ -647,6 +655,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> builtins.list[dict] | dict[str, Any]: ...
@overload
@@ -673,6 +682,7 @@ class RunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> builtins.list[dict] | dict[str, Any]: ...
async def wait(
+8 -7
View File
@@ -16,7 +16,6 @@ from __future__ import annotations
import asyncio
import contextlib
import logging
import random
from collections.abc import AsyncGenerator, AsyncIterator, Generator, Mapping
from dataclasses import dataclass, field
from typing import Any, Literal, TypedDict, cast
@@ -25,7 +24,7 @@ from langchain_core.language_models.chat_model_stream import AsyncChatModelStrea
from langchain_protocol import Event, SubscribeParams
from langgraph_sdk._async.http import HttpClient
from langgraph_sdk._shared.utilities import _quote_path_param
from langgraph_sdk._shared.utilities import _quote_path_param, _reconnect_delay
from langgraph_sdk.schema import LangSmithTracing, QueryParamTypes
from langgraph_sdk.stream.controller import _SeenEventIds
from langgraph_sdk.stream.decoders import (
@@ -1691,11 +1690,13 @@ class AsyncThreadStream:
async def _reconnect_sleep(self, attempt: int) -> None:
"""Sleep with exponential backoff and jitter for reconnect attempt `attempt`."""
base = self._shared_reconnect_backoff_base
cap = self._shared_reconnect_backoff_cap
delay = min(cap, base * (2**attempt))
jitter = random.uniform(0, delay * 0.25)
await asyncio.sleep(delay + jitter)
await asyncio.sleep(
_reconnect_delay(
attempt,
base=self._shared_reconnect_backoff_base,
cap=self._shared_reconnect_backoff_cap,
)
)
async def _reconnect_shared_stream(self) -> bool:
"""Attempt to reopen the shared stream after a post-ready transport drop.
+30 -3
View File
@@ -21,6 +21,7 @@ from langgraph_sdk.schema import (
ThreadSelectField,
ThreadSortBy,
ThreadState,
ThreadStateSelectField,
ThreadStatus,
ThreadStreamMode,
ThreadUpdateStateResponse,
@@ -489,6 +490,7 @@ class ThreadsClient:
checkpoint_id: str | None = None, # deprecated
*,
subgraphs: bool = False,
select: list[ThreadStateSelectField] | None = None,
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
) -> ThreadState:
@@ -499,6 +501,9 @@ class ThreadsClient:
checkpoint: The checkpoint to get the state of.
checkpoint_id: (deprecated) The checkpoint ID to get the state of.
subgraphs: Include subgraphs states.
select: Fields to include in the response. Use `values.<key>` to
return a single state key (e.g. `values.messages`), which lets the
server skip loading the rest of the state. Defaults to all fields.
headers: Optional custom headers to include with the request.
params: Optional query parameters to include with the request.
@@ -591,16 +596,30 @@ class ThreadsClient:
}
}
```
Fetch only the `messages` state key:
```python
thread_state = await client.threads.get_state(
thread_id="my_thread_id",
select=["values.messages"],
)
```
"""
if checkpoint:
payload: dict[str, Any] = {"checkpoint": checkpoint, "subgraphs": subgraphs}
if select:
payload["select"] = select
return await self.http.post(
f"/threads/{_quote_path_param(thread_id)}/state/checkpoint",
json={"checkpoint": checkpoint, "subgraphs": subgraphs},
json=payload,
headers=headers,
params=params,
)
elif checkpoint_id:
get_params = {"subgraphs": subgraphs}
get_params: dict[str, Any] = {"subgraphs": subgraphs}
if select:
get_params["select"] = select
if params:
get_params = {**get_params, **dict(params)}
return await self.http.get(
@@ -609,7 +628,9 @@ class ThreadsClient:
headers=headers,
)
else:
get_params = {"subgraphs": subgraphs}
get_params: dict[str, Any] = {"subgraphs": subgraphs}
if select:
get_params["select"] = select
if params:
get_params = {**get_params, **dict(params)}
return await self.http.get(
@@ -692,6 +713,7 @@ class ThreadsClient:
before: str | Checkpoint | None = None,
metadata: Mapping[str, Any] | None = None,
checkpoint: Checkpoint | None = None,
select: list[ThreadStateSelectField] | None = None,
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
) -> list[ThreadState]:
@@ -703,6 +725,9 @@ class ThreadsClient:
limit: The maximum number of states to return.
before: Return states before this checkpoint.
metadata: Filter states by metadata key-value pairs.
select: Fields to include in the response. Use `values.<key>` to
return a single state key (e.g. `values.messages`), which lets the
server skip loading the rest of the state. Defaults to all fields.
headers: Optional custom headers to include with the request.
params: Optional query parameters to include with the request.
@@ -729,6 +754,8 @@ class ThreadsClient:
payload["metadata"] = metadata
if checkpoint:
payload["checkpoint"] = checkpoint
if select:
payload["select"] = select
return await self.http.post(
f"/threads/{_quote_path_param(thread_id)}/history",
json=payload,
+30 -1
View File
@@ -4,11 +4,12 @@ from __future__ import annotations
import functools
import os
import random
import re
from collections.abc import Mapping
from datetime import tzinfo
from typing import TYPE_CHECKING, Any, cast
from urllib.parse import quote, urlparse
from urllib.parse import quote, urlparse, urlsplit, urlunsplit
import httpx
@@ -194,6 +195,34 @@ def _validate_reconnect_location(base_url: httpx.URL, location: str) -> str:
return location
_RUN_STREAM_PATH = re.compile(r"(.*/threads/[^/]+/runs/[^/]+)/stream")
def _run_url_from_stream_location(location: str) -> str | None:
url = urlsplit(location)
if match := _RUN_STREAM_PATH.fullmatch(url.path):
return urlunsplit((url.scheme, url.netloc, match[1], "", ""))
return None
def _run_check_headers(reconnect_headers: Mapping[str, str]) -> dict[str, str]:
headers = {
key: value
for key, value in reconnect_headers.items()
if key.lower() not in ("accept", "last-event-id")
}
return {**headers, "Accept": "application/json"}
def _reconnect_delay(attempt: int, *, base: float, cap: float) -> float:
"""Seconds to wait before reconnect attempt `attempt` (0-based).
Exponential backoff capped at `cap`, plus up to 25% jitter.
"""
delay = min(cap, base * (2**attempt))
return delay + random.uniform(0, delay * 0.25)
def _provided_vals(d: Mapping[str, Any]) -> dict[str, Any]:
return {k: v for k, v in d.items() if v is not None}
+147 -57
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import logging
import sys
import time
import warnings
from collections.abc import Callable, Iterator, Mapping
from typing import Any, cast
@@ -13,9 +14,16 @@ import orjson
from langgraph_sdk._shared.utilities import (
_orjson_default,
_reconnect_delay,
_run_check_headers,
_run_url_from_stream_location,
_validate_reconnect_location,
)
from langgraph_sdk.errors import _raise_for_status_typed
from langgraph_sdk.errors import (
InternalServerError,
RateLimitError,
_raise_for_status_typed,
)
from langgraph_sdk.schema import QueryParamTypes, StreamPart
from langgraph_sdk.sse import SSEDecoder, iter_lines_raw
@@ -146,7 +154,8 @@ class SyncHttpClient:
on_response: Callable[[httpx.Response], None] | None = None,
reconnect_limit: int = 5,
) -> Any:
"""Send a request that automatically reconnects to Location header."""
"""Send a request that reconnects to its Location header if the response is
cut off, with backoff between attempts."""
request_headers, content = _encode_json(json)
if headers:
request_headers.update(headers)
@@ -176,13 +185,27 @@ class SyncHttpClient:
stacklevel=2,
)
r.close()
return self.request_reconnect(
loc,
"GET",
headers=request_headers,
# don't pass on_response so it's only called once
reconnect_limit=reconnect_limit - 1,
)
for attempt in range(reconnect_limit):
time.sleep(_reconnect_delay(attempt, base=1.0, cap=5.0))
# A restart or a deploy looks like a refused connection, a
# dropped body, a 429 or a 5xx; other errors won't go away.
try:
return self.request_reconnect(
loc,
"GET",
headers=headers,
# don't pass on_response so it's only called once
reconnect_limit=0,
)
except httpx.HTTPStatusError as err:
status = err.response.status_code
if attempt == reconnect_limit - 1 or (
status != 429 and status < 500
):
raise
except httpx.TransportError:
if attempt == reconnect_limit - 1:
raise
def stream(
self,
@@ -214,79 +237,121 @@ class SyncHttpClient:
reconnect_path: str | None = None
reconnect_attempts = 0
max_reconnect_attempts = 5
run_ended = False
while True:
current_headers = dict(
request_headers if reconnect_path is None else reconnect_headers
)
if last_event_id is not None:
current_headers = {
key: value
for key, value in current_headers.items()
if key.lower() != "last-event-id"
}
if last_event_id:
current_headers["Last-Event-ID"] = last_event_id
if reconnect_path is not None and not any(
key.lower() == "last-event-id" and value
for key, value in current_headers.items()
):
warnings.warn(
"Reconnecting without a Last-Event-ID, so events sent while "
"disconnected are not replayed. Only runs created with "
"stream_resumable=True send the event IDs a reconnect resumes "
"from.",
stacklevel=2,
)
current_method = method if reconnect_path is None else "GET"
current_content = content if reconnect_path is None else None
current_params = params if reconnect_path is None else None
retry = False
with self.client.stream(
current_method,
reconnect_path or path,
headers=current_headers,
content=current_content,
params=current_params,
) as res:
if reconnect_path is None and on_response:
on_response(res)
# check status
_raise_for_status_typed(res)
# check content type
content_type = res.headers.get("content-type", "").partition(";")[0]
if "text/event-stream" not in content_type:
raise httpx.TransportError(
"Expected response header Content-Type to contain 'text/event-stream', "
f"got {content_type!r}"
)
reconnect_cause: httpx.HTTPError | None = None
try:
with self.client.stream(
current_method,
reconnect_path or path,
headers=current_headers,
content=current_content,
params=current_params,
) as res:
if reconnect_path is None and on_response:
on_response(res)
# check status
_raise_for_status_typed(res)
# check content type
content_type = res.headers.get("content-type", "").partition(";")[0]
if "text/event-stream" not in content_type:
raise httpx.TransportError(
"Expected response header Content-Type to contain 'text/event-stream', "
f"got {content_type!r}"
)
reconnect_location = res.headers.get("location")
if reconnect_location:
_validate_reconnect_location(
self.client.base_url, reconnect_location
)
reconnect_path = reconnect_location
reconnect_location = res.headers.get("location")
if reconnect_location:
_validate_reconnect_location(
self.client.base_url, reconnect_location
)
reconnect_path = reconnect_location
decoder = SSEDecoder()
try:
for line in iter_lines_raw(res):
sse = decoder.decode(cast(bytes, line).rstrip(b"\n"))
if sse is not None:
decoder = SSEDecoder(last_event_id=last_event_id)
try:
for line in iter_lines_raw(res):
sse = decoder.decode(cast(bytes, line).rstrip(b"\n"))
if sse is not None:
if decoder.last_event_id is not None:
last_event_id = decoder.last_event_id
if sse.event or decoder.has_data:
# Progress since the last drop earns a fresh
# set of reconnect attempts.
reconnect_attempts = 0
run_ended |= sse.event in ("end", "error")
yield sse
except httpx.HTTPError as err:
# httpx.TransportError inherits from HTTPError, so transient
# disconnects during streaming land here.
if reconnect_path is None:
raise
reconnect_cause = err
else:
if sse := decoder.decode(b""):
if decoder.last_event_id is not None:
last_event_id = decoder.last_event_id
if sse.event or sse.data is not None:
if sse.event or decoder.has_data:
# See async stream implementation for rationale on
# skipping empty flush events.
run_ended |= sse.event in ("end", "error")
yield sse
except httpx.HTTPError:
# httpx.TransportError inherits from HTTPError, so transient
# disconnects during streaming land here.
if reconnect_path is None:
raise
retry = True
else:
if sse := decoder.decode(b""):
if decoder.last_event_id is not None:
last_event_id = decoder.last_event_id
if sse.event or sse.data is not None:
# See async stream implementation for rationale on
# skipping empty flush events.
yield sse
if retry:
except (httpx.TransportError, RateLimitError, InternalServerError) as err:
# A reconnect that can't open (connection refused, 429, 5xx or a
# non-SSE body) usually means the server is restarting or
# mid-deploy, so retry it like a drop. Before the server sends a
# Location there is nothing to reconnect to, so raise.
if reconnect_path is None:
raise
reconnect_cause = err
# A run's stream closes cleanly both when the run ends and when a
# proxy timeout or a server restart cuts it. Without an `end` or
# `error` event the two look the same, so ask the run.
if reconnect_cause is None and reconnect_path is not None and not run_ended:
reconnect_cause = _check_run(
self.client, reconnect_path, reconnect_headers
)
if reconnect_cause is not None:
reconnect_attempts += 1
if reconnect_attempts > max_reconnect_attempts:
raise httpx.TransportError(
"Exceeded maximum SSE reconnection attempts"
)
) from reconnect_cause
time.sleep(_reconnect_delay(reconnect_attempts - 1, base=1.0, cap=5.0))
continue
break
def _encode_json(json: Any) -> tuple[dict[str, str], bytes]:
def _encode_json(json: Any) -> tuple[dict[str, str], bytes | None]:
if json is None:
return {}, None
body = orjson.dumps(
json,
_orjson_default,
@@ -301,3 +366,28 @@ def _encode_json(json: Any) -> tuple[dict[str, str], bytes]:
def _decode_json(r: httpx.Response) -> Any:
body = r.read()
return orjson.loads(body) if body else None
def _check_run(
client: httpx.Client, stream_location: str, headers: Mapping[str, str]
) -> httpx.HTTPError | None:
run_url = _run_url_from_stream_location(stream_location)
if run_url is None:
return None
try:
r = client.get(run_url, headers=_run_check_headers(headers))
if r.status_code == 429 or r.status_code >= 500:
_raise_for_status_typed(r)
status = _decode_json(r)["status"] if r.is_success else None
except (httpx.TransportError, RateLimitError, InternalServerError) as err:
# The restart that closed the stream can fail the check too, so retry
# it like a drop.
return err
except Exception:
# Anything else must not turn a normal end of stream into an error.
return None
if status in ("pending", "running"):
return httpx.TransportError(
"Stream closed while the run was still pending or running"
)
return None
+21 -2
View File
@@ -80,6 +80,7 @@ class SyncRunsClient:
command: Command | None = None,
stream_mode: StreamMode | Sequence[StreamMode] = "values",
stream_subgraphs: bool = False,
stream_resumable: bool = False,
metadata: Mapping[str, Any] | None = None,
config: Config | None = None,
context: Context | None = None,
@@ -98,6 +99,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v1"] = "v1",
) -> Iterator[StreamPart]: ...
@@ -111,6 +113,7 @@ class SyncRunsClient:
command: Command | None = None,
stream_mode: StreamMode | Sequence[StreamMode] = "values",
stream_subgraphs: bool = False,
stream_resumable: bool = False,
metadata: Mapping[str, Any] | None = None,
config: Config | None = None,
context: Context | None = None,
@@ -129,6 +132,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v2"],
) -> Iterator[StreamPartV2]: ...
@@ -159,6 +163,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v1"] = "v1",
) -> Iterator[StreamPart]: ...
@@ -189,6 +194,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
version: Literal["v2"],
) -> Iterator[StreamPartV2]: ...
@@ -380,6 +386,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> Run: ...
@overload
@@ -409,6 +416,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> Run: ...
def create(
@@ -642,6 +650,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> builtins.list[dict] | dict[str, Any]: ...
@overload
@@ -668,6 +677,7 @@ class SyncRunsClient:
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
durability: Durability | None = None,
) -> builtins.list[dict] | dict[str, Any]: ...
def wait(
@@ -815,7 +825,6 @@ class SyncRunsClient:
"checkpoint_during": checkpoint_during,
"on_completion": on_completion,
"after_seconds": after_seconds,
"raise_error": raise_error,
"durability": durability,
"langsmith_tracer": langsmith_tracing,
}
@@ -830,7 +839,7 @@ class SyncRunsClient:
if thread_id is not None
else "/runs/wait"
)
return self.http.request_reconnect(
response = self.http.request_reconnect(
endpoint,
"POST",
json={k: v for k, v in payload.items() if v is not None},
@@ -838,6 +847,16 @@ class SyncRunsClient:
headers=headers,
on_response=on_response if on_run_created else None,
)
if (
raise_error
and isinstance(response, dict)
and "__error__" in response
and isinstance(response["__error__"], dict)
):
raise Exception(
f"{response['__error__'].get('error')}: {response['__error__'].get('message')}"
)
return response
def list(
self,
+29 -3
View File
@@ -21,6 +21,7 @@ from langgraph_sdk.schema import (
ThreadSelectField,
ThreadSortBy,
ThreadState,
ThreadStateSelectField,
ThreadStatus,
ThreadStreamMode,
ThreadUpdateStateResponse,
@@ -480,6 +481,7 @@ class SyncThreadsClient:
checkpoint_id: str | None = None, # deprecated
*,
subgraphs: bool = False,
select: list[ThreadStateSelectField] | None = None,
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
) -> ThreadState:
@@ -489,6 +491,9 @@ class SyncThreadsClient:
thread_id: The ID of the thread to get the state of.
checkpoint: The checkpoint to get the state of.
subgraphs: Include subgraphs states.
select: Fields to include in the response. Use `values.<key>` to
return a single state key (e.g. `values.messages`), which lets the
server skip loading the rest of the state. Defaults to all fields.
headers: Optional custom headers to include with the request.
Returns:
@@ -581,16 +586,29 @@ class SyncThreadsClient:
}
```
Fetch only the `messages` state key:
```python
thread_state = client.threads.get_state(
thread_id="my_thread_id",
select=["values.messages"],
)
```
"""
if checkpoint:
payload: dict[str, Any] = {"checkpoint": checkpoint, "subgraphs": subgraphs}
if select:
payload["select"] = select
return self.http.post(
f"/threads/{_quote_path_param(thread_id)}/state/checkpoint",
json={"checkpoint": checkpoint, "subgraphs": subgraphs},
json=payload,
headers=headers,
params=params,
)
elif checkpoint_id:
get_params = {"subgraphs": subgraphs}
get_params: dict[str, Any] = {"subgraphs": subgraphs}
if select:
get_params["select"] = select
if params:
get_params = {**get_params, **dict(params)}
return self.http.get(
@@ -599,7 +617,9 @@ class SyncThreadsClient:
headers=headers,
)
else:
get_params = {"subgraphs": subgraphs}
get_params: dict[str, Any] = {"subgraphs": subgraphs}
if select:
get_params["select"] = select
if params:
get_params = {**get_params, **dict(params)}
return self.http.get(
@@ -679,6 +699,7 @@ class SyncThreadsClient:
before: str | Checkpoint | None = None,
metadata: Mapping[str, Any] | None = None,
checkpoint: Checkpoint | None = None,
select: list[ThreadStateSelectField] | None = None,
headers: Mapping[str, str] | None = None,
params: QueryParamTypes | None = None,
) -> list[ThreadState]:
@@ -690,6 +711,9 @@ class SyncThreadsClient:
limit: The maximum number of states to return.
before: Return states before this checkpoint.
metadata: Filter states by metadata key-value pairs.
select: Fields to include in the response. Use `values.<key>` to
return a single state key (e.g. `values.messages`), which lets the
server skip loading the rest of the state. Defaults to all fields.
headers: Optional custom headers to include with the request.
Returns:
@@ -717,6 +741,8 @@ class SyncThreadsClient:
payload["metadata"] = metadata
if checkpoint:
payload["checkpoint"] = checkpoint
if select:
payload["select"] = select
return self.http.post(
f"/threads/{_quote_path_param(thread_id)}/history",
json=payload,
+1 -1
View File
@@ -811,7 +811,7 @@ class _On:
def __call__(
self,
*,
resources: str | Sequence[str],
resources: str | Sequence[str] | None = None,
actions: str | Sequence[str] | None = None,
) -> Callable[[AHO], AHO]: ...
@@ -102,7 +102,7 @@ class Priority:
@priority.assign
async def assign(ctx: PriorityContext) -> PrioritySettings | None:
return {
"priority": 3 if "premium" in ctx.permissions else 0,
"priority": -3 if "premium" in ctx.permissions else 0,
# Stored unencrypted, so an opaque ID rather than personal data.
"fairness_group": ctx.user["org_id"],
}
@@ -134,7 +134,7 @@ class Priority:
@priority.assign
async def assign(ctx: PriorityContext) -> PrioritySettings | None:
if ctx.cron_id is not None:
return {"priority": -2}
return {"priority": 2}
return None
```
+8 -6
View File
@@ -50,18 +50,20 @@ class PrioritySettings(typing.TypedDict, total=False):
Every key is optional: a missing key or a key set to `None` takes its
default, and returning `None` instead of a dict gives every key its default.
A value outside its range, or an empty `fairness_group`, is an error.
A `priority` outside -5 to 5, or a `fairness_group` longer than 256
characters, is an error.
"""
priority: int | None
"""An integer from -5 to 5, where 5 runs first and -5 runs last. Defaults to 0."""
"""An integer from -5 to 5, where -5 runs first and 5 runs last. Defaults to 0."""
fairness_group: str | None
"""The thread's fairness group, a non-empty string. Defaults to `"default"`.
"""Currently disabled.
The thread's fairness group, a string of at most 256 characters.
Defaults to `"default"`, as does an empty string. It has no effect on run
order for now.
The server stores it unencrypted, even when the deployment uses custom
encryption, so use an opaque ID rather than personal data.
"""
fairness_weight: int | None
"""The weight of the thread's fairness group, an integer from 1 to 100. Defaults to 1."""
+15
View File
@@ -480,6 +480,21 @@ ThreadSelectField = Literal[
"interrupts",
]
ThreadStateSelectField = (
Literal[
"values",
"next",
"tasks",
"checkpoint",
"metadata",
"created_at",
"parent_checkpoint",
"interrupts",
]
| str
)
"""Also accepts `values.<key>` for a single state key, e.g. `values.messages`."""
RunSelectField = Literal[
"run_id",
"thread_id",
+44 -20
View File
@@ -30,18 +30,14 @@ class BytesLineDecoder:
# See https://docs.python.org/3/glossary.html#term-universal-newlines
NEWLINE_CHARS = b"\n\r"
# We always push a trailing `\r` into the next decode iteration.
if self.trailing_cr:
text = b"\r" + text
self.trailing_cr = False
if text.endswith(b"\r"):
self.trailing_cr = True
text = text[:-1]
if not text:
return []
if self.trailing_cr and text.startswith(b"\n"):
text = text[1:]
self.trailing_cr = text.endswith(b"\r")
if not text:
# NOTE: the edge case input of empty text doesn't occur in practice,
# because other httpx internals filter out this value
return [] # pragma: no cover
return []
trailing_newline = text[-1] in NEWLINE_CHARS
lines = cast(list[BytesLike], text.splitlines())
@@ -66,7 +62,7 @@ class BytesLineDecoder:
return lines
def flush(self) -> list[BytesLike]:
if not self.buffer and not self.trailing_cr:
if not self.buffer:
return []
lines: list[BytesLike] = [self.buffer]
@@ -76,40 +72,66 @@ class BytesLineDecoder:
class SSEDecoder:
def __init__(self) -> None:
def __init__(self, *, last_event_id: str | None = None) -> None:
self._event = ""
self._data = bytearray()
self._last_event_id = ""
self._last_event_id = last_event_id
self._retry: int | None = None
self._at_stream_start = True
self._has_data = False
self._id_changed = False
@property
def has_data(self) -> bool:
"""Whether the last decoded block contained a data field."""
return self._has_data
@property
def last_event_id(self) -> str | None:
"""Return the last event identifier that was seen."""
"""Return the last event identifier that was seen.
return self._last_event_id or None
`None` until an ID is supplied or received, and `""` after an empty
one, which resets the reconnect cursor.
"""
return self._last_event_id
def decode(self, line: bytes) -> StreamPart | None:
# See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
if self._at_stream_start:
self._at_stream_start = False
line = line.removeprefix(b"\xef\xbb\xbf")
if not line:
self._has_data = bool(self._data)
if (
not self._event
and not self._data
and not self._last_event_id
and not self._id_changed
and self._retry is None
):
return None
body = self._data[:-1]
data = None
if body and not body.isspace():
try:
data = orjson.loads(body)
except orjson.JSONDecodeError:
data = orjson.loads(body.decode(errors="replace"))
sse = StreamPart(
event=self._event,
data=orjson.loads(self._data) if self._data else None, # ty: ignore[invalid-argument-type]
id=self.last_event_id,
data=data, # ty: ignore[invalid-argument-type]
id=self._last_event_id or None,
)
# NOTE: as per the SSE spec, do not reset last_event_id.
self._event = ""
self._data = bytearray()
self._retry = None
self._id_changed = False
return sse
@@ -122,14 +144,16 @@ class SSEDecoder:
value = value[1:]
if fieldname == b"event":
self._event = value.decode()
self._event = value.decode(errors="replace")
elif fieldname == b"data":
self._data.extend(value)
self._data.extend(b"\n")
elif fieldname == b"id":
if b"\0" in value:
pass
else:
self._last_event_id = value.decode()
self._last_event_id = value.decode(errors="replace")
self._id_changed = True
elif fieldname == b"retry":
with contextlib.suppress(TypeError, ValueError):
self._retry = int(value)
@@ -15,7 +15,6 @@ from __future__ import annotations
import asyncio
import contextlib
import logging
import random
from collections import OrderedDict
from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable
from dataclasses import dataclass, field
@@ -23,6 +22,7 @@ from typing import Any
from langchain_protocol import Event, SubscribeParams
from langgraph_sdk._shared.utilities import _reconnect_delay
from langgraph_sdk.stream.transport import AsyncProtocolTransport, EventStreamHandle
# ---------------------------------------------------------------------------
@@ -275,11 +275,13 @@ class StreamController:
async def _reconnect_sleep(self, attempt: int) -> None:
"""Sleep with exponential backoff and jitter for reconnect attempt *attempt*."""
base = self._reconnect_backoff_base
cap = self._reconnect_backoff_cap
delay = min(cap, base * (2**attempt))
jitter = random.uniform(0, delay * 0.25)
await asyncio.sleep(delay + jitter)
await asyncio.sleep(
_reconnect_delay(
attempt,
base=self._reconnect_backoff_base,
cap=self._reconnect_backoff_cap,
)
)
async def _reconnect_shared_stream(self) -> bool:
"""Attempt to reopen the shared stream after a transport drop.
@@ -4,7 +4,6 @@ from __future__ import annotations
import contextlib
import logging
import random
import threading
import time
from dataclasses import dataclass, field
@@ -13,6 +12,7 @@ from typing import Any
from langchain_protocol import Event, SubscribeParams
from langgraph_sdk._shared.utilities import _reconnect_delay
from langgraph_sdk.stream.errors import (
is_non_retryable_stream_error,
stream_error_frame,
@@ -282,11 +282,13 @@ class SyncStreamController:
def _reconnect_sleep(self, attempt: int) -> None:
"""Sleep with exponential backoff + jitter before a reconnect attempt."""
base = self._reconnect_backoff_base
cap = self._reconnect_backoff_cap
delay = min(cap, base * (2**attempt))
jitter = random.uniform(0, delay * 0.25)
time.sleep(delay + jitter)
time.sleep(
_reconnect_delay(
attempt,
base=self._reconnect_backoff_base,
cap=self._reconnect_backoff_cap,
)
)
def _reconnect_shared_stream(self) -> bool:
"""Attempt to reopen the shared stream after a transport drop.
+17
View File
@@ -0,0 +1,17 @@
import asyncio
import time
import pytest
@pytest.fixture
def no_sleep(monkeypatch):
"""Record backoff sleeps without actually waiting."""
sleeps: list[float] = []
async def fake_async_sleep(d):
sleeps.append(d)
monkeypatch.setattr(asyncio, "sleep", fake_async_sleep)
monkeypatch.setattr(time, "sleep", lambda d: sleeps.append(d))
return sleeps
+44
View File
@@ -1,7 +1,9 @@
from __future__ import annotations
import ast
import inspect
import re
from pathlib import Path
import pytest
@@ -128,3 +130,45 @@ def test_sync_api_matches_async(async_cls, sync_cls):
f"Return annotation mismatch for {async_cls.__name__}.{name}: "
f"async={a_ret}, sync={s_ret}"
)
# Stateless overloads (`thread_id: None`) leave out the thread-only parameters,
# and stateful ones (`thread_id: str`) leave out `on_completion`, which only
# applies to stateless runs.
_RUN_OVERLOAD_OMISSIONS = {
"None": {"checkpoint", "checkpoint_id", "multitask_strategy"},
"str": {"on_completion"},
}
@pytest.mark.parametrize("client_cls", [RunsClient, SyncRunsClient])
def test_run_overloads_accept_implementation_params(client_cls) -> None:
tree = ast.parse(Path(inspect.getfile(client_cls)).read_text())
cls = next(
node
for node in tree.body
if isinstance(node, ast.ClassDef) and node.name == client_cls.__name__
)
defs = [
node
for node in cls.body
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
and node.name in {"stream", "create", "wait"}
]
impl_params = {
node.name: {arg.arg for arg in node.args.kwonlyargs}
for node in defs
if not node.decorator_list
}
gaps = []
for stub in defs:
if not stub.decorator_list:
continue
thread_id = next(arg for arg in stub.args.args if arg.arg == "thread_id")
assert thread_id.annotation is not None
kind = ast.unparse(thread_id.annotation)
expected = impl_params[stub.name] - _RUN_OVERLOAD_OMISSIONS[kind]
if missing := expected - {arg.arg for arg in stub.args.kwonlyargs}:
gaps.append(f"{stub.name}(thread_id: {kind}) line {stub.lineno}: {missing}")
assert not gaps, gaps
+41
View File
@@ -1,8 +1,49 @@
import ast
import inspect
import pytest
from langgraph_sdk import Auth
@pytest.mark.parametrize(
"kwargs",
[{}, {"actions": "read"}, {"resources": None}],
)
def test_global_handler_factory_accepts_optional_resources(kwargs) -> None:
auth = Auth()
tree = ast.parse(inspect.getsource(type(auth.on)))
cls = tree.body[0]
assert isinstance(cls, ast.ClassDef)
factory_overload = next(
node
for node in cls.body
if isinstance(node, ast.FunctionDef)
and node.name == "__call__"
and node.decorator_list
and any(arg.arg == "resources" for arg in node.args.kwonlyargs)
)
resources = next(
arg for arg in factory_overload.args.kwonlyargs if arg.arg == "resources"
)
assert resources.annotation is not None
assert ast.unparse(resources.annotation) == "str | Sequence[str] | None"
default = factory_overload.args.kw_defaults[0]
assert isinstance(default, ast.Constant) and default.value is None
@auth.on(**kwargs)
async def handler(ctx, value):
del ctx, value
return None
if kwargs.get("actions"):
assert auth._handlers == {("*", "read"): [handler]}
assert auth._global_handlers == []
else:
assert auth._global_handlers == [handler]
assert auth._handlers == {}
def test_handler_multiple_resources_and_actions() -> None:
auth = Auth()
+808 -1
View File
@@ -1,10 +1,11 @@
from __future__ import annotations
from collections.abc import Iterator, Sequence
from collections.abc import Callable, Iterator, Sequence
from pathlib import Path
from typing import Any
import httpx
import orjson
import pytest
from typing_extensions import assert_type
@@ -12,6 +13,7 @@ from langgraph_sdk._async.runs import _wrap_stream_v2
from langgraph_sdk._shared.utilities import _sse_to_v2_dict
from langgraph_sdk._sync.runs import _wrap_stream_v2_sync
from langgraph_sdk.client import HttpClient, SyncHttpClient
from langgraph_sdk.errors import InternalServerError, NotFoundError
from langgraph_sdk.schema import (
CheckpointPayload,
CheckpointsStreamPart,
@@ -110,6 +112,129 @@ def test_stream_sse():
assert len(parts) == 79
def test_sse_decoder_joins_data_lines_with_a_newline():
decoder = SSEDecoder()
for line in (b"data: [1,", b"data: 2]"):
decoder.decode(line)
assert decoder.decode(b"") == StreamPart(event="", data=[1, 2], id=None) # ty: ignore[invalid-argument-type]
for line in (b"data: [1", b"data: 0]"):
decoder.decode(line)
with pytest.raises(orjson.JSONDecodeError):
decoder.decode(b"")
def test_sse_decoder_strips_a_leading_bom():
decoder = SSEDecoder()
for line in (b"\xef\xbb\xbfevent: values", b"data: 1"):
decoder.decode(line)
assert decoder.decode(b"") == StreamPart(event="values", data=1, id=None) # ty: ignore[invalid-argument-type]
def test_control_blocks_do_not_repeat_after_a_cursor_update():
decoder = SSEDecoder()
decoder.decode(b"id: cursor")
assert decoder.decode(b"") == StreamPart(event="", data=None, id="cursor") # ty: ignore[invalid-argument-type]
decoder.decode(b": heartbeat")
assert decoder.decode(b"") is None
assert decoder.decode(b"") is None
decoder.decode(b"id:")
assert decoder.decode(b"") == StreamPart(event="", data=None, id=None) # ty: ignore[invalid-argument-type]
assert decoder.last_event_id == ""
assert decoder.decode(b"") is None
def test_cr_lines_are_emitted_without_waiting_for_another_chunk():
decoder = BytesLineDecoder()
assert decoder.decode(b"data: 1\r\r") == [b"data: 1", b""]
assert decoder.decode(b"\n") == []
assert decoder.decode(b"data: 2\r") == [b"data: 2"]
assert decoder.decode(b"") == []
assert decoder.decode(b"\n\r") == [b""]
assert decoder.flush() == []
def test_empty_data_lines_preserve_their_newlines():
decoder = SSEDecoder()
for line in (b"data", b"data: 1", b"data:"):
decoder.decode(line)
assert decoder._data == b"\n1\n\n"
assert decoder.decode(b"") == StreamPart(event="", data=1, id=None) # ty: ignore[invalid-argument-type]
decoder.decode(b"data:")
decoder.decode(b"data:")
assert decoder.decode(b"") == StreamPart(event="", data=None, id=None) # ty: ignore[invalid-argument-type]
assert decoder.has_data
assert decoder.decode(b"") is None
assert not decoder.has_data
def test_malformed_utf8_is_replaced_in_fields_and_json_strings():
decoder = SSEDecoder()
for line in (b"event: \xff", b"id: \xff", b'data: "\xff"'):
decoder.decode(line)
assert decoder.decode(b"") == StreamPart(event="\ufffd", data="\ufffd", id="\ufffd") # ty: ignore[invalid-argument-type]
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_null_and_empty_data_are_not_dropped(asynchronous: bool):
def handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(
200,
headers={"Content-Type": "text/event-stream"},
content=b"data: null\n\ndata:\n\n",
)
transport = httpx.MockTransport(handler)
if asynchronous:
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
parts = [part async for part in HttpClient(client).stream("/stream", "GET")]
else:
with httpx.Client(
transport=transport, base_url="https://example.com"
) as client:
parts = list(SyncHttpClient(client).stream("/stream", "GET"))
assert parts == [StreamPart(event="", data=None), StreamPart(event="", data=None)] # ty: ignore[invalid-argument-type]
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_cursor_is_inherited_after_reconnect(asynchronous: bool):
requests: list[httpx.Request] = []
def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
if len(requests) == 1:
stream_cls = AsyncListByteStream if asynchronous else ListByteStream
return httpx.Response(
200,
headers={"Content-Type": "text/event-stream", "Location": "/reconnect"},
stream=stream_cls(
[b"id: cursor\nevent: values\ndata: 1\n\n"],
httpx.RemoteProtocolError("incomplete chunked read"),
),
)
assert (b"Last-Event-ID", b"cursor") in request.headers.raw
return httpx.Response(
200,
headers={"Content-Type": "text/event-stream"},
content=b"event: values\ndata: 2\n\n",
)
transport = httpx.MockTransport(handler)
if asynchronous:
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
parts = [part async for part in HttpClient(client).stream("/stream", "GET")]
else:
with httpx.Client(
transport=transport, base_url="https://example.com"
) as client:
parts = list(SyncHttpClient(client).stream("/stream", "GET"))
assert [part.id for part in parts] == ["cursor", "cursor"]
# --- HTTP client streaming ---
@@ -156,6 +281,7 @@ def test_sync_http_client_stream_flushes_trailing_event():
assert parts == [StreamPart(event="foo", data={"bar": 1})]
@pytest.mark.usefixtures("no_sleep")
def test_sync_http_client_stream_recovers_after_disconnect():
reconnect_path = "/reconnect"
first_chunks = [
@@ -221,6 +347,7 @@ def test_sync_http_client_stream_recovers_after_disconnect():
@pytest.mark.asyncio
@pytest.mark.usefixtures("no_sleep")
async def test_http_client_stream_recovers_after_disconnect():
reconnect_path = "/reconnect"
first_chunks = [
@@ -292,6 +419,686 @@ async def test_http_client_stream_recovers_after_disconnect():
]
def _drop_after(chunks: list[bytes], stream_cls: type, reconnects: list[httpx.Request]):
def handler(request: httpx.Request) -> httpx.Response:
if request.method == "POST":
return httpx.Response(
200,
headers={"Content-Type": "text/event-stream", "Location": "/reconnect"},
stream=stream_cls(
chunks, httpx.RemoteProtocolError("incomplete chunked read")
),
)
reconnects.append(request)
return httpx.Response(
200,
headers={"Content-Type": "text/event-stream"},
stream=stream_cls([b"event: end\ndata: null\n\n"]),
)
return handler
def _sync_reconnect(
chunks: list[bytes], headers: dict[str, str] | None
) -> httpx.Request:
reconnects: list[httpx.Request] = []
transport = httpx.MockTransport(_drop_after(chunks, ListByteStream, reconnects))
with httpx.Client(transport=transport, base_url="https://example.com") as client:
list(SyncHttpClient(client).stream("/stream", "POST", json={}, headers=headers))
return reconnects[0]
async def _async_reconnect(
chunks: list[bytes], headers: dict[str, str] | None
) -> httpx.Request:
reconnects: list[httpx.Request] = []
transport = httpx.MockTransport(
_drop_after(chunks, AsyncListByteStream, reconnects)
)
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
[
part
async for part in HttpClient(client).stream(
"/stream", "POST", json={}, headers=headers
)
]
return reconnects[0]
_EMPTY_IDS = pytest.mark.parametrize(
"empty_id",
[b"id:\nevent: values\ndata: 2\n\n", b"id:\n\n"],
ids=["with-an-event", "alone"],
)
_CALLER_CURSORS = pytest.mark.parametrize(
"headers",
[None, {"Last-Event-ID": "5"}, {"last-event-id": "5"}],
ids=["no-caller-cursor", "caller-cursor", "lowercase-caller-cursor"],
)
@_EMPTY_IDS
@_CALLER_CURSORS
@pytest.mark.usefixtures("no_sleep")
def test_sync_stream_reconnects_without_a_cursor_after_an_empty_id(
empty_id: bytes, headers: dict[str, str] | None
):
with pytest.warns(UserWarning, match="Last-Event-ID"):
reconnect = _sync_reconnect(
[b"id: 1\nevent: values\ndata: 1\n\n", empty_id], headers
)
assert "last-event-id" not in reconnect.headers
@_EMPTY_IDS
@_CALLER_CURSORS
@pytest.mark.usefixtures("no_sleep")
async def test_async_stream_reconnects_without_a_cursor_after_an_empty_id(
empty_id: bytes, headers: dict[str, str] | None
):
with pytest.warns(UserWarning, match="Last-Event-ID"):
reconnect = await _async_reconnect(
[b"id: 1\nevent: values\ndata: 1\n\n", empty_id], headers
)
assert "last-event-id" not in reconnect.headers
@pytest.mark.usefixtures("no_sleep")
def test_sync_stream_id_replaces_a_lowercase_caller_cursor():
reconnect = _sync_reconnect(
[b"id: 1\nevent: values\ndata: 1\n\n"], {"last-event-id": "5"}
)
assert reconnect.headers.get_list("last-event-id") == ["1"]
@pytest.mark.usefixtures("no_sleep")
async def test_async_stream_id_replaces_a_lowercase_caller_cursor():
reconnect = await _async_reconnect(
[b"id: 1\nevent: values\ndata: 1\n\n"], {"last-event-id": "5"}
)
assert reconnect.headers.get_list("last-event-id") == ["1"]
@pytest.mark.usefixtures("no_sleep")
def test_sync_stream_keeps_the_caller_cursor_until_the_stream_sends_an_id():
reconnect = _sync_reconnect([b"event: values\ndata: 1\n\n"], {"Last-Event-ID": "5"})
assert reconnect.headers.get_list("last-event-id") == ["5"]
@pytest.mark.usefixtures("no_sleep")
async def test_async_stream_keeps_the_caller_cursor_until_the_stream_sends_an_id():
reconnect = await _async_reconnect(
[b"event: values\ndata: 1\n\n"], {"Last-Event-ID": "5"}
)
assert reconnect.headers.get_list("last-event-id") == ["5"]
# A reconnect that sends no Last-Event-ID can't replay what was sent while it was
# disconnected, so it warns.
_RECONNECT_CURSORS = pytest.mark.parametrize(
("chunks", "headers", "warns"),
[
([b"event: values\ndata: 1\n\n"], None, True),
([b"id: 1\nevent: values\ndata: 1\n\n"], None, False),
([b"event: values\ndata: 1\n\n"], {"Last-Event-ID": "5"}, False),
],
ids=["no-cursor", "stream-cursor", "caller-cursor"],
)
@_RECONNECT_CURSORS
@pytest.mark.usefixtures("no_sleep")
def test_sync_reconnect_warns_only_without_a_cursor(
chunks: list[bytes],
headers: dict[str, str] | None,
warns: bool,
recwarn: pytest.WarningsRecorder,
):
_sync_reconnect(chunks, headers)
assert any("Last-Event-ID" in str(w.message) for w in recwarn) is warns
@_RECONNECT_CURSORS
@pytest.mark.usefixtures("no_sleep")
async def test_async_reconnect_warns_only_without_a_cursor(
chunks: list[bytes],
headers: dict[str, str] | None,
warns: bool,
recwarn: pytest.WarningsRecorder,
):
await _async_reconnect(chunks, headers)
assert any("Last-Event-ID" in str(w.message) for w in recwarn) is warns
# --- reconnects survive a server restart or a deploy ---
SSE = {"Content-Type": "text/event-stream"}
DROP = httpx.RemoteProtocolError("incomplete chunked read")
def _event(i: int) -> bytes:
return f"id: {i}\nevent: values\ndata: {i}\n\n".encode()
def _scripted(steps: list[Callable[[httpx.Request], httpx.Response]]):
"""Handler that plays one step per request and records each request."""
seen: list[httpx.Request] = []
def handler(request: httpx.Request) -> httpx.Response:
seen.append(request)
if len(seen) > len(steps):
raise AssertionError("unexpected request")
return steps[len(seen) - 1](request)
return handler, seen
def _ok(stream_cls, chunks, *, drop=False, location=None):
headers = dict(SSE)
if location:
headers["Location"] = location
def step(_request):
return httpx.Response(
200, headers=headers, stream=stream_cls(chunks, DROP if drop else None)
)
return step
def _status(code):
return lambda request: httpx.Response(
code, json={"detail": "upstream"}, request=request
)
def _connect_error(request):
raise httpx.ConnectError("[Errno 61] Connection refused", request=request)
def _not_sse(request):
return httpx.Response(
200, headers={"Content-Type": "text/html"}, text="<html>", request=request
)
def _transient_steps(stream_cls, failure):
"""POST drops after event 1; first reconnect fails; second succeeds."""
return [
_ok(stream_cls, [_event(1)], drop=True, location="/reconnect"),
failure,
_ok(stream_cls, [_event(2), b"event: end\ndata: null\n\n"]),
]
def _long_stream_steps(stream_cls, drops):
"""POST + `drops` reconnects, each delivering one event then dropping."""
steps = [_ok(stream_cls, [_event(0)], drop=True, location="/reconnect")]
for i in range(1, drops):
steps.append(_ok(stream_cls, [_event(i)], drop=True))
steps.append(_ok(stream_cls, [_event(drops), b"event: end\ndata: null\n\n"]))
return steps
def _run_sync(steps, headers=None):
handler, seen = _scripted(steps)
with httpx.Client(
transport=httpx.MockTransport(handler), base_url="https://example.com"
) as client:
parts = list(
SyncHttpClient(client).stream("/stream", "POST", json={}, headers=headers)
)
return parts, seen
async def _run_async(steps, headers=None):
sync_handler, seen = _scripted(steps)
async def handler(request):
return sync_handler(request)
async with httpx.AsyncClient(
transport=httpx.MockTransport(handler), base_url="https://example.com"
) as client:
parts = [
p
async for p in HttpClient(client).stream(
"/stream", "POST", json={}, headers=headers
)
]
return parts, seen
FAILURES = {
"429": _status(429),
"502": _status(502),
"503": _status(503),
"connect_error": _connect_error,
"not_sse": _not_sse,
}
# A reconnect that hits a 429 / 502 / 503 / connect error / non-SSE body, then
# succeeds, should recover.
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("failure", FAILURES.values(), ids=FAILURES.keys())
def test_sync_reconnect_recovers_from_transient_failure(failure):
parts, seen = _run_sync(_transient_steps(ListByteStream, failure))
assert [p.event for p in parts] == ["values", "values", "end"]
assert len(seen) == 3
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("failure", FAILURES.values(), ids=FAILURES.keys())
async def test_async_reconnect_recovers_from_transient_failure(failure):
parts, seen = await _run_async(_transient_steps(AsyncListByteStream, failure))
assert [p.event for p in parts] == ["values", "values", "end"]
assert len(seen) == 3
# Any other 4xx on a reconnect won't go away by retrying, so it raises.
def _client_error_steps(stream_cls):
return [
_ok(stream_cls, [_event(1)], drop=True, location="/reconnect"),
_status(404),
]
@pytest.mark.usefixtures("no_sleep")
def test_sync_reconnect_does_not_retry_a_client_error():
with pytest.raises(NotFoundError):
_run_sync(_client_error_steps(ListByteStream))
@pytest.mark.usefixtures("no_sleep")
async def test_async_reconnect_does_not_retry_a_client_error():
with pytest.raises(NotFoundError):
await _run_async(_client_error_steps(AsyncListByteStream))
# The first request isn't a reconnect, so it raises even when it names a Location.
def _first_request_503(request):
return httpx.Response(
503,
headers={"Location": "/reconnect"},
json={"detail": "upstream"},
request=request,
)
@pytest.mark.usefixtures("no_sleep")
def test_sync_first_request_failure_is_not_retried():
with pytest.raises(InternalServerError):
_run_sync([_first_request_503])
@pytest.mark.usefixtures("no_sleep")
async def test_async_first_request_failure_is_not_retried():
with pytest.raises(InternalServerError):
await _run_async([_first_request_503])
# A server that stays down gets five reconnects, backing off 1, 2, 4, 5 and 5s
# (each plus up to 25% jitter), so about 20s, before the stream gives up. The
# error it gives up with is caused by the last failure, so callers see why.
GIVE_UP = "Exceeded maximum SSE reconnection attempts"
def _server_down_steps(stream_cls):
return [
_ok(stream_cls, [_event(1)], drop=True, location="/reconnect"),
*[_status(502)] * 5,
]
def _assert_backed_off_for_about_20s(sleeps: list[float]) -> None:
assert len(sleeps) == 5
for slept, delay in zip(sleeps, [1, 2, 4, 5, 5], strict=True):
assert delay <= slept <= delay * 1.25
def test_sync_reconnect_gives_up_after_five_backed_off_attempts(no_sleep):
with pytest.raises(httpx.TransportError, match=GIVE_UP) as exc_info:
_run_sync(_server_down_steps(ListByteStream))
assert isinstance(exc_info.value.__cause__, InternalServerError)
_assert_backed_off_for_about_20s(no_sleep)
async def test_async_reconnect_gives_up_after_five_backed_off_attempts(no_sleep):
with pytest.raises(httpx.TransportError, match=GIVE_UP) as exc_info:
await _run_async(_server_down_steps(AsyncListByteStream))
assert isinstance(exc_info.value.__cause__, InternalServerError)
_assert_backed_off_for_about_20s(no_sleep)
# A long stream that drops 6 times, each reconnect succeeding and making
# progress, should not run out of attempts.
@pytest.mark.usefixtures("no_sleep")
def test_sync_long_stream_survives_many_spaced_out_drops():
parts, _ = _run_sync(_long_stream_steps(ListByteStream, drops=6))
assert [p.data for p in parts] == [0, 1, 2, 3, 4, 5, 6, None]
@pytest.mark.usefixtures("no_sleep")
async def test_async_long_stream_survives_many_spaced_out_drops():
parts, _ = await _run_async(_long_stream_steps(AsyncListByteStream, drops=6))
assert [p.data for p in parts] == [0, 1, 2, 3, 4, 5, 6, None]
# Each reconnect resumes after the newest event the stream got, so a reconnect
# loop on a resumable run replays only what it missed. A failed reconnect keeps
# the cursor for the next one.
RECONNECT_LOOPS = {
"progress_between_drops": (
lambda stream_cls: _long_stream_steps(stream_cls, drops=3),
[None, "0", "1", "2"],
),
"failed_reconnect": (
lambda stream_cls: _transient_steps(stream_cls, _status(502)),
[None, "1", "1"],
),
}
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize(
("steps", "cursors"), RECONNECT_LOOPS.values(), ids=RECONNECT_LOOPS.keys()
)
def test_sync_reconnect_loop_resumes_after_the_newest_event(steps, cursors):
_, seen = _run_sync(steps(ListByteStream))
assert [r.headers.get("last-event-id") for r in seen] == cursors
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize(
("steps", "cursors"), RECONNECT_LOOPS.values(), ids=RECONNECT_LOOPS.keys()
)
async def test_async_reconnect_loop_resumes_after_the_newest_event(steps, cursors):
_, seen = await _run_async(steps(AsyncListByteStream))
assert [r.headers.get("last-event-id") for r in seen] == cursors
# Reconnects should back off rather than hammer the server.
def test_sync_reconnect_backs_off(no_sleep):
_run_sync(_long_stream_steps(ListByteStream, drops=2))
assert len(no_sleep) == 2 and all(d > 0 for d in no_sleep)
async def test_async_reconnect_backs_off(no_sleep):
await _run_async(_long_stream_steps(AsyncListByteStream, drops=2))
assert len(no_sleep) == 2 and all(d > 0 for d in no_sleep)
# --- a run stream that closes cleanly before the run ends ---
RUN_STREAM = "/threads/t1/runs/r1/stream"
RUN = "/threads/t1/runs/r1"
END = b"event: end\ndata: null\n\n"
Step = Callable[[httpx.Request], httpx.Response]
def _run_status(status: str) -> Step:
return lambda request: httpx.Response(200, json={"status": status}, request=request)
def _clean_close_steps(stream_cls: type, closes: int, check: Step) -> list[Step]:
steps = [_ok(stream_cls, [_event(0)], location=RUN_STREAM)]
for i in range(1, closes + 1):
steps += [check, _ok(stream_cls, [_event(i)])]
return [*steps, _run_status("success")]
def _stalled_steps(stream_cls: type) -> list[Step]:
return [
_ok(stream_cls, [_event(1)], location=RUN_STREAM),
*[_run_status("running"), _ok(stream_cls, [])] * 5,
_run_status("running"),
]
def _requests(seen: list[httpx.Request]) -> list[tuple[str, str, str | None]]:
return [(r.method, r.url.path, r.headers.get("last-event-id")) for r in seen]
RESUMED = [
("POST", "/stream", None),
("GET", RUN, None),
("GET", RUN_STREAM, "0"),
("GET", RUN, None),
]
CHECKS_THAT_RECONNECT = {
"pending": _run_status("pending"),
"running": _run_status("running"),
"429": _status(429),
"503": _status(503),
"connect_error": _connect_error,
}
@pytest.mark.parametrize(
"check", CHECKS_THAT_RECONNECT.values(), ids=CHECKS_THAT_RECONNECT.keys()
)
def test_sync_clean_close_reconnects_after_check(check: Step, no_sleep: list[float]):
parts, seen = _run_sync(_clean_close_steps(ListByteStream, 1, check))
assert [p.data for p in parts] == [0, 1]
assert _requests(seen) == RESUMED
assert len(no_sleep) == 1
@pytest.mark.parametrize(
"check", CHECKS_THAT_RECONNECT.values(), ids=CHECKS_THAT_RECONNECT.keys()
)
async def test_async_clean_close_reconnects_after_check(
check: Step, no_sleep: list[float]
):
parts, seen = await _run_async(_clean_close_steps(AsyncListByteStream, 1, check))
assert [p.data for p in parts] == [0, 1]
assert _requests(seen) == RESUMED
assert len(no_sleep) == 1
@pytest.mark.usefixtures("no_sleep")
def test_sync_clean_closes_with_progress_do_not_run_out_of_attempts():
parts, _ = _run_sync(_clean_close_steps(ListByteStream, 6, _run_status("running")))
assert [p.data for p in parts] == [0, 1, 2, 3, 4, 5, 6]
@pytest.mark.usefixtures("no_sleep")
async def test_async_clean_closes_with_progress_do_not_run_out_of_attempts():
parts, _ = await _run_async(
_clean_close_steps(AsyncListByteStream, 6, _run_status("running"))
)
assert [p.data for p in parts] == [0, 1, 2, 3, 4, 5, 6]
CHECKS_THAT_END = {
"success": _run_status("success"),
"error": _run_status("error"),
"timeout": _run_status("timeout"),
"interrupted": _run_status("interrupted"),
"403": _status(403),
"404": _status(404),
"not_json": _not_sse,
"json_without_status": lambda request: httpx.Response(
200, json={}, request=request
),
}
CHECKED = [("POST", "/stream", None), ("GET", RUN, None)]
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("check", CHECKS_THAT_END.values(), ids=CHECKS_THAT_END.keys())
def test_sync_clean_close_ends_the_stream_after_check(check: Step):
parts, seen = _run_sync(
[_ok(ListByteStream, [_event(1)], location=RUN_STREAM), check]
)
assert [p.data for p in parts] == [1]
assert _requests(seen) == CHECKED
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("check", CHECKS_THAT_END.values(), ids=CHECKS_THAT_END.keys())
async def test_async_clean_close_ends_the_stream_after_check(check: Step):
parts, seen = await _run_async(
[_ok(AsyncListByteStream, [_event(1)], location=RUN_STREAM), check]
)
assert [p.data for p in parts] == [1]
assert _requests(seen) == CHECKED
NO_CHECK = {
"end": lambda s: [_ok(s, [_event(1), END], location=RUN_STREAM)],
"error": lambda s: [
_ok(s, [_event(1), b"event: error\ndata: {}\n\n"], location=RUN_STREAM)
],
"unterminated_end": lambda s: [
_ok(s, [_event(1), b"event: end\ndata: null\n"], location=RUN_STREAM)
],
"end_on_an_earlier_connection": lambda s: [
_ok(s, [_event(1), END], drop=True, location=RUN_STREAM),
_ok(s, []),
],
"thread_stream": lambda s: [_ok(s, [_event(1)], location="/threads/t1/stream")],
"threadless_run_location": lambda s: [
_ok(s, [_event(1)], location="/runs/r1/stream")
],
}
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("steps", NO_CHECK.values(), ids=NO_CHECK.keys())
def test_sync_stream_ends_without_a_run_check(steps: Callable[[type], list[Step]]):
_, seen = _run_sync(steps(ListByteStream))
assert [r.url.path for r in seen if not r.url.path.endswith("/stream")] == []
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("steps", NO_CHECK.values(), ids=NO_CHECK.keys())
async def test_async_stream_ends_without_a_run_check(
steps: Callable[[type], list[Step]],
):
_, seen = await _run_async(steps(AsyncListByteStream))
assert [r.url.path for r in seen if not r.url.path.endswith("/stream")] == []
def test_sync_clean_closes_without_progress_give_up_after_five_attempts(
no_sleep: list[float],
):
with pytest.raises(httpx.TransportError, match=GIVE_UP) as exc_info:
_run_sync(_stalled_steps(ListByteStream))
assert "still pending or running" in str(exc_info.value.__cause__)
_assert_backed_off_for_about_20s(no_sleep)
async def test_async_clean_closes_without_progress_give_up_after_five_attempts(
no_sleep: list[float],
):
with pytest.raises(httpx.TransportError, match=GIVE_UP) as exc_info:
await _run_async(_stalled_steps(AsyncListByteStream))
assert "still pending or running" in str(exc_info.value.__cause__)
_assert_backed_off_for_about_20s(no_sleep)
CHECK_URLS = {
"path": ("/threads/t1/runs/r1/stream", "https://example.com/threads/t1/runs/r1"),
"prefix_and_query": (
"/api/threads/t1/runs/r1/stream?stream_mode=values",
"https://example.com/api/threads/t1/runs/r1",
),
"absolute": (
"https://example.com/threads/t1/runs/r1/stream",
"https://example.com/threads/t1/runs/r1",
),
}
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize(
("location", "url"), CHECK_URLS.values(), ids=CHECK_URLS.keys()
)
def test_sync_run_check_gets_the_run_behind_the_stream_location(
location: str, url: str
):
_, seen = _run_sync(
[_ok(ListByteStream, [_event(1)], location=location), _run_status("success")]
)
assert str(seen[-1].url) == url
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize(
("location", "url"), CHECK_URLS.values(), ids=CHECK_URLS.keys()
)
async def test_async_run_check_gets_the_run_behind_the_stream_location(
location: str, url: str
):
_, seen = await _run_async(
[
_ok(AsyncListByteStream, [_event(1)], location=location),
_run_status("success"),
]
)
assert str(seen[-1].url) == url
CALLER_HEADERS = {"Authorization": "Bearer token", "Last-Event-ID": "0"}
BODY_AND_CURSOR_HEADERS = {"content-type", "content-length", "last-event-id"}
def _assert_run_check_headers(seen: list[httpx.Request]) -> None:
check = seen[-1]
assert check.url.path == RUN
assert check.headers["authorization"] == "Bearer token"
assert check.headers["accept"] == "application/json"
assert check.headers.keys() & BODY_AND_CURSOR_HEADERS == set()
@pytest.mark.usefixtures("no_sleep")
def test_sync_run_check_sends_caller_headers_and_accepts_json_without_body_or_cursor():
_, seen = _run_sync(
[_ok(ListByteStream, [_event(1)], location=RUN_STREAM), _run_status("success")],
headers=CALLER_HEADERS,
)
_assert_run_check_headers(seen)
@pytest.mark.usefixtures("no_sleep")
async def test_async_run_check_sends_caller_headers_and_accepts_json_without_body_or_cursor():
_, seen = await _run_async(
[
_ok(AsyncListByteStream, [_event(1)], location=RUN_STREAM),
_run_status("success"),
],
headers=CALLER_HEADERS,
)
_assert_run_check_headers(seen)
# --- _sse_to_v2_dict conversion ---
+1 -5
View File
@@ -120,9 +120,5 @@ def test_context_is_frozen():
def test_every_settings_key_is_optional():
assert PrioritySettings.__optional_keys__ == {
"priority",
"fairness_group",
"fairness_weight",
}
assert PrioritySettings.__optional_keys__ == {"priority", "fairness_group"}
assert PrioritySettings.__required_keys__ == frozenset()
+214
View File
@@ -0,0 +1,214 @@
from __future__ import annotations
import json
from collections.abc import AsyncIterator, Callable, Iterator, Sequence
from typing import Any
import httpx
import pytest
from langgraph_sdk.client import (
HttpClient,
RunsClient,
SyncHttpClient,
SyncRunsClient,
)
class _BodyDropsMidRead(httpx.SyncByteStream, httpx.AsyncByteStream):
def __iter__(self) -> Iterator[bytes]:
yield b'{"partial'
raise httpx.ReadError("connection dropped")
async def __aiter__(self) -> AsyncIterator[bytes]:
yield b'{"partial'
raise httpx.ReadError("connection dropped")
def _done(_request: httpx.Request) -> httpx.Response:
return httpx.Response(200, json={"done": True})
def _wait_handler(
requests: list[httpx.Request],
reconnects: Sequence[Callable[[httpx.Request], httpx.Response]] = (_done,),
):
"""POST drops mid-body; the reconnects answer from `reconnects` in order."""
def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
if request.method == "POST":
return httpx.Response(
200,
headers={"Location": "/threads/t1/runs/r1/join"},
stream=_BodyDropsMidRead(),
)
return reconnects[len(requests) - 2](request)
return handler
def _assert_bodyless_get(request: httpx.Request) -> None:
assert request.method == "GET"
assert request.url.path == "/threads/t1/runs/r1/join"
assert "content-length" not in request.headers, request.headers
assert "content-type" not in request.headers, request.headers
assert request.read() == b""
@pytest.mark.usefixtures("no_sleep")
def test_sync_wait_reconnects_with_a_bodyless_get():
requests: list[httpx.Request] = []
transport = httpx.MockTransport(_wait_handler(requests))
with httpx.Client(transport=transport, base_url="https://example.com") as client:
runs = SyncRunsClient(SyncHttpClient(client))
with pytest.warns(UserWarning, match="attempting reconnect"):
result = runs.wait("t1", "agent", input={"question": "hi"})
assert result == {"done": True}
_assert_bodyless_get(requests[1])
@pytest.mark.usefixtures("no_sleep")
async def test_async_wait_reconnects_with_a_bodyless_get():
requests: list[httpx.Request] = []
transport = httpx.MockTransport(_wait_handler(requests))
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
runs = RunsClient(HttpClient(client))
with pytest.warns(UserWarning, match="attempting reconnect"):
result = await runs.wait("t1", "agent", input={"question": "hi"})
assert result == {"done": True}
_assert_bodyless_get(requests[1])
def _connect_error(request: httpx.Request) -> httpx.Response:
raise httpx.ConnectError("[Errno 61] Connection refused", request=request)
async def _wait(
handler: Callable[[httpx.Request], httpx.Response], *, asynchronous: bool
) -> Any:
transport = httpx.MockTransport(handler)
if asynchronous:
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
return await RunsClient(HttpClient(client)).wait("t1", "agent", input={})
with httpx.Client(transport=transport, base_url="https://example.com") as client:
return SyncRunsClient(SyncHttpClient(client)).wait("t1", "agent", input={})
# What a server restart or a deploy looks like to the reconnect GET: each one is
# retried after a backoff.
RECONNECT_FAILURES = {
"429": lambda request: httpx.Response(429, request=request),
"503": lambda request: httpx.Response(503, request=request),
"connect_error": _connect_error,
"body_drops": lambda _request: httpx.Response(
200,
headers={"Location": "/threads/t1/runs/r1/join"},
stream=_BodyDropsMidRead(),
),
}
@pytest.mark.parametrize("asynchronous", [False, True])
@pytest.mark.parametrize(
"failure", RECONNECT_FAILURES.values(), ids=RECONNECT_FAILURES.keys()
)
async def test_wait_retries_a_failed_reconnect_after_a_backoff(
failure, asynchronous, no_sleep
):
requests: list[httpx.Request] = []
with pytest.warns(UserWarning, match="attempting reconnect"):
result = await _wait(
_wait_handler(requests, [failure, _done]), asynchronous=asynchronous
)
assert result == {"done": True}
assert len(requests) == 3
assert len(no_sleep) == 2 and min(no_sleep) >= 1, no_sleep
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_wait_raises_after_five_failed_reconnects(asynchronous):
requests: list[httpx.Request] = []
with (
pytest.warns(UserWarning, match="attempting reconnect"),
pytest.raises(httpx.HTTPStatusError, match="503"),
):
await _wait(
_wait_handler(requests, [RECONNECT_FAILURES["503"]] * 5),
asynchronous=asynchronous,
)
assert len(requests) == 6
@pytest.mark.usefixtures("no_sleep")
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_wait_does_not_retry_a_client_error(asynchronous):
requests: list[httpx.Request] = []
with (
pytest.warns(UserWarning, match="attempting reconnect"),
pytest.raises(httpx.HTTPStatusError, match="404"),
):
await _wait(
_wait_handler(
requests, [lambda request: httpx.Response(404, request=request)]
),
asynchronous=asynchronous,
)
assert len(requests) == 2
def test_sync_wait_raises_the_run_error():
requests: list[httpx.Request] = []
def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
return httpx.Response(
200, json={"__error__": {"error": "RuntimeError", "message": "boom"}}
)
transport = httpx.MockTransport(handler)
with httpx.Client(transport=transport, base_url="https://example.com") as client:
runs = SyncRunsClient(SyncHttpClient(client))
with pytest.raises(Exception, match="RuntimeError: boom"):
runs.wait("t1", "agent", input={}, raise_error=True)
assert "raise_error" not in json.loads(requests[0].content)
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_wait_returns_the_run_error_without_raise_error(asynchronous):
requests: list[httpx.Request] = []
run_error = {"__error__": {"error": "RuntimeError", "message": "boom"}}
def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
return httpx.Response(200, json=run_error)
transport = httpx.MockTransport(handler)
if asynchronous:
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
result = await RunsClient(HttpClient(client)).wait(
"t1", "agent", input={}, raise_error=False
)
else:
with httpx.Client(
transport=transport, base_url="https://example.com"
) as client:
result = SyncRunsClient(SyncHttpClient(client)).wait(
"t1", "agent", input={}, raise_error=False
)
assert result == run_error
assert "raise_error" not in json.loads(requests[0].content)
+126
View File
@@ -11,6 +11,14 @@ from langgraph_sdk.client import (
SyncThreadsClient,
ThreadsClient,
)
from langgraph_sdk.schema import Checkpoint
CHECKPOINT: Checkpoint = {
"thread_id": "thread_123",
"checkpoint_ns": "",
"checkpoint_id": "cp_1",
"checkpoint_map": None,
}
@pytest.mark.asyncio
@@ -56,3 +64,121 @@ def test_sync_threads_update_return_minimal():
)
assert result is None
@pytest.mark.asyncio
async def test_async_threads_get_state_select():
requests: list[httpx.Request] = []
async def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
return httpx.Response(200, json={})
transport = httpx.MockTransport(handler)
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
threads_client = ThreadsClient(HttpClient(client))
await threads_client.get_state("thread_123", select=["values.messages", "next"])
await threads_client.get_state(
"thread_123", checkpoint_id="cp_1", select=["values.messages"]
)
await threads_client.get_state(
"thread_123",
checkpoint=CHECKPOINT,
select=["values.messages"],
)
await threads_client.get_state("thread_123")
latest, by_id, by_checkpoint, default = requests
assert latest.method == "GET"
assert latest.url.path == "/threads/thread_123/state"
assert latest.url.params.get_list("select") == ["values.messages", "next"]
assert by_id.method == "GET"
assert by_id.url.path == "/threads/thread_123/state/cp_1"
assert by_id.url.params.get_list("select") == ["values.messages"]
assert by_checkpoint.method == "POST"
assert by_checkpoint.url.path == "/threads/thread_123/state/checkpoint"
assert json.loads(by_checkpoint.content) == {
"checkpoint": CHECKPOINT,
"subgraphs": False,
"select": ["values.messages"],
}
assert "select" not in default.url.params
@pytest.mark.asyncio
async def test_async_threads_get_history_select():
bodies: list[dict] = []
async def handler(request: httpx.Request) -> httpx.Response:
assert request.method == "POST"
assert request.url.path == "/threads/thread_123/history"
bodies.append(json.loads(request.content))
return httpx.Response(200, json=[])
transport = httpx.MockTransport(handler)
async with httpx.AsyncClient(
transport=transport, base_url="https://example.com"
) as client:
threads_client = ThreadsClient(HttpClient(client))
await threads_client.get_history("thread_123", select=["values.messages"])
await threads_client.get_history("thread_123")
assert bodies == [{"limit": 10, "select": ["values.messages"]}, {"limit": 10}]
def test_sync_threads_get_state_select():
requests: list[httpx.Request] = []
def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
return httpx.Response(200, json={})
transport = httpx.MockTransport(handler)
with httpx.Client(transport=transport, base_url="https://example.com") as client:
threads_client = SyncThreadsClient(SyncHttpClient(client))
threads_client.get_state("thread_123", select=["values.messages", "next"])
threads_client.get_state(
"thread_123", checkpoint_id="cp_1", select=["values.messages"]
)
threads_client.get_state(
"thread_123",
checkpoint=CHECKPOINT,
select=["values.messages"],
)
threads_client.get_state("thread_123")
latest, by_id, by_checkpoint, default = requests
assert latest.method == "GET"
assert latest.url.path == "/threads/thread_123/state"
assert latest.url.params.get_list("select") == ["values.messages", "next"]
assert by_id.method == "GET"
assert by_id.url.path == "/threads/thread_123/state/cp_1"
assert by_id.url.params.get_list("select") == ["values.messages"]
assert by_checkpoint.method == "POST"
assert by_checkpoint.url.path == "/threads/thread_123/state/checkpoint"
assert json.loads(by_checkpoint.content) == {
"checkpoint": CHECKPOINT,
"subgraphs": False,
"select": ["values.messages"],
}
assert "select" not in default.url.params
def test_sync_threads_get_history_select():
bodies: list[dict] = []
def handler(request: httpx.Request) -> httpx.Response:
assert request.method == "POST"
assert request.url.path == "/threads/thread_123/history"
bodies.append(json.loads(request.content))
return httpx.Response(200, json=[])
transport = httpx.MockTransport(handler)
with httpx.Client(transport=transport, base_url="https://example.com") as client:
threads_client = SyncThreadsClient(SyncHttpClient(client))
threads_client.get_history("thread_123", select=["values.messages"])
threads_client.get_history("thread_123")
assert bodies == [{"limit": 10, "select": ["values.messages"]}, {"limit": 10}]
+2 -2
View File
@@ -301,7 +301,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.16"
version = "1.2.19"
source = { editable = "../langgraph" }
dependencies = [
{ name = "langchain-core" },
@@ -385,7 +385,7 @@ test = [
[[package]]
name = "langgraph-checkpoint"
version = "4.3.0"
version = "4.3.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },