Compare commits

...
66 Commits
Author SHA1 Message Date
Eugene Yurtsev 18a2a2a832 x 2025-12-09 16:24:45 -05:00
Eugene Yurtsev 5ae0aff522 x 2025-12-09 16:24:38 -05:00
Eugene Yurtsev b7d068677c x 2025-12-09 11:28:15 -05:00
Eugene Yurtsev 706f3e981e x 2025-12-08 22:35:50 -05:00
Eugene Yurtsev 344ab65351 checkpoint sqlite 2025-12-08 21:24:00 -05:00
94698c8a34 feat: custom encryption at rest (#6482)
**Description:** 
This PR adds the Python SDK types necessary for langgraph platform users
to inject their own custom encryption-at-rest functions. See [docs
PR](https://github.com/langchain-ai/docs/pull/1715) for more details.

note: this PR adds a starlette dev dependency so that custom encryption
can access BaseUser information.

**Issue:**
required for LSD-172 

**Dependencies:**
- [depended upon by associated langgraph-api
changes](https://github.com/langchain-ai/langgraph-api/pull/1773)(this
PR must merge before that one)
- [docs PR](https://github.com/langchain-ai/docs/pull/1715)

**TODO:**
- [x] move docs to docs repo
- [x] bump package versions before merge

---------

Signed-off-by: Connor Braa <cwlbraa@langchain.dev>
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-06 00:10:17 +00:00
Mason DaughertyandGitHub 7d557cb865 chore: fix links for docs (#6538) 2025-12-05 23:09:41 +00:00
William FHandGitHub f211fadc2b release(sdk-py): Configure loopback client (#6536)
Will defer defaulting to deferred registration until a later date.
2025-12-05 11:56:40 -08:00
William FHandGitHub 024468c5dd chore: Bump lockfile (#6537) 2025-12-05 11:16:19 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7f08a6dafd chore(deps): bump js-yaml from 4.1.0 to 4.1.1 in /docs/_scripts/js_translation/codeblocks (#6452)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md">js-yaml's
changelog</a>.</em></p>
<blockquote>
<h2>[4.1.1] - 2025-11-12</h2>
<h3>Security</h3>
<ul>
<li>Fix prototype pollution issue in yaml merge (&lt;&lt;)
operator.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/nodeca/js-yaml/commit/cc482e775913e6625137572a3712d2826170e53a"><code>cc482e7</code></a>
4.1.1 released</li>
<li><a
href="https://github.com/nodeca/js-yaml/commit/50968b862e75866ef90e626572fe0b2f97b55f9f"><code>50968b8</code></a>
dist rebuild</li>
<li><a
href="https://github.com/nodeca/js-yaml/commit/d092d866031751cb27c12d93f3e2470ad74d678b"><code>d092d86</code></a>
lint fix</li>
<li><a
href="https://github.com/nodeca/js-yaml/commit/383665ff4248ec2192d1274e934462bb30426879"><code>383665f</code></a>
fix prototype pollution in merge (&lt;&lt;)</li>
<li><a
href="https://github.com/nodeca/js-yaml/commit/0d3ca7a27b03a6c974790a30a89e456007d62976"><code>0d3ca7a</code></a>
README.md: HTTP =&gt; HTTPS (<a
href="https://redirect.github.com/nodeca/js-yaml/issues/678">#678</a>)</li>
<li><a
href="https://github.com/nodeca/js-yaml/commit/49baadd52af887d2991e2c39a6639baa56d6c71b"><code>49baadd</code></a>
doc: 'empty' style option for !!null</li>
<li><a
href="https://github.com/nodeca/js-yaml/commit/ba3460eb9d3e4478edcbc29edabe17c2157fc9ce"><code>ba3460e</code></a>
Fix demo link (<a
href="https://redirect.github.com/nodeca/js-yaml/issues/618">#618</a>)</li>
<li>See full diff in <a
href="https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=js-yaml&package-manager=npm_and_yarn&previous-version=4.1.0&new-version=4.1.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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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>
2025-12-02 19:54:26 +00:00
William FHandGitHub 76203e2c20 chore: Sync langgraph.json schema (#6530) 2025-12-02 19:52:06 +00:00
b6cc022861 fix(checkpoint): InMemorySaver context managers should return self in… (#6529)
h/t to @lexi-k for openening. merged here to check/fix CI

Co-authored-by: lexi-k <69981673+lexi-k@users.noreply.github.com>
2025-12-02 11:47:41 -08:00
Mason DaughertyandGitHub 02bb7a436a chore: remove erroneous release title prefix (#6528) 2025-12-02 13:45:43 -05:00
Hunter LovellandGitHub 43d21e9c7d chore(ci): disable redirects build hook (#6527) 2025-12-02 08:43:57 -05:00
William FHandGitHub 55ed7d4b49 feat: Include pagination in assistants search response (#6526) 2025-12-01 20:18:26 -08:00
William FHandGitHub 2b78bf3bad fix: docstring for serializer protocol (#6525) 2025-12-01 18:14:12 +00:00
Mason DaughertyandGitHub 630bd9ab95 fix: release name should be same as package name (#6503) 2025-11-25 19:02:44 -05:00
William FHandGitHub b945b1f21e release(langgraph): 1.0.4 (#6502)
This patch release includes a couple of small fixes and improvements,
such as:
- Include interrupt information in the values stream mode (no longer
rely solely on the updates stream mode):
https://github.com/langchain-ai/langgraph/pull/6475
- Omit thread_id from configurable to allow using context for stateful
runs via RemoteGraph:
https://github.com/langchain-ai/langgraph/pull/6497
2025-11-25 20:27:49 +00:00
William FHandGitHub 4cb108ccd9 release(sdk-py): 0.2.11 (#6501) 2025-11-25 19:17:40 +00:00
Josh RogersandGitHub 23d78c2817 feat(sdk-py): add sentinel to skip auto loading api key on sdk client create (#6500)
**Description:** There are times a user might want to create the client,
but conditionally set the API key. For example, consider a complex auth
situation where the system has user callers using jwts and system
callers using API keys. This allows explicitly disabling the
auto-loading behavior of API keys in the client today, so no key is set.
**Issue:** N/A
**Dependencies:** None
**Twitter handle:** N/A
2025-11-25 11:10:56 -08:00
William FHandGitHub f8c1a323cc chore(sdk-py): Improve type-hinting of inputs (#6480) 2025-11-24 18:29:03 -08:00
William FHandGitHub b7e329cf0a chore: pop thread ID from configurable fields in remote graph (#6497)
Otherwise, you cannot use `context` with stateful runs, because the
server throws if you provide both configurable and context in a single
call (due to ambiguous parameters)
2025-11-24 16:09:08 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3f49249e72 chore(deps): bump actions/checkout from 5 to 6 (#6490)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to
6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>v6-beta by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2298">actions/checkout#2298</a></li>
<li>update readme/changelog for v6 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2311">actions/checkout#2311</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v5.0.0...v6.0.0">https://github.com/actions/checkout/compare/v5.0.0...v6.0.0</a></p>
<h2>v6-beta</h2>
<h2>What's Changed</h2>
<p>Updated persist-credentials to store the credentials under
<code>$RUNNER_TEMP</code> instead of directly in the local git
config.</p>
<p>This requires a minimum Actions Runner version of <a
href="https://github.com/actions/runner/releases/tag/v2.329.0">v2.329.0</a>
to access the persisted credentials for <a
href="https://docs.github.com/en/actions/tutorials/use-containerized-services/create-a-docker-container-action">Docker
container action</a> scenarios.</p>
<h2>v5.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v5...v5.0.1">https://github.com/actions/checkout/compare/v5...v5.0.1</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>V6.0.0</h2>
<ul>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
</ul>
<h2>V5.0.1</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<h2>V5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>V4.3.1</h2>
<ul>
<li>Port v6 cleanup to v4 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
</ul>
<h2>V4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable
version. by <a href="https://github.com/jww3"><code>@​jww3</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkout#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkout#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkout#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkout#1695</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/checkout/commit/1af3b93b6815bc44a9784bd300feb67ff0d1eeb3"><code>1af3b93</code></a>
update readme/changelog for v6 (<a
href="https://redirect.github.com/actions/checkout/issues/2311">#2311</a>)</li>
<li><a
href="https://github.com/actions/checkout/commit/71cf2267d89c5cb81562390fa70a37fa40b1305e"><code>71cf226</code></a>
v6-beta (<a
href="https://redirect.github.com/actions/checkout/issues/2298">#2298</a>)</li>
<li><a
href="https://github.com/actions/checkout/commit/069c6959146423d11cd0184e6accf28f9d45f06e"><code>069c695</code></a>
Persist creds to a separate file (<a
href="https://redirect.github.com/actions/checkout/issues/2286">#2286</a>)</li>
<li><a
href="https://github.com/actions/checkout/commit/ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493"><code>ff7abcd</code></a>
Update README to include Node.js 24 support details and requirements (<a
href="https://redirect.github.com/actions/checkout/issues/2248">#2248</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/checkout/compare/v5...v6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=5&new-version=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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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>
2025-11-24 13:41:21 -08:00
Andrew NguonlyandGitHub 78910dade0 chore: Bump version of sdk-py to 0.2.10 (#6496)
**Description:** Updating `sdk-py` version for release.
2025-11-24 12:27:16 -08:00
Andrew NguonlyandGitHub 33fbd1bb9c feat(sdk-py): Add name parameter to Assistants search API (#6483)
**Description:** The Agent Server API now supports searching for
assistants by name.
2025-11-24 10:58:54 -08:00
Mason DaughertyandGitHub d45f52ab0c chore: clean up some refs (#6487) 2025-11-23 02:13:30 -05:00
William FHandGitHub ed07d4a5d4 chore(sdk-py): Add more type checking. (#6479) 2025-11-20 17:40:23 -08:00
Caspar BroekhuizenandGitHub 2284a54b64 fix: interrupt stream mode values (#6475)
This PR improves the consistency of interrupt streaming. 

- when streaming with stream_mode values, the stream chunk now contains
the entire state alongside the interrupt:

```python
class State(TypedDict):
    robot_input: str
# at this point in time robot_input is already set to  "beep boop i am a robot"
app.stream(..., stream_mode="values")
# before
{"__interrupt__": (Interrupt(value="interrupt",))}}
# after
{"robot_input": "beep boop i am a robot", "__interrupt__": (Interrupt(value="interrupt"))}
```

- when streaming with stream_mode=["values", "updates"], interrupts are
surfaced in both an update stream chunk and the value stream chunk, when
previously we keep interrupt in values only if we request values mode
only

```python
class State(TypedDict):
    robot_input: str
# at this point in time robot_input is already set to  "beep boop i am a robot"
app.stream(..., stream_mode=["values", "updates"])
# before (interrupt would only emit on update chunk, there would be no values chunk)
("updates", {"__interrupt__": (Interrupt(value="interrupt",))}})
# after
("updates", {"__interrupt__": (Interrupt(value="interrupt",))}})
("values", {"robot_input": "beep boop i am a robot", "__interrupt__": (Interrupt(value="interrupt"))})
```

For housekeeping: this PR improves on this revert:
https://github.com/langchain-ai/langgraph/pull/6141
2025-11-20 14:23:09 -08:00
Sydney RunkleandGitHub f8006b2fee release: langgraph-prebuilt 1.0.5 (#6473) 2025-11-20 11:45:19 -05:00
Sydney RunkleandGitHub 2164b7daa3 fix: refactor injection logic to respect function signatures (#6468)
## overview

The main purpose of this is to respect tool signatures that request
injected args (like `ToolRuntime`) even when the explicitly specified
`args_schema` does not.

Ex in the following example, we should still inject `runtime` despite
its absence in `ArgsSchema`

```py
class ArgsSchema(BaseModel):
    some_arg: int = Field(...)

@tool(args_schema=ArgsSchema)
def my_tool(some_arg: int, runtime: ToolRuntime): ...
```

This is accompanied by
https://github.com/langchain-ai/langchain/pull/34051 which has tests
that pass w/ this change. This tests injection w/ `create_agent` (more
end to end than tests added in
https://github.com/langchain-ai/langchain/pull/33999.

This unblocks the injection of `ToolRuntime` into MCP tools which is
exciting bc that exposes tool call id and state, which we previously
were unable to do.

## other benefits

* Cleaner code structure w/ more helpful docs about injected args.
* Nice perf boost, we're no longer inspecting the annotations of a
tool's schema 3 different times to detect store, state, and runtime
injections.

## additional notes

1. I could see a world where we want more of this logic to reside on the
tools themselves, but tools don't now about LG specific injection types
(like `ToolRuntime`, hence having this logic here for now).
2. We could separately add validation for the case where something is
specified in `args_schema` and not in the function signature (probably
at the tool level though).
2025-11-20 11:37:54 -05:00
Sydney RunkleandGitHub 6d20a0b9c7 fix: deprecate setattr on ToolCallRequest (#6462)
* one alternative considered was setting `frozen=True` on the dataclass,
but this is breaking, so a deprecation is a nicer approach
2025-11-19 13:12:11 -05:00
William FHandGitHub df8becd5cf refactor: separate prepare_push_* functions (#6450)
Extract two common cases from the big switch statement of
`prepare_single_task` since it's a tad more composable.

All this does is shift/extract code to separate functions
2025-11-14 16:13:03 -08:00
Lauren Hirata SinghandGitHub 056ba91a71 chore(docs): add more redirects + catchall (#6442) 2025-11-13 14:49:40 -05:00
Sydney RunkleandGitHub 02300de24c fix: dep warnings in prebuilt (#6443) 2025-11-13 13:59:34 -05:00
Sydney RunkleandGitHub ac16bdb795 release: prebuilt 1.0.3 (#6441) 2025-11-13 13:38:24 -05:00
Caspar BroekhuizenandGitHub 0d4ac836e3 chore: langgraph patch release (#6429) 2025-11-10 09:37:35 -08:00
Lauren Hirata SinghandGitHub 201c8015ea chore(docs): Update links in notebook_hooks.py for deployment (#6428) 2025-11-10 09:51:05 -05:00
Mason DaughertyandGitHub cf3e8252f5 feat(docs): warn that StateGraph is a builder class (#6417) 2025-11-07 21:09:15 -05:00
Mason DaughertyandGitHub 7a5e3c1e79 fix(docs): PartialState rendering in MkDocs (#6416)
The carat chars were not rendering without code style formatting
2025-11-07 21:08:01 -05:00
Mason DaughertyandGitHub 218c60717e fix(docs): synchronize invoke and ainvoke docstrings (#6415)
Similar to #6414
2025-11-07 20:44:33 -05:00
Mason DaughertyandGitHub 28b9f578b0 fix(docs): synchronize stream and astream docstrings (#6414)
`stream` and `astream` docstrings listed different available
`stream_mode` options.

Both methods support the same seven stream modes as defined in
`StreamMode`

Fixed for consistency
2025-11-07 20:44:25 -05:00
le-codeur-rapideandGitHub bef76b791c docs(langgraph): Fix docstring code examples of task function (#6410)
Hi all,
I found out that the sync and async code examples of the `task` function
in `libs/langgraph/langgraph/func/__init__.py` have a typo:
```
    Example: Sync Task
        ```python
        from langgraph.func import entrypoint, task


        @task
        def add_one(a: int) -> int:
            return a + 1


        @entrypoint()
        def add_one(numbers: list[int]) -> list[int]:
            futures = [add_one(n) for n in numbers]
            results = [f.result() for f in futures]
            return results


        # Call the entrypoint
        add_one.invoke([1, 2, 3])  # Returns [2, 3, 4]
        ```
```

Both task and entrypoint functions have the same name which gives an
error.

This is a small PR to fix this
2025-11-07 16:01:19 -05:00
a1b34efdb0 fix(checkpoint-postgres): ensure vector extension is created only if not exists (#6154)
Thank you for contributing to LangGraph! Follow these steps to mark your
pull request as ready for review. **If any of these steps are not
completed, your PR will not be considered for review.**

- [x] **PR title**: Follows the format: {TYPE}({SCOPE}): {DESCRIPTION}
  - Examples:
    - feat(core): add multi-tenant support
    - fix(cli): resolve flag parsing error
    - docs(openai): update API usage examples
  - Allowed `{TYPE}` values:
- feat, fix, docs, style, refactor, perf, test, build, ci, chore,
revert, release
  - Allowed `{SCOPE}` values (optional):
- langgraph, docs, cli, checkpoint, checkpoint-postgres,
checkpoint-sqlite, prebuilt, scheduler-kafka, sdk-py
- Once you've written the title, please delete this checklist item; do
not include it in the PR.

- **Description:** Azure Postgres SQL server has a limitation when doing
create extension vector is not exists, even though it manually created
before on a schema.
- **Issue:** Even though `CREATE EXTENSION vector` is executed manually
before, the permission issue arises. Putting it in an if else block
solves the issue and its not a breaking change.
```
Because vector isn't a trusted extension, only members of "azure_pg_admin" are allowed to use CREATE EXTENSION vector
HINT: to learn how to allow an extension or see the list of allowed extensions, please refer to https://go.microsoft.com/fwlink/?linkid=2301063
```

Co-authored-by: Josh Rogers <josh@langchain.dev>
2025-11-07 11:42:15 -05:00
André MenezesandGitHub 7ab5788f25 fix(langgraph): Unexpected behavior for stream_mode sequences that are not lists (#6354)
## Issue
The `stream_mode` argument type includes `Sequence`, but it doesn't
correctly support non-list sequences. On the other hand, the
`print_mode` argument works as expected.

### Example
```python
from langgraph.pregel.main import Pregel

pregel = Pregel(nodes={}, channels=None, input_channels=[], output_channels=[], auto_validate=False)
stream_modes, *_ = pregel._defaults(
    config={"recursion_limit": 1},
    stream_mode=("values", "messages"),
    print_mode=("values"),
    output_keys=None,
    interrupt_before=None,
    interrupt_after=None,
    durability=None,
)
print(stream_modes) # Expected `{'values', 'messages'}`, got `{('values', 'messages'), 'values'}`
```
2025-11-07 08:01:25 -05:00
Cole MurrayandGitHub b0a1029d55 fix(checkpoint-postgres): Replace f-string SQL formatting with parameterized queries in migration statements (#6328)
## Summary

Replace f-string SQL formatting with parameterized queries to prevent
potential SQL injection in checkpoint migration code.

## Changes

Updated the migration version tracking INSERT statements in all
checkpoint saver classes to use parameterized queries instead of
f-string formatting:

- `PostgresSaver`
(libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py:100)
- `AsyncPostgresSaver`
(libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py:104-106)
- `ShallowPostgresSaver`
(libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py:255)
- `AsyncShallowPostgresSaver`
(libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py:617-619)

**Before (vulnerable to SQL injection):**
```python
cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
```

**After (using parameterized query):**
```python
cur.execute("INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,))
```

## Risk Assessment

The practical risk is low since `v` is an integer loop variable
controlled by the codebase. However, using string formatting in SQL
queries is a well-known anti-pattern that can lead to SQL injection
vulnerabilities, especially if the code is later refactored or copied to
other contexts.

## Testing

-  All 216 tests passing on PostgreSQL 15 and 16
-  Linting and type checking passing
-  No functional changes to behavior
2025-11-07 08:00:10 -05:00
Mason DaughertyandGitHub c2ef3f3fd3 fix: remove SDK inline links (#6307)
these were broken; remove for now.
2025-11-07 07:56:00 -05:00
Michael LiandGitHub d455bd841d fix: fix previoius edge cases such as 0 (#6379)
Thank you for contributing to LangGraph! Follow these steps to mark your
pull request as ready for review. **If any of these steps are not
completed, your PR will not be considered for review.**

- [x] **PR title**: Follows the format: {TYPE}({SCOPE}): {DESCRIPTION}
  - Examples:
    - feat(core): add multi-tenant support
    - fix(cli): resolve flag parsing error
    - docs(openai): update API usage examples
  - Allowed `{TYPE}` values:
- feat, fix, docs, style, refactor, perf, test, build, ci, chore,
revert, release
  - Allowed `{SCOPE}` values (optional):
- langgraph, docs, cli, checkpoint, checkpoint-postgres,
checkpoint-sqlite, prebuilt, scheduler-kafka, sdk-py
- Once you've written the title, please delete this checklist item; do
not include it in the PR.

- [x] **PR message**: ***Delete this entire checklist*** and replace
with
- **Description:** a description of the change. Include a [closing
keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)
if applicable.
  - **Issue:** the issue # it fixes, if applicable
  - **Dependencies:** any dependencies required for this change
- **Twitter handle:** if your PR gets announced, and you'd like a
mention, we'll gladly shout you out!

- [x] **Add tests and docs**: If you're adding a new integration, you
must include:
1. A test for the integration, preferably unit tests that do not rely on
network access,
2. An example notebook showing its use. It lives in
`docs/docs/integrations` directory.

- [x] **Lint and test**: Run `make format`, `make lint` and `make test`
from the root of the package(s) you've modified. We will not consider a
PR unless these three are passing in CI. See [contribution
guidelines](https://github.com/langchain-ai/langgraph/blob/main/CONTRIBUTING.md)
for more.

Additional guidelines:

- Make sure optional dependencies are imported within a function.
- Please do not add dependencies to `pyproject.toml` files (even
optional ones) unless they are **required** for unit tests.
- Most PRs should not touch more than one package.
- Changes should be backwards compatible.
2025-11-07 07:54:22 -05:00
Pedro Enrique Agurto CastilloandGitHub 69a09adef6 fix(langgraph): export REMOVE_ALL_MESSAGES in __all__ to fix linting (#6375)
`REMOVE_ALL_MESSAGES` is a public constant used with `RemoveMessage` to
clear all messages from the state:

```python
from langchain_core.messages import RemoveMessage
from langgraph.graph.message import REMOVE_ALL_MESSAGES

# Clear all messages
[RemoveMessage(id=REMOVE_ALL_MESSAGES)]
```

However, it is not exported in __all__, causing:

Linting errors in IDEs (PyCharm)
no-member warnings from Pylint
Confusion for users

This PR:

Adds REMOVE_ALL_MESSAGES to __all__
Adds inline docstring with usage example

No runtime behavior changes — only improves IDE support and API clarity.

Thank you for contributing to LangGraph! Follow these steps to mark your
pull request as ready for review. **If any of these steps are not
completed, your PR will not be considered for review.**

---
Local verification:
```bash
# Before
from langgraph.graph.message import REMOVE_ALL_MESSAGES  # Pylint: no-member

# After: no error
```
CI Note: This is a pure export/docs fix. `make lint` and `make test`
pass unchanged.
2025-11-07 07:52:27 -05:00
Kavya GoyalandGitHub 35aa98b110 fix(sdk-py): use correct f-string representation when loading error (#6388)
Thank you for contributing to LangGraph! Follow these steps to mark your
pull request as ready for review. **If any of these steps are not
completed, your PR will not be considered for review.**


## Description
- Fixed a bug in `libs/sdk-py/langgraph_sdk/auth/__init__.py` where the
error message for an already-set authentication handler did not properly
render the handler value.
- Updated the error string from a static `{self._authenticate_handler}`
to a correctly interpolated f-string.

```python
"Authentication handler already set as {self._authenticate_handler}."
```

```python
f"Authentication handler already set as {self._authenticate_handler}."
```

- Error messages now correctly display the actual handler instance,
improving debugging clarity.



- **Issue:** Fixes https://github.com/langchain-ai/langgraph/issues/6387
  - **Dependencies:** -
  - **Twitter handle:** -

- [x] **Add tests and docs**: If you're adding a new integration, you
must include:
1. A test for the integration, preferably unit tests that do not rely on
network access,
2. An example notebook showing its use. It lives in
`docs/docs/integrations` directory.

- [ ] **Lint and test**: Run `make format`, `make lint` and `make test`
from the root of the package(s) you've modified. We will not consider a
PR unless these three are passing in CI. See [contribution
guidelines](https://github.com/langchain-ai/langgraph/blob/main/CONTRIBUTING.md)
for more.

Additional guidelines:

- Make sure optional dependencies are imported within a function.
- Please do not add dependencies to `pyproject.toml` files (even
optional ones) unless they are **required** for unit tests.
- Most PRs should not touch more than one package.
- Changes should be backwards compatible.
2025-11-07 07:51:35 -05:00
Mason DaughertyandGitHub 0f83d9fafe style: update docstrings to reference StateGraph (#6308)
nit
2025-11-07 07:47:29 -05:00
Logan RosenandGitHub 52d66df92c docs(langgraph): update streaming guide links (#6314)
Updating links to the LangGraph streaming guide to point to the new
documentation website for 1.0.
2025-11-07 07:46:48 -05:00
Mason DaughertyandGitHub 4ec92f9fb1 chore: add pyproject.toml links (#6364) 2025-11-07 07:43:51 -05:00
inhunandGitHub 2b72953064 docs: add license files for checkpoint-sqlite and checkpoint-postgres (#6392)
In this PR:

- Add missing LICENSE files for checkpoint-sqlite and
checkpoint-postgres libraries.

Both libraries specify the MIT License in their pyproject.toml files,
but the actual LICENSE files were missing.
This update adds the corresponding LICENSE files to ensure proper
license documentation and compliance.
2025-11-07 07:39:06 -05:00
le-codeur-rapideandGitHub 232014e8ef docs(langgraph): Fix typo in docstring of PregelLoop.tick (#6407)
This is a very small PR to correct a typo in the docstring of the
`PregelLoop.tick()` method.
```python
  def tick(self) -> bool:
      """Execute a single iteration of the Pregel loop.

      Args:
          input_keys: The key(s) to read input from.

      Returns:
          True if more iterations are needed.
      """
```

Corrected to :
```python
  def tick(self) -> bool:
      """Execute a single iteration of the Pregel loop.

      Returns:
          True if more iterations are needed.
      """
```

The docstring was written in #2946 when the signature of tick was
```python
    def tick(
        self,
        *,
        input_keys: Union[str, Sequence[str]],
    ) -> bool:
```
but  it was simplified to 
```python
def tick(self) -> bool:
```
in #5080
2025-11-07 07:38:16 -05:00
Josh RogersandGitHub 9fd3dfc542 chore(checkpoint-postgres): bump to 3.0.1 (#6402)
**Description:** Bumping the checkpoint-postgres package to version
3.0.1 to release an update to migrations
(https://github.com/langchain-ai/langgraph/pull/6400).
**Issue:** N/A
**Dependencies:** N/A
**Twitter handle:** N/A
2025-11-06 11:14:02 -05:00
Josh RogersandGitHub ff38f75594 fix(checkpoint-postgres): make async PG checkpoint migration idempotent (#6400)
- **Description:** The final migration for the postgres checkpointer is
not currently idempotent. That presents problems when migrating from one
checkpointer to another or if migrations otherwise get applied twice.
This makes the final migration idempotent to avoid this problem.
- **Issue:** N/A
- **Dependencies:** N/A
- **Twitter handle:** N/A
2025-11-06 09:49:51 -05:00
Kathryn MayandGitHub bbdd007341 chore(docs): Update redirects for langgraph server to agent server rename (#6399)
Renaming LangGraph Server to Agent Server, this updates the redirects
from the old site to the new site's renamed files.
PR also includes some hosting --> platform setup redirects
2025-11-05 11:21:09 -05:00
Caspar BroekhuizenandGitHub 3c75e414e5 fix(langgraph): do not apply pending writes when updating state (#6389)
PR #6195 fixed `bulk_update_state` to populate `task.result` by calling
`prepare_next_tasks` to discover task IDs. Before #6195,
prepare_next_tasks was gated by the condition `CONFIG_KEY_CHECKPOINT_ID
not in config[CONF]` - so it only ran if we were resuming from an empty
checkpoint. This check was removed in order to properly populate task
results. However, the removal of this check inadvertently applied
pending writes during manual state updates which caused issues when
forking:

- When you fork from a checkpoint by calling `update_state(config,
new_values, as_node="mynode")`, pending writes from the original
execution were being applied
- This caused stale data to leak into forked threads (eg. old tool call
results appearing in forked execution)

Changes

Removed pending writes application from `bulk_update_state` and
`abulk_update_state`:
- Still call `prepare_next_tasks` to discover task IDs, but skip the
code that applies null writes and regular pending writes

Tests

- Added `test_fork_does_not_apply_pending_writes` for sync and async
which verifies forking doesn't include stale pending writes from
original execution
2025-11-04 14:38:40 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
575401c78c chore(deps): bump hono from 4.9.7 to 4.10.3 in /docs/_scripts/js_translation/codeblocks (#6339)
Bumps [hono](https://github.com/honojs/hono) from 4.9.7 to 4.10.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/hono/releases">hono's
releases</a>.</em></p>
<blockquote>
<h2>v4.10.3</h2>
<h2>Securiy Fix</h2>
<p>A security issue in the CORS middleware has been fixed. In some
cases, a request header could affect the Vary response header. Please
update to the latest version if you are using the CORS middleware.</p>
<h2>What's Changed</h2>
<ul>
<li>fix(aws-lambda): serve microsoft office files as binary in lambda
handler by <a
href="https://github.com/matthiasfeist"><code>@​matthiasfeist</code></a>
in <a
href="https://redirect.github.com/honojs/hono/pull/4469">honojs/hono#4469</a></li>
<li>fix(request-id): validation accepts <code>=</code> by <a
href="https://github.com/ryuapp"><code>@​ryuapp</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/4478">honojs/hono#4478</a></li>
<li>refactor(jwt): reduce the size of the code generated by minification
by <a href="https://github.com/usualoma"><code>@​usualoma</code></a> in
<a
href="https://redirect.github.com/honojs/hono/pull/4480">honojs/hono#4480</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/matthiasfeist"><code>@​matthiasfeist</code></a>
made their first contribution in <a
href="https://redirect.github.com/honojs/hono/pull/4469">honojs/hono#4469</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/hono/compare/v4.10.2...v4.10.3">https://github.com/honojs/hono/compare/v4.10.2...v4.10.3</a></p>
<h2>v4.10.2</h2>
<h2>Security hardening improvement</h2>
<p>If you are using JWT middleware, please read the following and
consider applying the configuration.</p>
<h3>Improper Authorization in Hono (JWT Audience Validation)</h3>
<p>Hono’s JWT authentication middleware did not validate the aud
(Audience) claim by default. As a result, applications using the
middleware without an explicit audience check could accept tokens
intended for other audiences, leading to potential cross-service access
(token mix-up).</p>
<p>The issue is addressed by adding a new <code>verification.aud</code>
configuration option to allow RFC 7519–compliant audience validation.
This change is classified as a security hardening improvement, but the
lack of validation can still be considered a vulnerability in
deployments that rely on default JWT verification.</p>
<h3>Recommended secure configuration</h3>
<p>You can enable RFC 7519–compliant audience validation using the new
<code>verification.aud</code> option:</p>
<pre lang="ts"><code>import { Hono } from 'hono'
import { jwt } from 'hono/jwt'
<p>const app = new Hono()</p>
<p>app.use(<br />
'/api/*',<br />
jwt({<br />
secret: 'my-secret',<br />
verification: {<br />
// Require this API to only accept tokens with aud = 'service-a'<br />
aud: 'service-a',<br />
},<br />
})<br />
)<br />
</code></pre></p>
<h2>What's Changed</h2>
<ul>
<li>tests: Fix test case of handlers without a path by <a
href="https://github.com/IAmSSH"><code>@​IAmSSH</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/4472">honojs/hono#4472</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/honojs/hono/commit/fcefd50c65144eda31e2bc6752c81904171d9629"><code>fcefd50</code></a>
4.10.3</li>
<li><a
href="https://github.com/honojs/hono/commit/95ae4d372119cddba32e4935d2bbc6f4e2768dab"><code>95ae4d3</code></a>
refactor(jwt): reduce the size of the code generated by minification (<a
href="https://redirect.github.com/honojs/hono/issues/4480">#4480</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/d9b8b4b73b4f997994f2764013207365fe711282"><code>d9b8b4b</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/honojs/hono/commit/52161170e83298fc3d13312bfceba3992916bfa2"><code>5216117</code></a>
fix(request-id): validation accepts <code>=</code> (<a
href="https://redirect.github.com/honojs/hono/issues/4478">#4478</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/253ec2857a083595e52a446694923645084e9ecd"><code>253ec28</code></a>
fix(aws-lambda): serve microsoft office files as binary in lambda
handler (<a
href="https://redirect.github.com/honojs/hono/issues/4">#4</a>...</li>
<li><a
href="https://github.com/honojs/hono/commit/0c6455dc10db6428257bdd601eca559247e27de6"><code>0c6455d</code></a>
4.10.2</li>
<li><a
href="https://github.com/honojs/hono/commit/45ba3bf9e3dff8e4bd85d6b47d4b71c8d6c66bef"><code>45ba3bf</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/honojs/hono/commit/4cbad8b3e2a67d77849710ec400d9de020c435fd"><code>4cbad8b</code></a>
tests: Fix test case of handlers without a path (<a
href="https://redirect.github.com/honojs/hono/issues/4472">#4472</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/db764c2f1d8a2905d66c78c41aa47e47d3a4165d"><code>db764c2</code></a>
4.10.1</li>
<li><a
href="https://github.com/honojs/hono/commit/8774bf9a59278a9593d5e91cc85543d5a4bb518c"><code>8774bf9</code></a>
fix(types): cannot <code>.use</code> non-return mw from
<code>createMiddleware</code> (<a
href="https://redirect.github.com/honojs/hono/issues/4465">#4465</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/honojs/hono/compare/v4.9.7...v4.10.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hono&package-manager=npm_and_yarn&previous-version=4.9.7&new-version=4.10.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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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>
2025-11-04 13:52:15 -08:00
William FHandGitHub 092c9ecde6 chore: update ormsgpack minbound and add OPT_REPLACE_SURROGATES (#6395)
This lets the default `msgpack` serialization mode handle more cases
where user data contains invalid unicode.
2025-11-04 13:51:34 -08:00
William FHandGitHub 8336686e89 release(cli): 0.4.7 expand api bounds (#6390)
Fixes #6380
2025-11-03 23:47:05 +00:00
Parker J. RuleandGitHub 8b080a091e fix(checkpoint): update checkpoint interface specification in README (#6386)
This syncs the checkpoint interface specification with the base class
(`BaseCheckpointSaver`) in
`langgraph/libs/checkpoint/langgraph/checkpoint/base /__init__.py`.
2025-11-03 21:44:41 +00:00
Asamu DavidandGitHub 1157ec77b4 fix(cli): add buildkit syntax directiive, update tests (#6385)
**Description:** Adds the syntax directive to generated dockerfile for
langgraph builds if we have additional contexts
**Issue:** fixes issue with python monorepo builds failing
**Dependencies:** N/A
2025-11-03 18:13:19 +00:00
William FHandGitHub a10a66cbd1 chore: Update cli config schema (#6372) 2025-11-01 09:50:46 -07:00
Kathryn MayandGitHub ae525fb74f docs: Update hosting page name redirect to platform setup (#6371) 2025-10-31 14:12:59 -04:00
Mason DaughertyandGitHub a6dde39be7 chore: style fixes for refs (#6365) 2025-10-30 17:59:40 -04:00
115 changed files with 8397 additions and 4791 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ def main():
tree = ast.parse(file.read())
classes = find_classes(tree)
def is_sync(class_spec: Tuple[str, List[str]]) -> bool:
return class_spec[0].startswith("Sync")
+37 -13
View File
@@ -1,7 +1,8 @@
import logging
import pathlib
import sys
import time
from urllib import request, error
from urllib import error, request
import langgraph_cli
import langgraph_cli.config
@@ -11,9 +12,13 @@ from langgraph_cli.constants import DEFAULT_PORT
from langgraph_cli.exec import Runner, subp_exec
from langgraph_cli.progress import Progress
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
"""Spin up API with Postgres/Redis via docker compose and wait until ready."""
logger.info("Starting test...")
with Runner() as runner, Progress(message="Pulling...") as set:
# Detect docker/compose capabilities
capabilities = langgraph_cli.docker.check_capabilities(runner)
@@ -57,7 +62,9 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
sys.stderr.write(f"docker compose up failed: {e}\n")
try:
sys.stderr.write("\n== docker compose ps ==\n")
runner.run(subp_exec(*compose_cmd, *args, "ps", input=stdin, verbose=False))
runner.run(
subp_exec(*compose_cmd, *args, "ps", input=stdin, verbose=False)
)
except Exception:
pass
try:
@@ -93,7 +100,7 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
set("")
base_url = f"http://localhost:{port}"
ok_url = f"{base_url}/ok"
print(f"Waiting for {ok_url} to respond with 200...")
logger.info(f"Waiting for {ok_url} to respond with 200...")
deadline = time.time() + 30
last_err: Exception | None = None
while time.time() < deadline:
@@ -107,13 +114,16 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
break
else:
last_err = RuntimeError(f"Unexpected status: {resp.status}")
print(f"Unexpected status: {resp.status}")
logger.error(f"Unexpected status: {resp.status}")
except error.URLError as e:
logger.error(f"URLError: {e}")
last_err = e
except Exception as e: # noqa: BLE001
logger.error(f"Exception: {e}")
last_err = e
time.sleep(0.5)
else:
logger.error("Timeout waiting for /ok to return 200")
# Bring stack down before raising
args_down = [*args, "down", "-v", "--remove-orphans"]
try:
@@ -131,15 +141,23 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
)
# Clean up: bring compose stack down to free ports for next test
args_down = [*args, "down", "-v", "--remove-orphans"]
runner.run(
subp_exec(
*compose_cmd,
*args_down,
input=stdin,
verbose=verbose,
logger.info("Test succeeded. Bringing down compose stack...")
try:
args_down = [*args, "down", "-v", "--remove-orphans"]
runner.run(
subp_exec(
*compose_cmd,
*args_down,
input=stdin,
verbose=verbose,
)
)
)
logger.info("Compose stack down. Finishing...")
except Exception:
logger.exception("Failed to bring down compose stack")
pass
logger.info("Test finished")
if __name__ == "__main__":
@@ -150,4 +168,10 @@ if __name__ == "__main__":
parser.add_argument("-c", "--config", type=str, default="./langgraph.json")
parser.add_argument("-p", "--port", type=int, default=DEFAULT_PORT)
args = parser.parse_args()
test(pathlib.Path(args.config), args.port, args.tag, verbose=True)
try:
test(pathlib.Path(args.config), args.port, args.tag, verbose=True)
except BaseException:
logger.exception("Test failed")
raise
logger.info("Test execution finished")
+16 -9
View File
@@ -13,7 +13,6 @@ jobs:
matrix:
python-version:
- "3.10"
- "3.11"
- "3.14"
example:
- name: A
@@ -33,7 +32,7 @@ jobs:
run:
working-directory: libs/cli
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Get changed files
id: changed-files
uses: Ana06/get-changed-files@v2.3.0
@@ -67,19 +66,19 @@ jobs:
timeout 60 python "$REPO_ROOT/.github/scripts/run_langgraph_cli_test.py" -t ${{ matrix.example.tag }}
- name: Build JS service
if: steps.changed-files.outputs.all
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' }}
working-directory: libs/cli/js-examples
run: |
langgraph build -t langgraph-test-e
- name: Build JS monorepo service
if: steps.changed-files.outputs.all
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' }}
working-directory: libs/cli/js-monorepo-example
run: |
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json --build-command "yarn run turbo build" --install-command "yarn install"
- name: Build Python monorepo service
if: steps.changed-files.outputs.all
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' }}
working-directory: libs/cli/python-monorepo-example
run: |
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
@@ -88,24 +87,32 @@ jobs:
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
- name: Build and test prerelease reqs service
if: steps.changed-files.outputs.all
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' }}
working-directory: libs/cli/examples/graph_prerelease_reqs
run: |
langgraph build -t langgraph-test-h
cp ../.env.example .env
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env; fi
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-h
echo "Finished starting up langgraph-test-h"
LANGGRAPH_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langgraph'); print(v);")
if [ "$LANGGRAPH_VERSION" != "1.0.0a2" ]; then
if [ "$LANGGRAPH_VERSION" != "1.0.2" ]; then
echo "LANGGRAPH_VERSION != 1.0.2; $LANGGRAPH_VERSION"
exit 1
fi
LANGCHAIN_OPENAI_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-openai'); print(v);")
if [ "$LANGCHAIN_OPENAI_VERSION" != "0.3.0" ]; then
if [ "$LANGCHAIN_OPENAI_VERSION" != "1.0.1" ]; then
echo "LANGCHAIN_OPENAI_VERSION != 1.0.1; $LANGCHAIN_OPENAI_VERSION"
exit 1
fi
LANGCHAIN_ANTHROPIC_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-anthropic'); print(v);")
if [ "$LANGCHAIN_ANTHROPIC_VERSION" != "1.0.0a5" ]; then
echo "LANGCHAIN_ANTHROPIC_VERSION != 1.0.0a5; $LANGCHAIN_ANTHROPIC_VERSION"
exit 1
fi
- name: Build and test prerelease reqs fail service
if: steps.changed-files.outputs.all
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' }}
working-directory: libs/cli/examples/graph_prerelease_reqs_fail
run: |
langgraph build -t langgraph-test-i || [ $? -eq 1 ]
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
- "3.12"
name: "lint #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Get changed files
id: changed-files
uses: Ana06/get-changed-files@v2.3.0
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
name: "test #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v7
with:
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
working-directory: libs/langgraph
name: "test #${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v7
with:
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
version: ${{ steps.check-version.outputs.version }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python $${ env.PYTHON_VERSION }}
uses: astral-sh/setup-uv@v7
@@ -74,7 +74,7 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/download-artifact@v6
with:
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
run:
working-directory: libs/langgraph
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- run: SHA=$(git rev-parse HEAD) && echo "SHA=$SHA" >> $GITHUB_ENV
- name: Set up Python 3.11
uses: astral-sh/setup-uv@v7
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
run:
working-directory: libs/langgraph
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- id: files
name: Get changed files
uses: Ana06/get-changed-files@v2.3.0
+3 -3
View File
@@ -27,7 +27,7 @@ jobs:
python: ${{ steps.filter.outputs.python }}
deps: ${{ steps.filter.outputs.deps }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v3
id: filter
with:
@@ -100,7 +100,7 @@ jobs:
name: "Check SDK methods matching"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -118,7 +118,7 @@ jobs:
python-version:
- "3.11"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v7
with:
+1 -1
View File
@@ -21,7 +21,7 @@
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install Dependencies
run: |
-72
View File
@@ -1,72 +0,0 @@
name: Deploy Docs Redirects
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
defaults:
run:
working-directory: docs
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 10 # Job will be cancelled if it runs for more than 10 minutes
env:
GITHUB_TOKEN: ${{ secrets.MKDOCS_GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Python
uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"
enable-cache: true
cache-suffix: "docs"
- name: Install dependencies
run: |
yarn
uv sync --all-groups
# we run this installation only for internal PRs
# as GITHUB_TOKEN is not available for PRs from outside contributors
if [ -n "${GITHUB_TOKEN}" ]; then
uv run pip install "git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git"
fi
- name: Build llms-text
run: make llms-text
- name: Build site (redirects only)
run: make build-docs
env:
OPENAI_API_KEY: sf-proj-1234567890 # fake placeholder, shouldn't actually be used
ANTHROPIC_API_KEY: sk-ant-api03-1234567890 # fake placeholder, shouldn't actually be used
- name: Configure GitHub Pages
if: github.ref == 'refs/heads/main'
uses: actions/configure-pages@v5
- name: Upload Pages Artifact
if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v4
with:
path: ./docs/site/
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
id: deployment
uses: actions/deploy-pages@v4
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -36,7 +36,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 1
+6 -5
View File
@@ -25,7 +25,7 @@ jobs:
tag: ${{ steps.check-version.outputs.tag }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python
uses: astral-sh/setup-uv@v7
@@ -86,7 +86,7 @@ jobs:
outputs:
release-body: ${{ steps.generate-release-body.outputs.release-body }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
repository: langchain-ai/langgraph
path: langgraph
@@ -157,7 +157,7 @@ jobs:
- test-pypi-publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
# We explicitly *don't* set up caching here. This ensures our tests are
# maximally sensitive to catching breakage.
@@ -260,7 +260,7 @@ jobs:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python
uses: astral-sh/setup-uv@v7
@@ -301,7 +301,7 @@ jobs:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python
uses: astral-sh/setup-uv@v7
@@ -322,5 +322,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
generateReleaseNotes: false
tag: ${{needs.build.outputs.tag}}
name: ${{ needs.build.outputs.pkg-name }}==${{ needs.build.outputs.version }}
body: ${{ needs.release-notes.outputs.release-body }}
commit: ${{ github.sha }}
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- "latest"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python + Poetry
uses: astral-sh/setup-uv@v7
with:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up uv
uses: astral-sh/setup-uv@v7
+1 -1
View File
@@ -67,7 +67,7 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
- [LangChain](https://docs.langchain.com/oss/python/langchain/overview) Provides integrations and composable components to streamline LLM application development.
> [!NOTE]
> Looking for the JS version of LangGraph? See the [JS repo](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://langchain-ai.github.io/langgraphjs/).
> Looking for the JS version of LangGraph? See the [JS repo](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://docs.langchain.com/oss/javascript/langgraph/overview).
## Additional resources
@@ -2108,9 +2108,9 @@ __metadata:
linkType: hard
"hono@npm:^4.5.4":
version: 4.9.7
resolution: "hono@npm:4.9.7"
checksum: 10c0/089184660a9211ea216ab95bafa45260e371651cb019db49828064b7982b0ae61cc3c4715324bfeb9037aa2460c39ffa2c91d84ad0c8d500fa77cbcc7fc07a8f
version: 4.10.3
resolution: "hono@npm:4.10.3"
checksum: 10c0/bdcc4c7066c74ba7cfa63ed6550768a0f43a420286c8f8f74b7012ea4901b8b06778fa8e98264b46f1a86920f056b7ede1f07814da4934912f9945def4977c29
languageName: node
linkType: hard
@@ -2340,13 +2340,13 @@ __metadata:
linkType: hard
"js-yaml@npm:^4.1.0":
version: 4.1.0
resolution: "js-yaml@npm:4.1.0"
version: 4.1.1
resolution: "js-yaml@npm:4.1.1"
dependencies:
argparse: "npm:^2.0.1"
bin:
js-yaml: bin/js-yaml.js
checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f
checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7
languageName: node
linkType: hard
+60 -14
View File
@@ -81,7 +81,7 @@ REDIRECT_MAP = {
# cloud redirects
"cloud/index.md": "https://docs.langchain.com/oss/python/langgraph/overview",
"cloud/how-tos/index.md": "https://docs.langchain.com/langsmith/home",
"cloud/concepts/api.md": "https://docs.langchain.com/langsmith/langgraph-server",
"cloud/concepts/api.md": "https://docs.langchain.com/langsmith/agent-server",
"cloud/concepts/cloud.md": "https://docs.langchain.com/langsmith/cloud",
"cloud/faq/studio.md": "https://docs.langchain.com/langsmith/studio",
"cloud/how-tos/human_in_the_loop_edit_state.md": "https://docs.langchain.com/langsmith/add-human-in-the-loop",
@@ -111,8 +111,8 @@ REDIRECT_MAP = {
"tutorials/introduction.ipynb": "https://docs.langchain.com/oss/python/langgraph/overview",
"agents/deployment.md": "https://docs.langchain.com/oss/python/langgraph/local-server",
# deployment redirects
"how-tos/deploy-self-hosted.md": "https://docs.langchain.com/langsmith/hosting",
"concepts/self_hosted.md": "https://docs.langchain.com/langsmith/hosting",
"how-tos/deploy-self-hosted.md": "https://docs.langchain.com/langsmith/platform-setup",
"concepts/self_hosted.md": "https://docs.langchain.com/langsmith/platform-setup",
"tutorials/deployment.md": "https://docs.langchain.com/langsmith/deployments",
# assistant redirects
"cloud/how-tos/assistant_versioning.md": "https://docs.langchain.com/langsmith/configuration-cloud",
@@ -129,16 +129,30 @@ REDIRECT_MAP = {
"how-tos/human_in_the_loop/edit-graph-state.ipynb": "https://docs.langchain.com/oss/python/langgraph/use-time-travel",
# LGP mintlify migration redirects
"examples/index.md": "https://docs.langchain.com/oss/python/learn",
"guides/index.md": "https://docs.langchain.com/oss/python/langgraph/overview",
"concepts/index.md": "https://docs.langchain.com/oss/python/langgraph/overview",
"tutorials/index.md": "https://docs.langchain.com/oss/python/learn",
"llms-txt-overview.md": "https://docs.langchain.com/llms.txt",
"tutorials/rag/langgraph_adaptive_rag.md": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
"tutorials/multi_agent/multi-agent-collaboration.ipynb": "https://docs.langchain.com/oss/python/langchain/multi-agent",
"how-tos/create-react-agent-manage-message-history.ipynb": "https://docs.langchain.com/oss/python/langgraph/add-memory",
"how-tos/many-tools.ipynb": "https://docs.langchain.com/oss/python/langchain/tools",
"tutorials/customer-support/customer-support.ipynb": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
"how-tos/react-agent-structured-output.ipynb": "https://docs.langchain.com/oss/python/langchain/agents#structured-output",
"tutorials/code_assistant/langgraph_code_assistant.ipynb": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
"tutorials/multi_agent/hierarchical_agent_teams.ipynb": "https://docs.langchain.com/oss/python/langchain/supervisor",
"tutorials/auth/getting_started.md": "https://docs.langchain.com/langsmith/auth",
"tutorials/auth/resource_auth.md": "https://docs.langchain.com/langsmith/resource-auth",
"tutorials/auth/add_auth_server.md": "https://docs.langchain.com/langsmith/add-auth-server",
"how-tos/use-remote-graph.md": "https://docs.langchain.com/langsmith/use-remote-graph",
"how-tos/autogen-integration.md": "https://docs.langchain.com/langsmith/autogen-integration",
"how-tos/human_in_the_loop/wait-user-input.ipynb": "https://docs.langchain.com/oss/python/langgraph/interrupts",
"cloud/how-tos/use_stream_react.md": "https://docs.langchain.com/langsmith/use-stream-react",
"cloud/how-tos/generative_ui_react.md": "https://docs.langchain.com/langsmith/generative-ui-react",
"concepts/langgraph_platform.md": "https://docs.langchain.com/langsmith/deployments",
"concepts/langgraph_components.md": "https://docs.langchain.com/langsmith/components",
"concepts/langgraph_server.md": "https://docs.langchain.com/langsmith/langgraph-server",
"concepts/langgraph_server.md": "https://docs.langchain.com/langsmith/agent-server",
"concepts/langgraph_data_plane.md": "https://docs.langchain.com/langsmith/data-plane",
"concepts/langgraph_control_plane.md": "https://docs.langchain.com/langsmith/control-plane",
"concepts/langgraph_cli.md": "https://docs.langchain.com/langsmith/cli",
@@ -180,7 +194,7 @@ REDIRECT_MAP = {
"cloud/concepts/data_storage_and_privacy.md": "https://docs.langchain.com/langsmith/data-storage-and-privacy",
"cloud/deployment/semantic_search.md": "https://docs.langchain.com/langsmith/semantic-search",
"how-tos/ttl/configure_ttl.md": "https://docs.langchain.com/langsmith/configure-ttl",
"concepts/deployment_options.md": "https://docs.langchain.com/langsmith/hosting",
"concepts/deployment_options.md": "https://docs.langchain.com/langsmith/platform-setup",
"cloud/quick_start.md": "https://docs.langchain.com/langsmith/deployment-quickstart",
"cloud/deployment/setup.md": "https://docs.langchain.com/langsmith/setup-app-requirements-txt",
"cloud/deployment/setup_pyproject.md": "https://docs.langchain.com/langsmith/setup-pyproject",
@@ -201,7 +215,7 @@ REDIRECT_MAP = {
"cloud/deployment/egress.md": "https://docs.langchain.com/langsmith/env-var",
"cloud/how-tos/streaming.md": "https://docs.langchain.com/langsmith/streaming",
"cloud/reference/api/api_ref.md": "https://docs.langchain.com/langsmith/server-api-ref",
"cloud/reference/langgraph_server_changelog.md": "https://docs.langchain.com/langsmith/langgraph-server-changelog",
"cloud/reference/langgraph_server_changelog.md": "https://docs.langchain.com/langsmith/agent-server-changelog",
"cloud/reference/api/api_ref_control_plane.md": "https://docs.langchain.com/langsmith/api-ref-control-plane",
"cloud/reference/cli.md": "https://docs.langchain.com/langsmith/cli",
"cloud/reference/env_var.md": "https://docs.langchain.com/langsmith/env-var",
@@ -219,12 +233,15 @@ REDIRECT_MAP = {
"tutorials/get-started/6-time-travel.md": "https://docs.langchain.com/oss/python/langgraph/quickstart",
"tutorials/langsmith/local-server.md": "https://docs.langchain.com/oss/python/langgraph/local-server",
"tutorials/workflows.md": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
"tutorials/plan-and-execute/plan-and-execute.ipynb": "https://docs.langchain.com/oss/python/langchain/middleware/built-in#to-do-list",
"tutorials/langgraph-platform/local-server/local-server.md": "https://docs.langchain.com/langsmith/local-server",
"concepts/agentic_concepts.md": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
"guides/index.md": "https://docs.langchain.com/oss/python/langchain/overview",
"agents/overview.md": "https://docs.langchain.com/oss/python/langchain/agents",
"agents/run_agents.md": "https://docs.langchain.com/oss/python/langgraph/quickstart",
"concepts/low_level.md": "https://docs.langchain.com/oss/python/langgraph/graph-api",
"how-tos/graph-api.md": "https://docs.langchain.com/oss/python/langgraph/graph-api",
"how-tos/react-agent-from-scratch.ipynb": "https://docs.langchain.com/oss/python/langchain/quickstart",
"concepts/functional_api.md": "https://docs.langchain.com/oss/python/langgraph/functional-api",
"how-tos/use-functional-api.md": "https://docs.langchain.com/oss/python/langgraph/functional-api",
"concepts/pregel.md": "https://docs.langchain.com/oss/python/langgraph/pregel",
@@ -282,8 +299,8 @@ REDIRECT_MAP = {
"reference/supervisor.md": "https://reference.langchain.com/python/langgraph/supervisor/",
"reference/swarm.md": "https://reference.langchain.com/python/langgraph/swarm/",
"reference/mcp.md": "https://reference.langchain.com/python/langgraph/mcp/",
"cloud/reference/sdk/python_sdk_ref.md": "https://reference.langchain.com/python/platform/python_sdk/",
"reference/remote_graph.md": "https://reference.langchain.com/python/platform/remote_graph/",
"cloud/reference/sdk/python_sdk_ref.md": "https://reference.langchain.com/python/langsmith/deployment/sdk/",
"reference/remote_graph.md": "https://reference.langchain.com/python/langsmith/deployment/remote_graph/",
# additional exclude-search entries from mkdocs.yml
"additional-resources/index.md": "https://docs.langchain.com/oss/python/langchain/overview",
@@ -294,8 +311,8 @@ REDIRECT_MAP = {
"cloud/deployment/custom_docker.md": "https://docs.langchain.com/langsmith/custom-docker",
"cloud/deployment/egress.md": "https://docs.langchain.com/langsmith/env-var",
"cloud/deployment/graph_rebuild.md": "https://docs.langchain.com/langsmith/graph-rebuild",
"cloud/deployment/self_hosted_control_plane.md": "https://docs.langchain.com/langsmith/hosting",
"cloud/deployment/self_hosted_data_plane.md": "https://docs.langchain.com/langsmith/hosting",
"cloud/deployment/self_hosted_control_plane.md": "https://docs.langchain.com/langsmith/platform-setup",
"cloud/deployment/self_hosted_data_plane.md": "https://docs.langchain.com/langsmith/platform-setup",
"cloud/deployment/semantic_search.md": "https://docs.langchain.com/langsmith/semantic-search",
"cloud/deployment/setup_javascript.md": "https://docs.langchain.com/langsmith/setup-javascript",
"cloud/deployment/setup_pyproject.md": "https://docs.langchain.com/langsmith/setup-pyproject",
@@ -331,7 +348,7 @@ REDIRECT_MAP = {
"cloud/reference/api/api_ref.md": "https://docs.langchain.com/langsmith/server-api-ref",
"cloud/reference/cli.md": "https://docs.langchain.com/langsmith/cli",
"cloud/reference/env_var.md": "https://docs.langchain.com/langsmith/env-var",
"cloud/reference/langgraph_server_changelog.md": "https://docs.langchain.com/langsmith/langgraph-server-changelog",
"cloud/reference/langgraph_server_changelog.md": "https://docs.langchain.com/langsmith/agent-server-changelog",
"cloud/reference/sdk/js_ts_sdk_ref.md": "https://reference.langchain.com/javascript/modules/langsmith.html",
"concepts/application_structure.md": "https://docs.langchain.com/langsmith/application-structure",
"concepts/assistants.md": "https://docs.langchain.com/langsmith/assistants",
@@ -345,9 +362,9 @@ REDIRECT_MAP = {
"concepts/langgraph_control_plane.md": "https://docs.langchain.com/langsmith/control-plane",
"concepts/langgraph_data_plane.md": "https://docs.langchain.com/langsmith/data-plane",
"concepts/langgraph_platform.md": "https://docs.langchain.com/langsmith/home",
"concepts/langgraph_self_hosted_control_plane.md": "https://docs.langchain.com/langsmith/hosting",
"concepts/langgraph_self_hosted_data_plane.md": "https://docs.langchain.com/langsmith/hosting",
"concepts/langgraph_server.md": "https://docs.langchain.com/langsmith/langgraph-server",
"concepts/langgraph_self_hosted_control_plane.md": "https://docs.langchain.com/langsmith/platform-setup",
"concepts/langgraph_self_hosted_data_plane.md": "https://docs.langchain.com/langsmith/platform-setup",
"concepts/langgraph_server.md": "https://docs.langchain.com/langsmith/agent-server",
"concepts/langgraph_standalone_container.md": "https://docs.langchain.com/langsmith/docker",
"concepts/langgraph_studio.md": "https://docs.langchain.com/langsmith/studio",
"concepts/plans.md": "https://docs.langchain.com/langsmith/home",
@@ -793,6 +810,17 @@ def on_post_build(config):
# Track which paths have explicit redirects
redirected_paths = set()
# Collect all existing HTML files in the site
all_html_files = set()
for root, dirs, files in os.walk(site_dir):
for file in files:
if file.endswith(".html"):
# Get relative path from site_dir
html_path = os.path.relpath(os.path.join(root, file), site_dir)
# Normalize path separators to forward slashes
html_path = html_path.replace(os.sep, "/")
all_html_files.add(html_path)
# Process explicit redirects from REDIRECT_MAP
for page_old, page_new in REDIRECT_MAP.items():
# Convert .ipynb to .md for path calculation
@@ -847,6 +875,24 @@ def on_post_build(config):
_write_html(site_dir, old_html_path, new_html_path)
# Create catch-all redirects for any HTML files not explicitly redirected
catchall_url = "https://docs.langchain.com/oss/python/langgraph/overview"
for html_file in all_html_files:
# Skip if this file is already explicitly redirected
if html_file in redirected_paths:
continue
# Skip the root index.html (we handle that separately)
if html_file == "index.html":
continue
# Skip reference documentation (keep those accessible)
if html_file.startswith("reference/"):
continue
# Create redirect for this unmapped file
_write_html(site_dir, html_file, catchall_url)
# Create root index.html redirect
root_redirect_html = """<!doctype html>
<html lang="en">
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 LangChain, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -97,7 +97,7 @@ class PostgresSaver(BasePostgresSaver):
strict=False,
):
cur.execute(migration)
cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
cur.execute("INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,))
if self.pipe:
self.pipe.sync()
@@ -102,7 +102,9 @@ class AsyncPostgresSaver(BasePostgresSaver):
strict=False,
):
await cur.execute(migration)
await cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
await cur.execute(
"INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,)
)
if self.pipe:
await self.pipe.sync()
@@ -81,7 +81,7 @@ MIGRATIONS = [
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS checkpoint_writes_thread_id_idx ON checkpoint_writes(thread_id);
""",
"""ALTER TABLE checkpoint_writes ADD COLUMN task_path TEXT NOT NULL DEFAULT '';""",
"""ALTER TABLE checkpoint_writes ADD COLUMN IF NOT EXISTS task_path TEXT NOT NULL DEFAULT '';""",
]
SELECT_SQL = """
@@ -77,7 +77,7 @@ MIGRATIONS = [
CREATE INDEX CONCURRENTLY IF NOT EXISTS checkpoint_writes_thread_id_idx ON checkpoint_writes(thread_id);
""",
"""
ALTER TABLE checkpoint_writes ADD COLUMN task_path TEXT NOT NULL DEFAULT '';
ALTER TABLE checkpoint_writes ADD COLUMN IF NOT EXISTS task_path TEXT NOT NULL DEFAULT '';
""",
]
@@ -252,7 +252,7 @@ class ShallowPostgresSaver(BasePostgresSaver):
strict=False,
):
cur.execute(migration)
cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
cur.execute("INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,))
if self.pipe:
self.pipe.sync()
@@ -614,7 +614,9 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
strict=False,
):
await cur.execute(migration)
await cur.execute(f"INSERT INTO checkpoint_migrations (v) VALUES ({v})")
await cur.execute(
"INSERT INTO checkpoint_migrations (v) VALUES (%s)", (v,)
)
if self.pipe:
await self.pipe.sync()
@@ -91,7 +91,12 @@ WHERE expires_at IS NOT NULL;
VECTOR_MIGRATIONS: Sequence[Migration] = [
Migration(
"""
CREATE EXTENSION IF NOT EXISTS vector;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN
CREATE EXTENSION vector;
END IF;
END $$;
""",
),
Migration(
+5 -2
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint-postgres"
version = "3.0.0"
version = "3.0.1"
description = "Library with a Postgres implementation of LangGraph checkpoint saver."
authors = []
requires-python = ">=3.10"
@@ -19,7 +19,10 @@ dependencies = [
]
[project.urls]
Repository = "https://www.github.com/langchain-ai/langgraph"
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-postgres"
Twitter = "https://x.com/LangChainAI"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
[dependency-groups]
test = [
+52 -52
View File
@@ -234,7 +234,7 @@ wheels = [
[[package]]
name = "langgraph-checkpoint"
version = "3.0.0"
version = "3.0.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -244,7 +244,7 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "langchain-core", specifier = ">=0.2.38" },
{ name = "ormsgpack", specifier = ">=1.10.0" },
{ name = "ormsgpack", specifier = ">=1.12.0" },
]
[package.metadata.requires-dev]
@@ -281,7 +281,7 @@ test = [
[[package]]
name = "langgraph-checkpoint-postgres"
version = "3.0.0"
version = "3.0.1"
source = { editable = "." }
dependencies = [
{ name = "langgraph-checkpoint" },
@@ -505,57 +505,57 @@ wheels = [
[[package]]
name = "ormsgpack"
version = "1.11.0"
version = "1.12.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/65/f8/224c342c0e03e131aaa1a1f19aa2244e167001783a433f4eed10eedd834b/ormsgpack-1.11.0.tar.gz", hash = "sha256:7c9988e78fedba3292541eb3bb274fa63044ef4da2ddb47259ea70c05dee4206", size = 49357, upload-time = "2025-10-08T17:29:15.621Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6c/67/d5ef41c3b4a94400be801984ef7c7fc9623e1a82b643e74eeec367e7462b/ormsgpack-1.12.0.tar.gz", hash = "sha256:94be818fdbb0285945839b88763b269987787cb2f7ef280cad5d6ec815b7e608", size = 49959, upload-time = "2025-11-04T18:30:10.083Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/3d/6996193cb2babc47fc92456223bef7d141065357ad4204eccf313f47a7b3/ormsgpack-1.11.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:03d4e658dd6e1882a552ce1d13cc7b49157414e7d56a4091fbe7823225b08cba", size = 367965, upload-time = "2025-10-08T17:28:06.736Z" },
{ url = "https://files.pythonhosted.org/packages/35/89/c83b805dd9caebb046f4ceeed3706d0902ed2dbbcf08b8464e89f2c52e05/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bb67eb913c2b703f0ed39607fc56e50724dd41f92ce080a586b4d6149eb3fe4", size = 195209, upload-time = "2025-10-08T17:28:08.395Z" },
{ url = "https://files.pythonhosted.org/packages/3a/17/427d9c4f77b120f0af01d7a71d8144771c9388c2a81f712048320e31353b/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e54175b92411f73a238e5653a998627f6660de3def37d9dd7213e0fd264ca56", size = 205868, upload-time = "2025-10-08T17:28:09.688Z" },
{ url = "https://files.pythonhosted.org/packages/82/32/a9ce218478bdbf3fee954159900e24b314ab3064f7b6a217ccb1e3464324/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca2b197f4556e1823d1319869d4c5dc278be335286d2308b0ed88b59a5afcc25", size = 207391, upload-time = "2025-10-08T17:28:11.031Z" },
{ url = "https://files.pythonhosted.org/packages/7a/d3/4413fe7454711596fdf08adabdfa686580e4656702015108e4975f00a022/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc62388262f58c792fe1e450e1d9dbcc174ed2fb0b43db1675dd7c5ff2319d6a", size = 377078, upload-time = "2025-10-08T17:28:12.39Z" },
{ url = "https://files.pythonhosted.org/packages/f0/ad/13fae555a45e35ca1ca929a27c9ee0a3ecada931b9d44454658c543f9b9c/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c48bc10af74adfbc9113f3fb160dc07c61ad9239ef264c17e449eba3de343dc2", size = 470776, upload-time = "2025-10-08T17:28:13.484Z" },
{ url = "https://files.pythonhosted.org/packages/36/60/51178b093ffc4e2ef3381013a67223e7d56224434fba80047249f4a84b26/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a608d3a1d4fa4acdc5082168a54513cff91f47764cef435e81a483452f5f7647", size = 380862, upload-time = "2025-10-08T17:28:14.747Z" },
{ url = "https://files.pythonhosted.org/packages/a6/e3/1cb6c161335e2ae7d711ecfb007a31a3936603626e347c13e5e53b7c7cf8/ormsgpack-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:97217b4f7f599ba45916b9c4c4b1d5656e8e2a4d91e2e191d72a7569d3c30923", size = 112058, upload-time = "2025-10-08T17:28:15.777Z" },
{ url = "https://files.pythonhosted.org/packages/a4/7c/90164d00e8e94b48eff8a17bc2f4be6b71ae356a00904bc69d5e8afe80fb/ormsgpack-1.11.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c7be823f47d8e36648d4bc90634b93f02b7d7cc7480081195f34767e86f181fb", size = 367964, upload-time = "2025-10-08T17:28:16.778Z" },
{ url = "https://files.pythonhosted.org/packages/7b/c2/fb6331e880a3446c1341e72c77bd5a46da3e92a8e2edf7ea84a4c6c14fff/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68accf15d1b013812755c0eb7a30e1fc2f81eb603a1a143bf0cda1b301cfa797", size = 195209, upload-time = "2025-10-08T17:28:17.796Z" },
{ url = "https://files.pythonhosted.org/packages/18/50/4943fb5df8cc02da6b7b1ee2c2a7fb13aebc9f963d69280b1bb02b1fb178/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:805d06fb277d9a4e503c0c707545b49cde66cbb2f84e5cf7c58d81dfc20d8658", size = 205869, upload-time = "2025-10-08T17:28:19.01Z" },
{ url = "https://files.pythonhosted.org/packages/1c/fa/e7e06835bfea9adeef43915143ce818098aecab0cbd3df584815adf3e399/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1e57cdf003e77acc43643bda151dc01f97147a64b11cdee1380bb9698a7601c", size = 207391, upload-time = "2025-10-08T17:28:20.352Z" },
{ url = "https://files.pythonhosted.org/packages/33/f0/f28a19e938a14ec223396e94f4782fbcc023f8c91f2ab6881839d3550f32/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37fc05bdaabd994097c62e2f3e08f66b03f856a640ede6dc5ea340bd15b77f4d", size = 377081, upload-time = "2025-10-08T17:28:21.926Z" },
{ url = "https://files.pythonhosted.org/packages/4f/e3/73d1d7287637401b0b6637e30ba9121e1aa1d9f5ea185ed9834ca15d512c/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a6e9db6c73eb46b2e4d97bdffd1368a66f54e6806b563a997b19c004ef165e1d", size = 470779, upload-time = "2025-10-08T17:28:22.993Z" },
{ url = "https://files.pythonhosted.org/packages/9c/46/7ba7f9721e766dd0dfe4cedf444439447212abffe2d2f4538edeeec8ccbd/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e9c44eae5ac0196ffc8b5ed497c75511056508f2303fa4d36b208eb820cf209e", size = 380865, upload-time = "2025-10-08T17:28:24.012Z" },
{ url = "https://files.pythonhosted.org/packages/a7/7d/bb92a0782bbe0626c072c0320001410cf3f6743ede7dc18f034b1a18edef/ormsgpack-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:11d0dfaf40ae7c6de4f7dbd1e4892e2e6a55d911ab1774357c481158d17371e4", size = 112058, upload-time = "2025-10-08T17:28:25.015Z" },
{ url = "https://files.pythonhosted.org/packages/28/1a/f07c6f74142815d67e1d9d98c5b2960007100408ade8242edac96d5d1c73/ormsgpack-1.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:0c63a3f7199a3099c90398a1bdf0cb577b06651a442dc5efe67f2882665e5b02", size = 105894, upload-time = "2025-10-08T17:28:25.93Z" },
{ url = "https://files.pythonhosted.org/packages/1e/16/2805ebfb3d2cbb6c661b5fae053960fc90a2611d0d93e2207e753e836117/ormsgpack-1.11.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3434d0c8d67de27d9010222de07fb6810fb9af3bb7372354ffa19257ac0eb83b", size = 368474, upload-time = "2025-10-08T17:28:27.532Z" },
{ url = "https://files.pythonhosted.org/packages/6f/39/6afae47822dca0ce4465d894c0bbb860a850ce29c157882dbdf77a5dd26e/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2da5bd097e8dbfa4eb0d4ccfe79acd6f538dee4493579e2debfe4fc8f4ca89b", size = 195321, upload-time = "2025-10-08T17:28:28.573Z" },
{ url = "https://files.pythonhosted.org/packages/f6/54/11eda6b59f696d2f16de469bfbe539c9f469c4b9eef5a513996b5879c6e9/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fdbaa0a5a8606a486960b60c24f2d5235d30ac7a8b98eeaea9854bffef14dc3d", size = 206036, upload-time = "2025-10-08T17:28:29.785Z" },
{ url = "https://files.pythonhosted.org/packages/1e/86/890430f704f84c4699ddad61c595d171ea2fd77a51fbc106f83981e83939/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3682f24f800c1837017ee90ce321086b2cbaef88db7d4cdbbda1582aa6508159", size = 207615, upload-time = "2025-10-08T17:28:31.076Z" },
{ url = "https://files.pythonhosted.org/packages/b6/b9/77383e16c991c0ecb772205b966fc68d9c519e0b5f9c3913283cbed30ffe/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fcca21202bb05ccbf3e0e92f560ee59b9331182e4c09c965a28155efbb134993", size = 377195, upload-time = "2025-10-08T17:28:32.436Z" },
{ url = "https://files.pythonhosted.org/packages/20/e2/15f9f045d4947f3c8a5e0535259fddf027b17b1215367488b3565c573b9d/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c30e5c4655ba46152d722ec7468e8302195e6db362ec1ae2c206bc64f6030e43", size = 470960, upload-time = "2025-10-08T17:28:33.556Z" },
{ url = "https://files.pythonhosted.org/packages/b8/61/403ce188c4c495bc99dff921a0ad3d9d352dd6d3c4b629f3638b7f0cf79b/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7138a341f9e2c08c59368f03d3be25e8b87b3baaf10d30fb1f6f6b52f3d47944", size = 381174, upload-time = "2025-10-08T17:28:34.781Z" },
{ url = "https://files.pythonhosted.org/packages/14/a8/94c94bc48c68da4374870a851eea03fc5a45eb041182ad4c5ed9acfc05a4/ormsgpack-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d4bd8589b78a11026d47f4edf13c1ceab9088bb12451f34396afe6497db28a27", size = 112314, upload-time = "2025-10-08T17:28:36.259Z" },
{ url = "https://files.pythonhosted.org/packages/19/d0/aa4cf04f04e4cc180ce7a8d8ddb5a7f3af883329cbc59645d94d3ba157a5/ormsgpack-1.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:e5e746a1223e70f111d4001dab9585ac8639eee8979ca0c8db37f646bf2961da", size = 106072, upload-time = "2025-10-08T17:28:37.518Z" },
{ url = "https://files.pythonhosted.org/packages/8b/35/e34722edb701d053cf2240f55974f17b7dbfd11fdef72bd2f1835bcebf26/ormsgpack-1.11.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e7b36ab7b45cb95217ae1f05f1318b14a3e5ef73cb00804c0f06233f81a14e8", size = 368502, upload-time = "2025-10-08T17:28:38.547Z" },
{ url = "https://files.pythonhosted.org/packages/2f/6a/c2fc369a79d6aba2aa28c8763856c95337ac7fcc0b2742185cd19397212a/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43402d67e03a9a35cc147c8c03f0c377cad016624479e1ee5b879b8425551484", size = 195344, upload-time = "2025-10-08T17:28:39.554Z" },
{ url = "https://files.pythonhosted.org/packages/8b/6a/0f8e24b7489885534c1a93bdba7c7c434b9b8638713a68098867db9f254c/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:64fd992f932764d6306b70ddc755c1bc3405c4c6a69f77a36acf7af1c8f5ada4", size = 206045, upload-time = "2025-10-08T17:28:40.561Z" },
{ url = "https://files.pythonhosted.org/packages/99/71/8b460ba264f3c6f82ef5b1920335720094e2bd943057964ce5287d6df83a/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0362fb7fe4a29c046c8ea799303079a09372653a1ce5a5a588f3bbb8088368d0", size = 207641, upload-time = "2025-10-08T17:28:41.736Z" },
{ url = "https://files.pythonhosted.org/packages/50/cf/f369446abaf65972424ed2651f2df2b7b5c3b735c93fc7fa6cfb81e34419/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:de2f7a65a9d178ed57be49eba3d0fc9b833c32beaa19dbd4ba56014d3c20b152", size = 377211, upload-time = "2025-10-08T17:28:43.12Z" },
{ url = "https://files.pythonhosted.org/packages/2f/3f/948bb0047ce0f37c2efc3b9bb2bcfdccc61c63e0b9ce8088d4903ba39dcf/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f38cfae95461466055af966fc922d06db4e1654966385cda2828653096db34da", size = 470973, upload-time = "2025-10-08T17:28:44.465Z" },
{ url = "https://files.pythonhosted.org/packages/31/a4/92a8114d1d017c14aaa403445060f345df9130ca532d538094f38e535988/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c88396189d238f183cea7831b07a305ab5c90d6d29b53288ae11200bd956357b", size = 381161, upload-time = "2025-10-08T17:28:46.063Z" },
{ url = "https://files.pythonhosted.org/packages/d0/64/5b76447da654798bfcfdfd64ea29447ff2b7f33fe19d0e911a83ad5107fc/ormsgpack-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:5403d1a945dd7c81044cebeca3f00a28a0f4248b33242a5d2d82111628043725", size = 112321, upload-time = "2025-10-08T17:28:47.393Z" },
{ url = "https://files.pythonhosted.org/packages/46/5e/89900d06db9ab81e7ec1fd56a07c62dfbdcda398c435718f4252e1dc52a0/ormsgpack-1.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c57357b8d43b49722b876edf317bdad9e6d52071b523fdd7394c30cd1c67d5a0", size = 106084, upload-time = "2025-10-08T17:28:48.305Z" },
{ url = "https://files.pythonhosted.org/packages/4c/0b/c659e8657085c8c13f6a0224789f422620cef506e26573b5434defe68483/ormsgpack-1.11.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d390907d90fd0c908211592c485054d7a80990697ef4dff4e436ac18e1aab98a", size = 368497, upload-time = "2025-10-08T17:28:49.297Z" },
{ url = "https://files.pythonhosted.org/packages/1b/0e/451e5848c7ed56bd287e8a2b5cb5926e54466f60936e05aec6cb299f9143/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6153c2e92e789509098e04c9aa116b16673bd88ec78fbe0031deeb34ab642d10", size = 195385, upload-time = "2025-10-08T17:28:50.314Z" },
{ url = "https://files.pythonhosted.org/packages/4c/28/90f78cbbe494959f2439c2ec571f08cd3464c05a6a380b0d621c622122a9/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2b2c2a065a94d742212b2018e1fecd8f8d72f3c50b53a97d1f407418093446d", size = 206114, upload-time = "2025-10-08T17:28:51.336Z" },
{ url = "https://files.pythonhosted.org/packages/fb/db/34163f4c0923bea32dafe42cd878dcc66795a3e85669bc4b01c1e2b92a7b/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:110e65b5340f3d7ef8b0009deae3c6b169437e6b43ad5a57fd1748085d29d2ac", size = 207679, upload-time = "2025-10-08T17:28:53.627Z" },
{ url = "https://files.pythonhosted.org/packages/b6/14/04ee741249b16f380a9b4a0cc19d4134d0b7c74bab27a2117da09e525eb9/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c27e186fca96ab34662723e65b420919910acbbc50fc8e1a44e08f26268cb0e0", size = 377237, upload-time = "2025-10-08T17:28:56.12Z" },
{ url = "https://files.pythonhosted.org/packages/89/ff/53e588a6aaa833237471caec679582c2950f0e7e1a8ba28c1511b465c1f4/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d56b1f877c13d499052d37a3db2378a97d5e1588d264f5040b3412aee23d742c", size = 471021, upload-time = "2025-10-08T17:28:57.299Z" },
{ url = "https://files.pythonhosted.org/packages/a6/f9/f20a6d9ef2be04da3aad05e8f5699957e9a30c6d5c043a10a296afa7e890/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c88e28cd567c0a3269f624b4ade28142d5e502c8e826115093c572007af5be0a", size = 381205, upload-time = "2025-10-08T17:28:58.872Z" },
{ url = "https://files.pythonhosted.org/packages/f8/64/96c07d084b479ac8b7821a77ffc8d3f29d8b5c95ebfdf8db1c03dff02762/ormsgpack-1.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:8811160573dc0a65f62f7e0792c4ca6b7108dfa50771edb93f9b84e2d45a08ae", size = 112374, upload-time = "2025-10-08T17:29:00Z" },
{ url = "https://files.pythonhosted.org/packages/88/a5/5dcc18b818d50213a3cadfe336bb6163a102677d9ce87f3d2f1a1bee0f8c/ormsgpack-1.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:23e30a8d3c17484cf74e75e6134322255bd08bc2b5b295cc9c442f4bae5f3c2d", size = 106056, upload-time = "2025-10-08T17:29:01.29Z" },
{ url = "https://files.pythonhosted.org/packages/19/2b/776d1b411d2be50f77a6e6e94a25825cca55dcacfe7415fd691a144db71b/ormsgpack-1.11.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2905816502adfaf8386a01dd85f936cd378d243f4f5ee2ff46f67f6298dc90d5", size = 368661, upload-time = "2025-10-08T17:29:02.382Z" },
{ url = "https://files.pythonhosted.org/packages/a9/0c/81a19e6115b15764db3d241788f9fac093122878aaabf872cc545b0c4650/ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c04402fb9a0a9b9f18fbafd6d5f8398ee99b3ec619fb63952d3a954bc9d47daa", size = 195539, upload-time = "2025-10-08T17:29:03.472Z" },
{ url = "https://files.pythonhosted.org/packages/97/86/e5b50247a61caec5718122feb2719ea9d451d30ac0516c288c1dbc6408e8/ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a025ec07ac52056ecfd9e57b5cbc6fff163f62cb9805012b56cda599157f8ef2", size = 207718, upload-time = "2025-10-08T17:29:04.545Z" },
{ url = "https://files.pythonhosted.org/packages/4d/0c/8f45fcd22c95190b05d4fba71375d8f783a9e3b0b6aaf476812b693e3868/ormsgpack-1.12.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e08904c232358b94a682ccfbb680bc47d3fd5c424bb7dccb65974dd20c95e8e1", size = 369156, upload-time = "2025-11-04T18:29:17.629Z" },
{ url = "https://files.pythonhosted.org/packages/d2/f8/c7adc093d4ceb05e38786906815f868210f808326c27f8124b4d9466a26b/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9ed7a4b0037d69c8ba7e670e03ee65ae8d5c5114a409e73c5770d7fb5e4b895", size = 195743, upload-time = "2025-11-04T18:29:18.964Z" },
{ url = "https://files.pythonhosted.org/packages/fe/b8/bf002648fa6c150ed6157837b00303edafb35f65c352429463f83214de18/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db2928525b684f3f2af0367aef7ae8d20cde37fc5349c700017129d493a755aa", size = 206472, upload-time = "2025-11-04T18:29:19.951Z" },
{ url = "https://files.pythonhosted.org/packages/23/d6/1f445947c95a931bb189b7864a3f9dcbeebe7dcbc1b3c7387427b3779228/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45f911d9c5b23d11e49ff03fc8f9566745a2b1a7d9033733a1c0a2fa9301cd60", size = 207959, upload-time = "2025-11-04T18:29:21.282Z" },
{ url = "https://files.pythonhosted.org/packages/5c/9c/dd8ccd7553a5c1d0b4b69a0541611983a2f9bc2e8c5708a4bfffc0100468/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:98c54ae6fd682b2aceb264505af9b2255f3df9d84e6e4369bc44d2110f1f311d", size = 377659, upload-time = "2025-11-04T18:29:22.561Z" },
{ url = "https://files.pythonhosted.org/packages/3d/08/3282d8f6330e742d4cdbcfbe2c100b403ae5526ae82a1c1b6a11b7967e37/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:857ab987c3502de08258cc4baf0e87267cb2c80931601084e13df3c355b1ab9d", size = 471391, upload-time = "2025-11-04T18:29:23.663Z" },
{ url = "https://files.pythonhosted.org/packages/18/d4/94a2fbfd4837754bda7a099b6a23b9d40aba9e76e1af8b8fb8133612eb54/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27579d45dc502ee736238e1024559cb0a01aa72a3b68827448b8edf6a2dcdc9c", size = 381501, upload-time = "2025-11-04T18:29:24.771Z" },
{ url = "https://files.pythonhosted.org/packages/d8/c6/1a9fa122cb5deb10b067bbaa43165b12291a914cc0ce364988ff17bbf405/ormsgpack-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c78379d054760875540cf2e81f28da1bb78d09fda3eabdbeb6c53b3e297158cb", size = 112715, upload-time = "2025-11-04T18:29:26.016Z" },
{ url = "https://files.pythonhosted.org/packages/1a/ba/3cae83cf36420c1c8dd294f16c852c03313aafe2439a165c4c6ac611b1d0/ormsgpack-1.12.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c40d86d77391b18dd34de5295e3de2b8ad818bcab9c9def4121c8ec5c9714ae4", size = 369159, upload-time = "2025-11-04T18:29:27.057Z" },
{ url = "https://files.pythonhosted.org/packages/97/d4/5e176309e01a8b9098d80201aac1eb7db9336c3b5b4fa6254a2bbb0d0fa0/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:777b7fab364dc0f200bb382a98a385c8222ffa6a2333d627d763797326202c86", size = 195744, upload-time = "2025-11-04T18:29:28.069Z" },
{ url = "https://files.pythonhosted.org/packages/4f/83/6d80c8c5571639c000a39f38f77752dfaf9d9e552d775331e8d280f66a4e/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b5089ad9dd5b3d3013b245a55e4abaea2f8ad70f4a78e1b002127b02340004", size = 206474, upload-time = "2025-11-04T18:29:29.034Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e6/940311e48dc0cfc3e212bd7007a21ed0825158638057687d804f2c5c2cca/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaf0c87cace7bc08fbf68c5cc66605b593df6427e9f4de235b2da358787e008", size = 207959, upload-time = "2025-11-04T18:29:30.315Z" },
{ url = "https://files.pythonhosted.org/packages/1a/e3/fbe94b0a311815343b86a95a0627e4901b11ff6fd522679ca29a2a88c99b/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f62d476fe28bc5675d9aff30341bfa9f41d7de332c5b63fbbe9aaf6bb7ec74d4", size = 377666, upload-time = "2025-11-04T18:29:31.38Z" },
{ url = "https://files.pythonhosted.org/packages/a3/3b/229cfa28076798ffb619aaa854b842de3f2ed5ea4e6509bf34d14c038c4d/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ded7810095b887e28434f32f5a345d354e88cf851bab3c5435aeb86a718618d2", size = 471394, upload-time = "2025-11-04T18:29:32.521Z" },
{ url = "https://files.pythonhosted.org/packages/6b/bd/4eae4ab35586e4175c07acb5f98aec83aa9d8987f71ea0443aa900191bdf/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f72a1dea0c4ae7c4101dcfbe8133f274a9d769d0b87fe5188db4fab07ffabaee", size = 381506, upload-time = "2025-11-04T18:29:33.533Z" },
{ url = "https://files.pythonhosted.org/packages/dd/51/f9d56d6d015cbfa1ce9a4358ca30a41744644f0cf606e060d7203efe5af8/ormsgpack-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f479bfef847255d7d0b12c7a198f6a21490155da2da3062e082ba370893d4a1", size = 112707, upload-time = "2025-11-04T18:29:34.898Z" },
{ url = "https://files.pythonhosted.org/packages/f4/07/bb189ef7072979f2f96e8716e952172efdce9c54930aa0814bec73aee19b/ormsgpack-1.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:3583ca410e4502144b2594170542e4bbef7b15643fd1208703ae820f11029036", size = 106533, upload-time = "2025-11-04T18:29:36.112Z" },
{ url = "https://files.pythonhosted.org/packages/a2/f2/c1036b2775fcc0cfa5fd618c53bcd3b862ee07298fb627f03af4c7982f84/ormsgpack-1.12.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e0c1e08b64d99076fee155276097489b82cc56e8d5951c03c721a65a32f44494", size = 369538, upload-time = "2025-11-04T18:29:37.125Z" },
{ url = "https://files.pythonhosted.org/packages/d9/ca/526c4ae02f3cb34621af91bf8282a10d666757c2e0c6ff391ff5d403d607/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd43bcb299131690b8e0677af172020b2ada8e625169034b42ac0c13adf84aa", size = 195872, upload-time = "2025-11-04T18:29:38.34Z" },
{ url = "https://files.pythonhosted.org/packages/7f/0f/83bb7968e9715f6a85be53d041b1e6324a05428f56b8b980dac866886871/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0149d595341e22ead340bf281b2995c4cc7dc8d522a6b5f575fe17aa407604", size = 206469, upload-time = "2025-11-04T18:29:39.749Z" },
{ url = "https://files.pythonhosted.org/packages/02/e3/9e93ca1065f2d4af035804a842b1ff3025bab580c7918239bb225cd1fee2/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19a1b27d169deb553c80fd10b589fc2be1fc14cee779fae79fcaf40db04de2b", size = 208273, upload-time = "2025-11-04T18:29:40.769Z" },
{ url = "https://files.pythonhosted.org/packages/b3/d8/6d6ef901b3a8b8f3ab8836b135a56eb7f66c559003e251d9530bedb12627/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f28896942d655064940dfe06118b7ce1e3468d051483148bf02c99ec157483a", size = 377839, upload-time = "2025-11-04T18:29:42.092Z" },
{ url = "https://files.pythonhosted.org/packages/4c/72/fcb704bfa4c2c3a37b647d597cc45a13cffc9d50baac635a9ad620731d29/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9396efcfa48b4abbc06e44c5dbc3c4574a8381a80cb4cd01eea15d28b38c554e", size = 471446, upload-time = "2025-11-04T18:29:43.133Z" },
{ url = "https://files.pythonhosted.org/packages/84/f8/402e4e3eb997c2ee534c99bec4b5bb359c2a1f9edadf043e254a71e11378/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96586ed537a5fb386a162c4f9f7d8e6f76e07b38a990d50c73f11131e00ff040", size = 381783, upload-time = "2025-11-04T18:29:44.466Z" },
{ url = "https://files.pythonhosted.org/packages/f0/8d/5897b700360bc00911b70ae5ef1134ee7abf5baa81a92a4be005917d3dfd/ormsgpack-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e70387112fb3870e4844de090014212cdcf1342f5022047aecca01ec7de05d7a", size = 112943, upload-time = "2025-11-04T18:29:45.468Z" },
{ url = "https://files.pythonhosted.org/packages/5b/44/1e73649f79bb96d6cf9e5bcbac68b6216d238bba80af351c4c0cbcf7ee15/ormsgpack-1.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:d71290a23de5d4829610c42665d816c661ecad8979883f3f06b2e3ab9639962e", size = 106688, upload-time = "2025-11-04T18:29:46.411Z" },
{ url = "https://files.pythonhosted.org/packages/2e/e8/35f11ce9313111488b26b3035e4cbe55caa27909c0b6c8b5b5cd59f9661e/ormsgpack-1.12.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:766f2f3b512d85cd375b26a8b1329b99843560b50b93d3880718e634ad4a5de5", size = 369574, upload-time = "2025-11-04T18:29:47.431Z" },
{ url = "https://files.pythonhosted.org/packages/61/b0/77461587f412d4e598d3687bafe23455ed0f26269f44be20252eddaa624e/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84b285b1f3f185aad7da45641b873b30acfd13084cf829cf668c4c6480a81583", size = 195893, upload-time = "2025-11-04T18:29:48.735Z" },
{ url = "https://files.pythonhosted.org/packages/c6/67/e197ceb04c3b550589e5407fc9fdae10f4e2e2eba5fdac921a269e02e974/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e23604fc79fe110292cb365f4c8232e64e63a34f470538be320feae3921f271b", size = 206503, upload-time = "2025-11-04T18:29:49.99Z" },
{ url = "https://files.pythonhosted.org/packages/0b/b1/7fa8ba82a25cef678983c7976f85edeef5014f5c26495f338258e6a3cf1c/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc32b156c113a0fae2975051417d8d9a7a5247c34b2d7239410c46b75ce9348a", size = 208257, upload-time = "2025-11-04T18:29:51.007Z" },
{ url = "https://files.pythonhosted.org/packages/ce/b1/759e999390000d2589e6d0797f7265e6ec28378547075d28d3736248ab63/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:94ac500dd10c20fa8b8a23bc55606250bfe711bf9716828d9f3d44dfd1f25668", size = 377852, upload-time = "2025-11-04T18:29:52.103Z" },
{ url = "https://files.pythonhosted.org/packages/51/e7/0af737c94272494d9d84a3c29cc42c973ef7fd2342917020906596db863c/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c5201ff7ec24f721f813a182885a17064cffdbe46b2412685a52e6374a872c8f", size = 471456, upload-time = "2025-11-04T18:29:53.336Z" },
{ url = "https://files.pythonhosted.org/packages/f4/ba/c81f0aa4f19fbf457213395945b672e6fde3ce777e3587456e7f0fca2147/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a9740bb3839c9368aacae1cbcfc474ee6976458f41cc135372b7255d5206c953", size = 381813, upload-time = "2025-11-04T18:29:54.394Z" },
{ url = "https://files.pythonhosted.org/packages/ce/15/429c72d64323503fd42cc4ca8398930ded8aa8b3470df8a86b3bbae7a35c/ormsgpack-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ed37f29772432048b58174e920a1d4c4cde0404a5d448d3d8bbcc95d86a6918", size = 112949, upload-time = "2025-11-04T18:29:55.371Z" },
{ url = "https://files.pythonhosted.org/packages/55/b9/e72c451a40f8c57bfc229e0b8e536ecea7203c8f0a839676df2ffb605c62/ormsgpack-1.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:b03994bbec5d6d42e03d6604e327863f885bde67aa61e06107ce1fa5bdd3e71d", size = 106689, upload-time = "2025-11-04T18:29:56.262Z" },
{ url = "https://files.pythonhosted.org/packages/13/16/13eab1a75da531b359105fdee90dda0b6bd1ca0a09880250cf91d8bdfdea/ormsgpack-1.12.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0f3981ba3cba80656012090337e548e597799e14b41e3d0b595ab5ab05a23d7f", size = 369620, upload-time = "2025-11-04T18:29:57.255Z" },
{ url = "https://files.pythonhosted.org/packages/a0/c1/cbcc38b7af4ce58d8893e56d3595c0c8dcd117093bf048f889cf351bdba0/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:901f6f55184d6776dbd5183cbce14caf05bf7f467eef52faf9b094686980bf71", size = 195925, upload-time = "2025-11-04T18:29:58.34Z" },
{ url = "https://files.pythonhosted.org/packages/5c/59/4fa4dc0681490e12b75333440a1c0fd9741b0ebff272b1db4a29d35c2021/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13b15412571422b711b40f45e3fe6d993ea3314b5e97d1a853fe99226c5effc", size = 206594, upload-time = "2025-11-04T18:29:59.329Z" },
{ url = "https://files.pythonhosted.org/packages/39/67/249770896bc32bb91b22c30256961f935d0915cbcf6e289a7fc961d9b14c/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91fa8a452553a62e5fb3fbab471e7faf7b3bec3c87a2f355ebf3d7aab290fe4f", size = 208307, upload-time = "2025-11-04T18:30:00.377Z" },
{ url = "https://files.pythonhosted.org/packages/07/0a/e041a248cd72f2f4c07e155913e0a3ede4c86cf21a40ae6cd79f135f2847/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74ec101f69624695eec4ce7c953192d97748254abe78fb01b591f06d529e1952", size = 377844, upload-time = "2025-11-04T18:30:01.389Z" },
{ url = "https://files.pythonhosted.org/packages/d8/71/6f7773e4ffda73a358ce4bba69b3e8bee9d40a7a06315e4c1cd7a3ea9d02/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9bbf7896580848326c1f9bd7531f264e561f98db7e08e15aa75963d83832c717", size = 471572, upload-time = "2025-11-04T18:30:02.486Z" },
{ url = "https://files.pythonhosted.org/packages/65/29/af6769a4289c07acc71e7bda1d64fb31800563147d73142686e185e82348/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7567917da613b8f8d591c1674e411fd3404bea41ef2b9a0e0a1e049c0f9406d7", size = 381842, upload-time = "2025-11-04T18:30:03.799Z" },
{ url = "https://files.pythonhosted.org/packages/0b/dd/0a86195ee7a1a96c088aefc8504385e881cf56f4563ed81bafe21cbf1fb0/ormsgpack-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e418256c5d8622b8bc92861936f7c6a0131355e7bcad88a42102ae8227f8a1c", size = 113008, upload-time = "2025-11-04T18:30:04.777Z" },
{ url = "https://files.pythonhosted.org/packages/4c/57/fafc79e32f3087f6f26f509d80b8167516326bfea38d30502627c01617e0/ormsgpack-1.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:433ace29aa02713554f714c62a4e4dcad0c9e32674ba4f66742c91a4c3b1b969", size = 106648, upload-time = "2025-11-04T18:30:05.708Z" },
{ url = "https://files.pythonhosted.org/packages/b3/cf/5d58d9b132128d2fe5d586355dde76af386554abef00d608f66b913bff1f/ormsgpack-1.12.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e57164be4ca34b64e210ec515059193280ac84df4d6f31a6fcbfb2fc8436de55", size = 369803, upload-time = "2025-11-04T18:30:06.728Z" },
{ url = "https://files.pythonhosted.org/packages/67/42/968a2da361eaff2e4cbb17c82c7599787babf16684110ad70409646cc1e4/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:904f96289deaa92fc6440b122edc27c5bdc28234edd63717f6d853d88c823a83", size = 195991, upload-time = "2025-11-04T18:30:07.713Z" },
{ url = "https://files.pythonhosted.org/packages/03/f0/9696c6c6cf8ad35170f0be8d0ef3523cc258083535f6c8071cb8235ebb8b/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b291d086e524a1062d57d1b7b5a8bcaaf29caebf0212fec12fd86240bd33633", size = 208316, upload-time = "2025-11-04T18:30:08.663Z" },
]
[[package]]
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 LangChain, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -329,8 +329,9 @@ class SqliteSaver(BaseCheckpointSaver[str]):
FROM checkpoints
{where}
ORDER BY checkpoint_id DESC"""
if limit:
query += f" LIMIT {limit}"
if limit is not None:
query += " LIMIT ?"
param_values = (*param_values, limit)
with self.cursor(transaction=False) as cur, closing(self.conn.cursor()) as wcur:
cur.execute(query, param_values)
for (
@@ -425,8 +425,9 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
FROM checkpoints
{where}
ORDER BY checkpoint_id DESC"""
if limit:
query += f" LIMIT {limit}"
if limit is not None:
query += " LIMIT ?"
params = (*params, limit)
async with (
self.lock,
self.conn.execute(query, params) as cur,
@@ -1,6 +1,7 @@
from __future__ import annotations
import json
import re
from collections.abc import Sequence
from typing import Any
@@ -8,6 +9,23 @@ from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.base import get_checkpoint_id
def _validate_filter_key(key: str) -> None:
"""Validate that a filter key is safe for use in SQL queries.
Args:
key: The filter key to validate
Raises:
ValueError: If the key contains invalid characters that could enable SQL injection
"""
# Allow alphanumeric characters, underscores, dots, and hyphens
# This covers typical JSON property names while preventing SQL injection
if not re.match(r"^[a-zA-Z0-9_.-]+$", key):
raise ValueError(
f"Invalid filter key: '{key}'. Filter keys must contain only alphanumeric characters, underscores, dots, and hyphens."
)
def _metadata_predicate(
metadata_filter: dict[str, Any],
) -> tuple[Sequence[str], Sequence[Any]]:
@@ -43,6 +61,7 @@ def _metadata_predicate(
# process metadata query
for query_key, query_value in metadata_filter.items():
_validate_filter_key(query_key)
operator, param_value = _where_value(query_value)
predicates.append(
f"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}"
@@ -404,12 +404,9 @@ class BaseSqliteStore:
# SQLite json_extract returns unquoted string values
if isinstance(value, str):
filter_conditions.append(
"json_extract(value, '$."
+ key
+ "') = '"
+ value.replace("'", "''")
+ "'"
"json_extract(value, '$." + key + "') = ?"
)
filter_params.append(value)
elif value is None:
filter_conditions.append(
"json_extract(value, '$." + key + "') IS NULL"
@@ -423,9 +420,11 @@ class BaseSqliteStore:
+ ("1" if value else "0")
)
elif isinstance(value, (int, float)):
# Use parameterized query to handle special floats and large integers
filter_conditions.append(
"json_extract(value, '$." + key + "') = " + str(value)
"json_extract(value, '$." + key + "') = ?"
)
filter_params.append(float(value))
else:
# Complex objects (list, dict, …) compare JSON text
filter_conditions.append(
@@ -636,85 +635,66 @@ class BaseSqliteStore:
# We need to properly format values for SQLite JSON extraction comparison
if op == "$eq":
if isinstance(value, str):
# Direct string comparison with proper quoting for unquoted json_extract result
return (
f"json_extract(value, '$.{key}') = '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') = ?", [value]
elif value is None:
return f"json_extract(value, '$.{key}') IS NULL", []
elif isinstance(value, bool):
# SQLite JSON stores booleans as integers
return f"json_extract(value, '$.{key}') = {1 if value else 0}", []
elif isinstance(value, (int, float)):
return f"json_extract(value, '$.{key}') = {value}", []
# Convert to float to handle inf, -inf, nan, and very large integers
# SQLite REAL can handle these cases better than INTEGER
return f"json_extract(value, '$.{key}') = ?", [float(value)]
else:
return f"json_extract(value, '$.{key}') = ?", [orjson.dumps(value)]
elif op == "$gt":
# For numeric values, SQLite needs to compare as numbers, not strings
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) > {value}", []
# Convert to float to handle special values and very large integers
return f"CAST(json_extract(value, '$.{key}') AS REAL) > ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') > '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') > ?", [value]
else:
return f"json_extract(value, '$.{key}') > ?", [orjson.dumps(value)]
elif op == "$gte":
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) >= {value}", []
return f"CAST(json_extract(value, '$.{key}') AS REAL) >= ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') >= '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') >= ?", [value]
else:
return f"json_extract(value, '$.{key}') >= ?", [orjson.dumps(value)]
elif op == "$lt":
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) < {value}", []
return f"CAST(json_extract(value, '$.{key}') AS REAL) < ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') < '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') < ?", [value]
else:
return f"json_extract(value, '$.{key}') < ?", [orjson.dumps(value)]
elif op == "$lte":
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) <= {value}", []
return f"CAST(json_extract(value, '$.{key}') AS REAL) <= ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') <= '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') <= ?", [value]
else:
return f"json_extract(value, '$.{key}') <= ?", [orjson.dumps(value)]
elif op == "$ne":
if isinstance(value, str):
return (
f"json_extract(value, '$.{key}') != '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') != ?", [value]
elif value is None:
return f"json_extract(value, '$.{key}') IS NOT NULL", []
elif isinstance(value, bool):
return f"json_extract(value, '$.{key}') != {1 if value else 0}", []
elif isinstance(value, (int, float)):
return f"json_extract(value, '$.{key}') != {value}", []
# Convert to float for consistency
return f"json_extract(value, '$.{key}') != ?", [float(value)]
else:
return f"json_extract(value, '$.{key}') != ?", [orjson.dumps(value)]
else:
@@ -874,85 +854,66 @@ class SqliteStore(BaseSqliteStore, BaseStore):
# We need to properly format values for SQLite JSON extraction comparison
if op == "$eq":
if isinstance(value, str):
# Direct string comparison with proper quoting for unquoted json_extract result
return (
f"json_extract(value, '$.{key}') = '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') = ?", [value]
elif value is None:
return f"json_extract(value, '$.{key}') IS NULL", []
elif isinstance(value, bool):
# SQLite JSON stores booleans as integers
return f"json_extract(value, '$.{key}') = {1 if value else 0}", []
elif isinstance(value, (int, float)):
return f"json_extract(value, '$.{key}') = {value}", []
# Convert to float to handle inf, -inf, nan, and very large integers
# SQLite REAL can handle these cases better than INTEGER
return f"json_extract(value, '$.{key}') = ?", [float(value)]
else:
return f"json_extract(value, '$.{key}') = ?", [orjson.dumps(value)]
elif op == "$gt":
# For numeric values, SQLite needs to compare as numbers, not strings
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) > {value}", []
# Convert to float to handle special values and very large integers
return f"CAST(json_extract(value, '$.{key}') AS REAL) > ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') > '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') > ?", [value]
else:
return f"json_extract(value, '$.{key}') > ?", [orjson.dumps(value)]
elif op == "$gte":
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) >= {value}", []
return f"CAST(json_extract(value, '$.{key}') AS REAL) >= ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') >= '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') >= ?", [value]
else:
return f"json_extract(value, '$.{key}') >= ?", [orjson.dumps(value)]
elif op == "$lt":
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) < {value}", []
return f"CAST(json_extract(value, '$.{key}') AS REAL) < ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') < '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') < ?", [value]
else:
return f"json_extract(value, '$.{key}') < ?", [orjson.dumps(value)]
elif op == "$lte":
if isinstance(value, (int, float)):
return f"CAST(json_extract(value, '$.{key}') AS REAL) <= {value}", []
return f"CAST(json_extract(value, '$.{key}') AS REAL) <= ?", [
float(value)
]
elif isinstance(value, str):
return (
f"json_extract(value, '$.{key}') <= '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') <= ?", [value]
else:
return f"json_extract(value, '$.{key}') <= ?", [orjson.dumps(value)]
elif op == "$ne":
if isinstance(value, str):
return (
f"json_extract(value, '$.{key}') != '"
+ value.replace("'", "''")
+ "'",
[],
)
return f"json_extract(value, '$.{key}') != ?", [value]
elif value is None:
return f"json_extract(value, '$.{key}') IS NOT NULL", []
elif isinstance(value, bool):
return f"json_extract(value, '$.{key}') != {1 if value else 0}", []
elif isinstance(value, (int, float)):
return f"json_extract(value, '$.{key}') != {value}", []
# Convert to float for consistency
return f"json_extract(value, '$.{key}') != ?", [float(value)]
else:
return f"json_extract(value, '$.{key}') != ?", [orjson.dumps(value)]
else:
+4 -1
View File
@@ -18,7 +18,10 @@ dependencies = [
]
[project.urls]
Repository = "https://www.github.com/langchain-ai/langgraph"
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-sqlite"
Twitter = "https://x.com/LangChainAI"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
[dependency-groups]
test = [
+75 -1
View File
@@ -113,4 +113,78 @@ class TestAsyncSqliteSaver:
search_results_5[1].config["configurable"]["checkpoint_ns"],
} == {"", "inner"}
# TODO: test before and limit params
# Test limit param
search_results_6 = [
c
async for c in saver.alist(
{"configurable": {"thread_id": "thread-2"}}, limit=1
)
]
assert len(search_results_6) == 1
assert search_results_6[0].config["configurable"]["thread_id"] == "thread-2"
# Test before param
search_results_7 = [
c async for c in saver.alist(None, before=search_results_5[1].config)
]
assert len(search_results_7) == 1
assert search_results_7[0].config["configurable"]["thread_id"] == "thread-1"
async def test_limit_parameter_sql_injection_prevention(self) -> None:
"""Test that the limit parameter properly uses parameterized queries to prevent SQL injection."""
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
# Setup: Create multiple checkpoints
for i in range(5):
config: RunnableConfig = {
"configurable": {
"thread_id": f"thread-{i}",
"checkpoint_ns": "",
}
}
checkpoint = empty_checkpoint()
metadata: CheckpointMetadata = {"index": i}
await saver.aput(config, checkpoint, metadata, {})
# Test that limit works correctly with valid integer
results = [c async for c in saver.alist(None, limit=2)]
assert len(results) == 2
# Test that limit=0 returns no results
results = [c async for c in saver.alist(None, limit=0)]
assert len(results) == 0
# Test that limit=None returns all results
results = [c async for c in saver.alist(None, limit=None)]
assert len(results) == 5
# Test explicit SQL injection attempt via limit parameter
# Even if type checking is bypassed and a malicious string is passed,
# the parameterized query will treat it as a value, not SQL code
# This would cause an error (can't convert string to int for LIMIT),
# which is the correct secure behavior
malicious_limits = [
"1; DROP TABLE checkpoints; --",
"1 OR 1=1",
"999999 UNION SELECT * FROM checkpoints",
]
for malicious_limit in malicious_limits:
# The parameterized query should safely reject non-integer limits
# or convert them in a way that prevents SQL injection
try:
# Bypass type checking by casting
results = [
c
async for c in saver.alist(None, limit=malicious_limit) # type: ignore
]
# If it doesn't raise an error, it should at least not execute the injection
# SQLite's parameter binding will try to convert the string to an integer
# which will either fail or treat it as 0
except Exception:
# Expected: SQLite should reject invalid limit values
pass
# Verify the checkpoints table still exists and has all data
# (would have been dropped if injection succeeded)
results = [c async for c in saver.alist(None, limit=None)]
assert len(results) == 5
+117
View File
@@ -182,3 +182,120 @@ class TestSqliteSaver:
with pytest.raises(NotImplementedError, match="AsyncSqliteSaver"):
async for _ in saver.alist(self.config_1):
pass
def test_metadata_predicate_sql_injection_prevention(self) -> None:
"""Test that _metadata_predicate rejects malicious filter keys."""
# Test various SQL injection payloads
malicious_keys = [
"x') OR '1'='1", # Boolean-based injection
"x') OR 1=1 --", # Comment-based injection
"x') UNION SELECT 1,2,3,4,5,6,7 --", # UNION-based injection
"access') = 'public' OR '1'='1' OR json_extract(value, '$.", # Complex injection
"'; DROP TABLE checkpoints; --", # Destructive injection
]
for malicious_key in malicious_keys:
with pytest.raises(ValueError, match="Invalid filter key"):
_metadata_predicate({malicious_key: "dummy"})
def test_checkpoint_search_sql_injection_prevention(self) -> None:
"""Test that SQL injection via malicious filter keys is prevented in checkpoint search."""
with SqliteSaver.from_conn_string(":memory:") as saver:
# Setup: Create checkpoints with different metadata
config_public: RunnableConfig = {
"configurable": {
"thread_id": "thread-public",
"checkpoint_ns": "",
}
}
config_private: RunnableConfig = {
"configurable": {
"thread_id": "thread-private",
"checkpoint_ns": "",
}
}
checkpoint_public = empty_checkpoint()
checkpoint_private = empty_checkpoint()
metadata_public: CheckpointMetadata = {
"access": "public",
"data": "public information",
}
metadata_private: CheckpointMetadata = {
"access": "private",
"data": "secret information",
"password": "secret123",
}
saver.put(config_public, checkpoint_public, metadata_public, {})
saver.put(config_private, checkpoint_private, metadata_private, {})
# Normal query - should return only public checkpoint
normal_results = list(saver.list(None, filter={"access": "public"}))
assert len(normal_results) == 1
assert normal_results[0].metadata["access"] == "public"
# SQL injection attempt should raise ValueError
malicious_key = (
"access') = 'public' OR '1'='1' OR json_extract(metadata, '$."
)
with pytest.raises(ValueError, match="Invalid filter key"):
list(saver.list(None, filter={malicious_key: "dummy"}))
def test_limit_parameter_sql_injection_prevention(self) -> None:
"""Test that the limit parameter properly uses parameterized queries to prevent SQL injection."""
with SqliteSaver.from_conn_string(":memory:") as saver:
# Setup: Create multiple checkpoints
for i in range(5):
config: RunnableConfig = {
"configurable": {
"thread_id": f"thread-{i}",
"checkpoint_ns": "",
}
}
checkpoint = empty_checkpoint()
metadata: CheckpointMetadata = {"index": i}
saver.put(config, checkpoint, metadata, {})
# Test that limit works correctly with valid integer
results = list(saver.list(None, limit=2))
assert len(results) == 2
# Test that limit=0 returns no results
results = list(saver.list(None, limit=0))
assert len(results) == 0
# Test that limit=None returns all results
results = list(saver.list(None, limit=None))
assert len(results) == 5
# Test explicit SQL injection attempt via limit parameter
# Even if type checking is bypassed and a malicious string is passed,
# the parameterized query will treat it as a value, not SQL code
# This would cause an error (can't convert string to int for LIMIT),
# which is the correct secure behavior
malicious_limits = [
"1; DROP TABLE checkpoints; --",
"1 OR 1=1",
"999999 UNION SELECT * FROM checkpoints",
]
for malicious_limit in malicious_limits:
# The parameterized query should safely reject non-integer limits
# or convert them in a way that prevents SQL injection
try:
# Bypass type checking by casting
results = list(saver.list(None, limit=malicious_limit)) # type: ignore
# If it doesn't raise an error, it should at least not execute the injection
# SQLite's parameter binding will try to convert the string to an integer
# which will either fail or treat it as 0
except Exception:
# Expected: SQLite should reject invalid limit values
pass
# Verify the checkpoints table still exists and has all data
# (would have been dropped if injection succeeded)
results = list(saver.list(None, limit=None))
assert len(results) == 5
+104
View File
@@ -1069,6 +1069,110 @@ def test_sql_injection_vulnerability(store: SqliteStore) -> None:
store.search(("docs",), filter={malicious_key: "dummy"})
def test_sql_injection_filter_values(store: SqliteStore) -> None:
"""Test that SQL injection via malicious filter values is properly escaped."""
# Setup: Create documents with different access levels
store.put(("docs",), "doc1", {"access": "public", "title": "Public Document"})
store.put(("docs",), "doc2", {"access": "private", "title": "Private Document"})
store.put(("docs",), "doc3", {"access": "secret", "title": "Secret Document"})
# Test 1: Basic SQL injection attempt with single quote
malicious_value = "public' OR '1'='1"
results = store.search(("docs",), filter={"access": malicious_value})
# Should return 0 results because the malicious value is escaped and won't match anything
assert len(results) == 0, "SQL injection via string value should be blocked"
# Test 2: SQL injection with comment
malicious_value = "public'; --"
results = store.search(("docs",), filter={"access": malicious_value})
assert len(results) == 0, "SQL comment injection should be blocked"
# Test 3: UNION injection attempt
malicious_value = "public' UNION SELECT * FROM store --"
results = store.search(("docs",), filter={"access": malicious_value})
assert len(results) == 0, "UNION injection should be blocked"
# Test 4: Parameterized queries handle strings with null bytes and SQL injection attempts safely
malicious_value = "public\x00' OR '1'='1"
results = store.search(("docs",), filter={"access": malicious_value})
assert len(results) == 0, (
"Parameterized queries treat injection attempts as literal strings"
)
# Test 5: Multiple single quotes
malicious_value = "''''"
results = store.search(("docs",), filter={"access": malicious_value})
assert len(results) == 0, "Multiple quotes should be handled safely"
# Test 6: Legitimate value with single quote should work
store.put(("docs",), "doc4", {"title": "O'Brien's Document", "access": "public"})
results = store.search(("docs",), filter={"title": "O'Brien's Document"})
assert len(results) == 1, "Legitimate single quotes should work"
assert results[0].value["title"] == "O'Brien's Document"
# Test 7: Unicode characters with injection attempt
malicious_value = "public' OR 'א'='א"
results = store.search(("docs",), filter={"access": malicious_value})
assert len(results) == 0, "Unicode-based injection should be blocked"
def test_numeric_filter_safety(store: SqliteStore) -> None:
"""Test that numeric filter values are handled safely."""
# Setup: Create documents with numeric fields
store.put(("items",), "item1", {"price": 10, "quantity": 5})
store.put(("items",), "item2", {"price": 20, "quantity": 3})
store.put(("items",), "item3", {"price": 30, "quantity": 1})
# Test 1: Normal numeric comparison
results = store.search(("items",), filter={"price": {"$gt": 15}})
assert len(results) == 2
assert all(r.value["price"] > 15 for r in results)
# Test 2: Special float values (infinity)
results = store.search(("items",), filter={"price": {"$lt": float("inf")}})
assert len(results) == 3, "All finite values should be less than infinity"
# Test 3: Special float values (negative infinity)
results = store.search(("items",), filter={"price": {"$gt": float("-inf")}})
assert len(results) == 3, (
"All finite values should be greater than negative infinity"
)
# Test 4: NaN handling - NaN comparisons should not cause errors
try:
results = store.search(("items",), filter={"price": {"$eq": float("nan")}})
# NaN never equals anything, including itself, so should return 0 results
assert len(results) == 0
except Exception as e:
pytest.fail(f"NaN handling should not raise exception: {e}")
# Test 5: Very large numbers
results = store.search(("items",), filter={"price": {"$lt": 10**100}})
assert len(results) == 3, "Very large numbers should be handled safely"
# Test 6: Negative numbers
store.put(("items",), "item4", {"price": -10, "quantity": 0})
results = store.search(("items",), filter={"price": {"$lt": 0}})
assert len(results) == 1
assert results[0].key == "item4"
def test_boolean_filter_safety(store: SqliteStore) -> None:
"""Test that boolean filter values are handled safely."""
store.put(("flags",), "flag1", {"active": True, "name": "Feature A"})
store.put(("flags",), "flag2", {"active": False, "name": "Feature B"})
store.put(("flags",), "flag3", {"active": True, "name": "Feature C"})
# Test boolean filters
results = store.search(("flags",), filter={"active": True})
assert len(results) == 2
assert all(r.value["active"] is True for r in results)
results = store.search(("flags",), filter={"active": False})
assert len(results) == 1
assert results[0].value["active"] is False
@pytest.mark.parametrize("distance_type", VECTOR_TYPES)
def test_non_ascii(
fake_embeddings: CharacterEmbeddings,
+51 -51
View File
@@ -246,7 +246,7 @@ wheels = [
[[package]]
name = "langgraph-checkpoint"
version = "3.0.0"
version = "3.0.1"
source = { editable = "../checkpoint" }
dependencies = [
{ name = "langchain-core" },
@@ -256,7 +256,7 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "langchain-core", specifier = ">=0.2.38" },
{ name = "ormsgpack", specifier = ">=1.10.0" },
{ name = "ormsgpack", specifier = ">=1.12.0" },
]
[package.metadata.requires-dev]
@@ -511,57 +511,57 @@ wheels = [
[[package]]
name = "ormsgpack"
version = "1.11.0"
version = "1.12.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/65/f8/224c342c0e03e131aaa1a1f19aa2244e167001783a433f4eed10eedd834b/ormsgpack-1.11.0.tar.gz", hash = "sha256:7c9988e78fedba3292541eb3bb274fa63044ef4da2ddb47259ea70c05dee4206", size = 49357, upload-time = "2025-10-08T17:29:15.621Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6c/67/d5ef41c3b4a94400be801984ef7c7fc9623e1a82b643e74eeec367e7462b/ormsgpack-1.12.0.tar.gz", hash = "sha256:94be818fdbb0285945839b88763b269987787cb2f7ef280cad5d6ec815b7e608", size = 49959, upload-time = "2025-11-04T18:30:10.083Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/3d/6996193cb2babc47fc92456223bef7d141065357ad4204eccf313f47a7b3/ormsgpack-1.11.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:03d4e658dd6e1882a552ce1d13cc7b49157414e7d56a4091fbe7823225b08cba", size = 367965, upload-time = "2025-10-08T17:28:06.736Z" },
{ url = "https://files.pythonhosted.org/packages/35/89/c83b805dd9caebb046f4ceeed3706d0902ed2dbbcf08b8464e89f2c52e05/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bb67eb913c2b703f0ed39607fc56e50724dd41f92ce080a586b4d6149eb3fe4", size = 195209, upload-time = "2025-10-08T17:28:08.395Z" },
{ url = "https://files.pythonhosted.org/packages/3a/17/427d9c4f77b120f0af01d7a71d8144771c9388c2a81f712048320e31353b/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e54175b92411f73a238e5653a998627f6660de3def37d9dd7213e0fd264ca56", size = 205868, upload-time = "2025-10-08T17:28:09.688Z" },
{ url = "https://files.pythonhosted.org/packages/82/32/a9ce218478bdbf3fee954159900e24b314ab3064f7b6a217ccb1e3464324/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca2b197f4556e1823d1319869d4c5dc278be335286d2308b0ed88b59a5afcc25", size = 207391, upload-time = "2025-10-08T17:28:11.031Z" },
{ url = "https://files.pythonhosted.org/packages/7a/d3/4413fe7454711596fdf08adabdfa686580e4656702015108e4975f00a022/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc62388262f58c792fe1e450e1d9dbcc174ed2fb0b43db1675dd7c5ff2319d6a", size = 377078, upload-time = "2025-10-08T17:28:12.39Z" },
{ url = "https://files.pythonhosted.org/packages/f0/ad/13fae555a45e35ca1ca929a27c9ee0a3ecada931b9d44454658c543f9b9c/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c48bc10af74adfbc9113f3fb160dc07c61ad9239ef264c17e449eba3de343dc2", size = 470776, upload-time = "2025-10-08T17:28:13.484Z" },
{ url = "https://files.pythonhosted.org/packages/36/60/51178b093ffc4e2ef3381013a67223e7d56224434fba80047249f4a84b26/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a608d3a1d4fa4acdc5082168a54513cff91f47764cef435e81a483452f5f7647", size = 380862, upload-time = "2025-10-08T17:28:14.747Z" },
{ url = "https://files.pythonhosted.org/packages/a6/e3/1cb6c161335e2ae7d711ecfb007a31a3936603626e347c13e5e53b7c7cf8/ormsgpack-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:97217b4f7f599ba45916b9c4c4b1d5656e8e2a4d91e2e191d72a7569d3c30923", size = 112058, upload-time = "2025-10-08T17:28:15.777Z" },
{ url = "https://files.pythonhosted.org/packages/a4/7c/90164d00e8e94b48eff8a17bc2f4be6b71ae356a00904bc69d5e8afe80fb/ormsgpack-1.11.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c7be823f47d8e36648d4bc90634b93f02b7d7cc7480081195f34767e86f181fb", size = 367964, upload-time = "2025-10-08T17:28:16.778Z" },
{ url = "https://files.pythonhosted.org/packages/7b/c2/fb6331e880a3446c1341e72c77bd5a46da3e92a8e2edf7ea84a4c6c14fff/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68accf15d1b013812755c0eb7a30e1fc2f81eb603a1a143bf0cda1b301cfa797", size = 195209, upload-time = "2025-10-08T17:28:17.796Z" },
{ url = "https://files.pythonhosted.org/packages/18/50/4943fb5df8cc02da6b7b1ee2c2a7fb13aebc9f963d69280b1bb02b1fb178/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:805d06fb277d9a4e503c0c707545b49cde66cbb2f84e5cf7c58d81dfc20d8658", size = 205869, upload-time = "2025-10-08T17:28:19.01Z" },
{ url = "https://files.pythonhosted.org/packages/1c/fa/e7e06835bfea9adeef43915143ce818098aecab0cbd3df584815adf3e399/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1e57cdf003e77acc43643bda151dc01f97147a64b11cdee1380bb9698a7601c", size = 207391, upload-time = "2025-10-08T17:28:20.352Z" },
{ url = "https://files.pythonhosted.org/packages/33/f0/f28a19e938a14ec223396e94f4782fbcc023f8c91f2ab6881839d3550f32/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37fc05bdaabd994097c62e2f3e08f66b03f856a640ede6dc5ea340bd15b77f4d", size = 377081, upload-time = "2025-10-08T17:28:21.926Z" },
{ url = "https://files.pythonhosted.org/packages/4f/e3/73d1d7287637401b0b6637e30ba9121e1aa1d9f5ea185ed9834ca15d512c/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a6e9db6c73eb46b2e4d97bdffd1368a66f54e6806b563a997b19c004ef165e1d", size = 470779, upload-time = "2025-10-08T17:28:22.993Z" },
{ url = "https://files.pythonhosted.org/packages/9c/46/7ba7f9721e766dd0dfe4cedf444439447212abffe2d2f4538edeeec8ccbd/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e9c44eae5ac0196ffc8b5ed497c75511056508f2303fa4d36b208eb820cf209e", size = 380865, upload-time = "2025-10-08T17:28:24.012Z" },
{ url = "https://files.pythonhosted.org/packages/a7/7d/bb92a0782bbe0626c072c0320001410cf3f6743ede7dc18f034b1a18edef/ormsgpack-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:11d0dfaf40ae7c6de4f7dbd1e4892e2e6a55d911ab1774357c481158d17371e4", size = 112058, upload-time = "2025-10-08T17:28:25.015Z" },
{ url = "https://files.pythonhosted.org/packages/28/1a/f07c6f74142815d67e1d9d98c5b2960007100408ade8242edac96d5d1c73/ormsgpack-1.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:0c63a3f7199a3099c90398a1bdf0cb577b06651a442dc5efe67f2882665e5b02", size = 105894, upload-time = "2025-10-08T17:28:25.93Z" },
{ url = "https://files.pythonhosted.org/packages/1e/16/2805ebfb3d2cbb6c661b5fae053960fc90a2611d0d93e2207e753e836117/ormsgpack-1.11.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3434d0c8d67de27d9010222de07fb6810fb9af3bb7372354ffa19257ac0eb83b", size = 368474, upload-time = "2025-10-08T17:28:27.532Z" },
{ url = "https://files.pythonhosted.org/packages/6f/39/6afae47822dca0ce4465d894c0bbb860a850ce29c157882dbdf77a5dd26e/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2da5bd097e8dbfa4eb0d4ccfe79acd6f538dee4493579e2debfe4fc8f4ca89b", size = 195321, upload-time = "2025-10-08T17:28:28.573Z" },
{ url = "https://files.pythonhosted.org/packages/f6/54/11eda6b59f696d2f16de469bfbe539c9f469c4b9eef5a513996b5879c6e9/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fdbaa0a5a8606a486960b60c24f2d5235d30ac7a8b98eeaea9854bffef14dc3d", size = 206036, upload-time = "2025-10-08T17:28:29.785Z" },
{ url = "https://files.pythonhosted.org/packages/1e/86/890430f704f84c4699ddad61c595d171ea2fd77a51fbc106f83981e83939/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3682f24f800c1837017ee90ce321086b2cbaef88db7d4cdbbda1582aa6508159", size = 207615, upload-time = "2025-10-08T17:28:31.076Z" },
{ url = "https://files.pythonhosted.org/packages/b6/b9/77383e16c991c0ecb772205b966fc68d9c519e0b5f9c3913283cbed30ffe/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fcca21202bb05ccbf3e0e92f560ee59b9331182e4c09c965a28155efbb134993", size = 377195, upload-time = "2025-10-08T17:28:32.436Z" },
{ url = "https://files.pythonhosted.org/packages/20/e2/15f9f045d4947f3c8a5e0535259fddf027b17b1215367488b3565c573b9d/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c30e5c4655ba46152d722ec7468e8302195e6db362ec1ae2c206bc64f6030e43", size = 470960, upload-time = "2025-10-08T17:28:33.556Z" },
{ url = "https://files.pythonhosted.org/packages/b8/61/403ce188c4c495bc99dff921a0ad3d9d352dd6d3c4b629f3638b7f0cf79b/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7138a341f9e2c08c59368f03d3be25e8b87b3baaf10d30fb1f6f6b52f3d47944", size = 381174, upload-time = "2025-10-08T17:28:34.781Z" },
{ url = "https://files.pythonhosted.org/packages/14/a8/94c94bc48c68da4374870a851eea03fc5a45eb041182ad4c5ed9acfc05a4/ormsgpack-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d4bd8589b78a11026d47f4edf13c1ceab9088bb12451f34396afe6497db28a27", size = 112314, upload-time = "2025-10-08T17:28:36.259Z" },
{ url = "https://files.pythonhosted.org/packages/19/d0/aa4cf04f04e4cc180ce7a8d8ddb5a7f3af883329cbc59645d94d3ba157a5/ormsgpack-1.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:e5e746a1223e70f111d4001dab9585ac8639eee8979ca0c8db37f646bf2961da", size = 106072, upload-time = "2025-10-08T17:28:37.518Z" },
{ url = "https://files.pythonhosted.org/packages/8b/35/e34722edb701d053cf2240f55974f17b7dbfd11fdef72bd2f1835bcebf26/ormsgpack-1.11.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e7b36ab7b45cb95217ae1f05f1318b14a3e5ef73cb00804c0f06233f81a14e8", size = 368502, upload-time = "2025-10-08T17:28:38.547Z" },
{ url = "https://files.pythonhosted.org/packages/2f/6a/c2fc369a79d6aba2aa28c8763856c95337ac7fcc0b2742185cd19397212a/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43402d67e03a9a35cc147c8c03f0c377cad016624479e1ee5b879b8425551484", size = 195344, upload-time = "2025-10-08T17:28:39.554Z" },
{ url = "https://files.pythonhosted.org/packages/8b/6a/0f8e24b7489885534c1a93bdba7c7c434b9b8638713a68098867db9f254c/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:64fd992f932764d6306b70ddc755c1bc3405c4c6a69f77a36acf7af1c8f5ada4", size = 206045, upload-time = "2025-10-08T17:28:40.561Z" },
{ url = "https://files.pythonhosted.org/packages/99/71/8b460ba264f3c6f82ef5b1920335720094e2bd943057964ce5287d6df83a/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0362fb7fe4a29c046c8ea799303079a09372653a1ce5a5a588f3bbb8088368d0", size = 207641, upload-time = "2025-10-08T17:28:41.736Z" },
{ url = "https://files.pythonhosted.org/packages/50/cf/f369446abaf65972424ed2651f2df2b7b5c3b735c93fc7fa6cfb81e34419/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:de2f7a65a9d178ed57be49eba3d0fc9b833c32beaa19dbd4ba56014d3c20b152", size = 377211, upload-time = "2025-10-08T17:28:43.12Z" },
{ url = "https://files.pythonhosted.org/packages/2f/3f/948bb0047ce0f37c2efc3b9bb2bcfdccc61c63e0b9ce8088d4903ba39dcf/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f38cfae95461466055af966fc922d06db4e1654966385cda2828653096db34da", size = 470973, upload-time = "2025-10-08T17:28:44.465Z" },
{ url = "https://files.pythonhosted.org/packages/31/a4/92a8114d1d017c14aaa403445060f345df9130ca532d538094f38e535988/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c88396189d238f183cea7831b07a305ab5c90d6d29b53288ae11200bd956357b", size = 381161, upload-time = "2025-10-08T17:28:46.063Z" },
{ url = "https://files.pythonhosted.org/packages/d0/64/5b76447da654798bfcfdfd64ea29447ff2b7f33fe19d0e911a83ad5107fc/ormsgpack-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:5403d1a945dd7c81044cebeca3f00a28a0f4248b33242a5d2d82111628043725", size = 112321, upload-time = "2025-10-08T17:28:47.393Z" },
{ url = "https://files.pythonhosted.org/packages/46/5e/89900d06db9ab81e7ec1fd56a07c62dfbdcda398c435718f4252e1dc52a0/ormsgpack-1.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c57357b8d43b49722b876edf317bdad9e6d52071b523fdd7394c30cd1c67d5a0", size = 106084, upload-time = "2025-10-08T17:28:48.305Z" },
{ url = "https://files.pythonhosted.org/packages/4c/0b/c659e8657085c8c13f6a0224789f422620cef506e26573b5434defe68483/ormsgpack-1.11.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d390907d90fd0c908211592c485054d7a80990697ef4dff4e436ac18e1aab98a", size = 368497, upload-time = "2025-10-08T17:28:49.297Z" },
{ url = "https://files.pythonhosted.org/packages/1b/0e/451e5848c7ed56bd287e8a2b5cb5926e54466f60936e05aec6cb299f9143/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6153c2e92e789509098e04c9aa116b16673bd88ec78fbe0031deeb34ab642d10", size = 195385, upload-time = "2025-10-08T17:28:50.314Z" },
{ url = "https://files.pythonhosted.org/packages/4c/28/90f78cbbe494959f2439c2ec571f08cd3464c05a6a380b0d621c622122a9/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2b2c2a065a94d742212b2018e1fecd8f8d72f3c50b53a97d1f407418093446d", size = 206114, upload-time = "2025-10-08T17:28:51.336Z" },
{ url = "https://files.pythonhosted.org/packages/fb/db/34163f4c0923bea32dafe42cd878dcc66795a3e85669bc4b01c1e2b92a7b/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:110e65b5340f3d7ef8b0009deae3c6b169437e6b43ad5a57fd1748085d29d2ac", size = 207679, upload-time = "2025-10-08T17:28:53.627Z" },
{ url = "https://files.pythonhosted.org/packages/b6/14/04ee741249b16f380a9b4a0cc19d4134d0b7c74bab27a2117da09e525eb9/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c27e186fca96ab34662723e65b420919910acbbc50fc8e1a44e08f26268cb0e0", size = 377237, upload-time = "2025-10-08T17:28:56.12Z" },
{ url = "https://files.pythonhosted.org/packages/89/ff/53e588a6aaa833237471caec679582c2950f0e7e1a8ba28c1511b465c1f4/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d56b1f877c13d499052d37a3db2378a97d5e1588d264f5040b3412aee23d742c", size = 471021, upload-time = "2025-10-08T17:28:57.299Z" },
{ url = "https://files.pythonhosted.org/packages/a6/f9/f20a6d9ef2be04da3aad05e8f5699957e9a30c6d5c043a10a296afa7e890/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c88e28cd567c0a3269f624b4ade28142d5e502c8e826115093c572007af5be0a", size = 381205, upload-time = "2025-10-08T17:28:58.872Z" },
{ url = "https://files.pythonhosted.org/packages/f8/64/96c07d084b479ac8b7821a77ffc8d3f29d8b5c95ebfdf8db1c03dff02762/ormsgpack-1.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:8811160573dc0a65f62f7e0792c4ca6b7108dfa50771edb93f9b84e2d45a08ae", size = 112374, upload-time = "2025-10-08T17:29:00Z" },
{ url = "https://files.pythonhosted.org/packages/88/a5/5dcc18b818d50213a3cadfe336bb6163a102677d9ce87f3d2f1a1bee0f8c/ormsgpack-1.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:23e30a8d3c17484cf74e75e6134322255bd08bc2b5b295cc9c442f4bae5f3c2d", size = 106056, upload-time = "2025-10-08T17:29:01.29Z" },
{ url = "https://files.pythonhosted.org/packages/19/2b/776d1b411d2be50f77a6e6e94a25825cca55dcacfe7415fd691a144db71b/ormsgpack-1.11.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2905816502adfaf8386a01dd85f936cd378d243f4f5ee2ff46f67f6298dc90d5", size = 368661, upload-time = "2025-10-08T17:29:02.382Z" },
{ url = "https://files.pythonhosted.org/packages/a9/0c/81a19e6115b15764db3d241788f9fac093122878aaabf872cc545b0c4650/ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c04402fb9a0a9b9f18fbafd6d5f8398ee99b3ec619fb63952d3a954bc9d47daa", size = 195539, upload-time = "2025-10-08T17:29:03.472Z" },
{ url = "https://files.pythonhosted.org/packages/97/86/e5b50247a61caec5718122feb2719ea9d451d30ac0516c288c1dbc6408e8/ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a025ec07ac52056ecfd9e57b5cbc6fff163f62cb9805012b56cda599157f8ef2", size = 207718, upload-time = "2025-10-08T17:29:04.545Z" },
{ url = "https://files.pythonhosted.org/packages/4d/0c/8f45fcd22c95190b05d4fba71375d8f783a9e3b0b6aaf476812b693e3868/ormsgpack-1.12.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e08904c232358b94a682ccfbb680bc47d3fd5c424bb7dccb65974dd20c95e8e1", size = 369156, upload-time = "2025-11-04T18:29:17.629Z" },
{ url = "https://files.pythonhosted.org/packages/d2/f8/c7adc093d4ceb05e38786906815f868210f808326c27f8124b4d9466a26b/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9ed7a4b0037d69c8ba7e670e03ee65ae8d5c5114a409e73c5770d7fb5e4b895", size = 195743, upload-time = "2025-11-04T18:29:18.964Z" },
{ url = "https://files.pythonhosted.org/packages/fe/b8/bf002648fa6c150ed6157837b00303edafb35f65c352429463f83214de18/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db2928525b684f3f2af0367aef7ae8d20cde37fc5349c700017129d493a755aa", size = 206472, upload-time = "2025-11-04T18:29:19.951Z" },
{ url = "https://files.pythonhosted.org/packages/23/d6/1f445947c95a931bb189b7864a3f9dcbeebe7dcbc1b3c7387427b3779228/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45f911d9c5b23d11e49ff03fc8f9566745a2b1a7d9033733a1c0a2fa9301cd60", size = 207959, upload-time = "2025-11-04T18:29:21.282Z" },
{ url = "https://files.pythonhosted.org/packages/5c/9c/dd8ccd7553a5c1d0b4b69a0541611983a2f9bc2e8c5708a4bfffc0100468/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:98c54ae6fd682b2aceb264505af9b2255f3df9d84e6e4369bc44d2110f1f311d", size = 377659, upload-time = "2025-11-04T18:29:22.561Z" },
{ url = "https://files.pythonhosted.org/packages/3d/08/3282d8f6330e742d4cdbcfbe2c100b403ae5526ae82a1c1b6a11b7967e37/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:857ab987c3502de08258cc4baf0e87267cb2c80931601084e13df3c355b1ab9d", size = 471391, upload-time = "2025-11-04T18:29:23.663Z" },
{ url = "https://files.pythonhosted.org/packages/18/d4/94a2fbfd4837754bda7a099b6a23b9d40aba9e76e1af8b8fb8133612eb54/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27579d45dc502ee736238e1024559cb0a01aa72a3b68827448b8edf6a2dcdc9c", size = 381501, upload-time = "2025-11-04T18:29:24.771Z" },
{ url = "https://files.pythonhosted.org/packages/d8/c6/1a9fa122cb5deb10b067bbaa43165b12291a914cc0ce364988ff17bbf405/ormsgpack-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c78379d054760875540cf2e81f28da1bb78d09fda3eabdbeb6c53b3e297158cb", size = 112715, upload-time = "2025-11-04T18:29:26.016Z" },
{ url = "https://files.pythonhosted.org/packages/1a/ba/3cae83cf36420c1c8dd294f16c852c03313aafe2439a165c4c6ac611b1d0/ormsgpack-1.12.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c40d86d77391b18dd34de5295e3de2b8ad818bcab9c9def4121c8ec5c9714ae4", size = 369159, upload-time = "2025-11-04T18:29:27.057Z" },
{ url = "https://files.pythonhosted.org/packages/97/d4/5e176309e01a8b9098d80201aac1eb7db9336c3b5b4fa6254a2bbb0d0fa0/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:777b7fab364dc0f200bb382a98a385c8222ffa6a2333d627d763797326202c86", size = 195744, upload-time = "2025-11-04T18:29:28.069Z" },
{ url = "https://files.pythonhosted.org/packages/4f/83/6d80c8c5571639c000a39f38f77752dfaf9d9e552d775331e8d280f66a4e/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b5089ad9dd5b3d3013b245a55e4abaea2f8ad70f4a78e1b002127b02340004", size = 206474, upload-time = "2025-11-04T18:29:29.034Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e6/940311e48dc0cfc3e212bd7007a21ed0825158638057687d804f2c5c2cca/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaf0c87cace7bc08fbf68c5cc66605b593df6427e9f4de235b2da358787e008", size = 207959, upload-time = "2025-11-04T18:29:30.315Z" },
{ url = "https://files.pythonhosted.org/packages/1a/e3/fbe94b0a311815343b86a95a0627e4901b11ff6fd522679ca29a2a88c99b/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f62d476fe28bc5675d9aff30341bfa9f41d7de332c5b63fbbe9aaf6bb7ec74d4", size = 377666, upload-time = "2025-11-04T18:29:31.38Z" },
{ url = "https://files.pythonhosted.org/packages/a3/3b/229cfa28076798ffb619aaa854b842de3f2ed5ea4e6509bf34d14c038c4d/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ded7810095b887e28434f32f5a345d354e88cf851bab3c5435aeb86a718618d2", size = 471394, upload-time = "2025-11-04T18:29:32.521Z" },
{ url = "https://files.pythonhosted.org/packages/6b/bd/4eae4ab35586e4175c07acb5f98aec83aa9d8987f71ea0443aa900191bdf/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f72a1dea0c4ae7c4101dcfbe8133f274a9d769d0b87fe5188db4fab07ffabaee", size = 381506, upload-time = "2025-11-04T18:29:33.533Z" },
{ url = "https://files.pythonhosted.org/packages/dd/51/f9d56d6d015cbfa1ce9a4358ca30a41744644f0cf606e060d7203efe5af8/ormsgpack-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f479bfef847255d7d0b12c7a198f6a21490155da2da3062e082ba370893d4a1", size = 112707, upload-time = "2025-11-04T18:29:34.898Z" },
{ url = "https://files.pythonhosted.org/packages/f4/07/bb189ef7072979f2f96e8716e952172efdce9c54930aa0814bec73aee19b/ormsgpack-1.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:3583ca410e4502144b2594170542e4bbef7b15643fd1208703ae820f11029036", size = 106533, upload-time = "2025-11-04T18:29:36.112Z" },
{ url = "https://files.pythonhosted.org/packages/a2/f2/c1036b2775fcc0cfa5fd618c53bcd3b862ee07298fb627f03af4c7982f84/ormsgpack-1.12.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e0c1e08b64d99076fee155276097489b82cc56e8d5951c03c721a65a32f44494", size = 369538, upload-time = "2025-11-04T18:29:37.125Z" },
{ url = "https://files.pythonhosted.org/packages/d9/ca/526c4ae02f3cb34621af91bf8282a10d666757c2e0c6ff391ff5d403d607/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd43bcb299131690b8e0677af172020b2ada8e625169034b42ac0c13adf84aa", size = 195872, upload-time = "2025-11-04T18:29:38.34Z" },
{ url = "https://files.pythonhosted.org/packages/7f/0f/83bb7968e9715f6a85be53d041b1e6324a05428f56b8b980dac866886871/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0149d595341e22ead340bf281b2995c4cc7dc8d522a6b5f575fe17aa407604", size = 206469, upload-time = "2025-11-04T18:29:39.749Z" },
{ url = "https://files.pythonhosted.org/packages/02/e3/9e93ca1065f2d4af035804a842b1ff3025bab580c7918239bb225cd1fee2/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19a1b27d169deb553c80fd10b589fc2be1fc14cee779fae79fcaf40db04de2b", size = 208273, upload-time = "2025-11-04T18:29:40.769Z" },
{ url = "https://files.pythonhosted.org/packages/b3/d8/6d6ef901b3a8b8f3ab8836b135a56eb7f66c559003e251d9530bedb12627/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f28896942d655064940dfe06118b7ce1e3468d051483148bf02c99ec157483a", size = 377839, upload-time = "2025-11-04T18:29:42.092Z" },
{ url = "https://files.pythonhosted.org/packages/4c/72/fcb704bfa4c2c3a37b647d597cc45a13cffc9d50baac635a9ad620731d29/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9396efcfa48b4abbc06e44c5dbc3c4574a8381a80cb4cd01eea15d28b38c554e", size = 471446, upload-time = "2025-11-04T18:29:43.133Z" },
{ url = "https://files.pythonhosted.org/packages/84/f8/402e4e3eb997c2ee534c99bec4b5bb359c2a1f9edadf043e254a71e11378/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96586ed537a5fb386a162c4f9f7d8e6f76e07b38a990d50c73f11131e00ff040", size = 381783, upload-time = "2025-11-04T18:29:44.466Z" },
{ url = "https://files.pythonhosted.org/packages/f0/8d/5897b700360bc00911b70ae5ef1134ee7abf5baa81a92a4be005917d3dfd/ormsgpack-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e70387112fb3870e4844de090014212cdcf1342f5022047aecca01ec7de05d7a", size = 112943, upload-time = "2025-11-04T18:29:45.468Z" },
{ url = "https://files.pythonhosted.org/packages/5b/44/1e73649f79bb96d6cf9e5bcbac68b6216d238bba80af351c4c0cbcf7ee15/ormsgpack-1.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:d71290a23de5d4829610c42665d816c661ecad8979883f3f06b2e3ab9639962e", size = 106688, upload-time = "2025-11-04T18:29:46.411Z" },
{ url = "https://files.pythonhosted.org/packages/2e/e8/35f11ce9313111488b26b3035e4cbe55caa27909c0b6c8b5b5cd59f9661e/ormsgpack-1.12.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:766f2f3b512d85cd375b26a8b1329b99843560b50b93d3880718e634ad4a5de5", size = 369574, upload-time = "2025-11-04T18:29:47.431Z" },
{ url = "https://files.pythonhosted.org/packages/61/b0/77461587f412d4e598d3687bafe23455ed0f26269f44be20252eddaa624e/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84b285b1f3f185aad7da45641b873b30acfd13084cf829cf668c4c6480a81583", size = 195893, upload-time = "2025-11-04T18:29:48.735Z" },
{ url = "https://files.pythonhosted.org/packages/c6/67/e197ceb04c3b550589e5407fc9fdae10f4e2e2eba5fdac921a269e02e974/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e23604fc79fe110292cb365f4c8232e64e63a34f470538be320feae3921f271b", size = 206503, upload-time = "2025-11-04T18:29:49.99Z" },
{ url = "https://files.pythonhosted.org/packages/0b/b1/7fa8ba82a25cef678983c7976f85edeef5014f5c26495f338258e6a3cf1c/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc32b156c113a0fae2975051417d8d9a7a5247c34b2d7239410c46b75ce9348a", size = 208257, upload-time = "2025-11-04T18:29:51.007Z" },
{ url = "https://files.pythonhosted.org/packages/ce/b1/759e999390000d2589e6d0797f7265e6ec28378547075d28d3736248ab63/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:94ac500dd10c20fa8b8a23bc55606250bfe711bf9716828d9f3d44dfd1f25668", size = 377852, upload-time = "2025-11-04T18:29:52.103Z" },
{ url = "https://files.pythonhosted.org/packages/51/e7/0af737c94272494d9d84a3c29cc42c973ef7fd2342917020906596db863c/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c5201ff7ec24f721f813a182885a17064cffdbe46b2412685a52e6374a872c8f", size = 471456, upload-time = "2025-11-04T18:29:53.336Z" },
{ url = "https://files.pythonhosted.org/packages/f4/ba/c81f0aa4f19fbf457213395945b672e6fde3ce777e3587456e7f0fca2147/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a9740bb3839c9368aacae1cbcfc474ee6976458f41cc135372b7255d5206c953", size = 381813, upload-time = "2025-11-04T18:29:54.394Z" },
{ url = "https://files.pythonhosted.org/packages/ce/15/429c72d64323503fd42cc4ca8398930ded8aa8b3470df8a86b3bbae7a35c/ormsgpack-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ed37f29772432048b58174e920a1d4c4cde0404a5d448d3d8bbcc95d86a6918", size = 112949, upload-time = "2025-11-04T18:29:55.371Z" },
{ url = "https://files.pythonhosted.org/packages/55/b9/e72c451a40f8c57bfc229e0b8e536ecea7203c8f0a839676df2ffb605c62/ormsgpack-1.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:b03994bbec5d6d42e03d6604e327863f885bde67aa61e06107ce1fa5bdd3e71d", size = 106689, upload-time = "2025-11-04T18:29:56.262Z" },
{ url = "https://files.pythonhosted.org/packages/13/16/13eab1a75da531b359105fdee90dda0b6bd1ca0a09880250cf91d8bdfdea/ormsgpack-1.12.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0f3981ba3cba80656012090337e548e597799e14b41e3d0b595ab5ab05a23d7f", size = 369620, upload-time = "2025-11-04T18:29:57.255Z" },
{ url = "https://files.pythonhosted.org/packages/a0/c1/cbcc38b7af4ce58d8893e56d3595c0c8dcd117093bf048f889cf351bdba0/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:901f6f55184d6776dbd5183cbce14caf05bf7f467eef52faf9b094686980bf71", size = 195925, upload-time = "2025-11-04T18:29:58.34Z" },
{ url = "https://files.pythonhosted.org/packages/5c/59/4fa4dc0681490e12b75333440a1c0fd9741b0ebff272b1db4a29d35c2021/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13b15412571422b711b40f45e3fe6d993ea3314b5e97d1a853fe99226c5effc", size = 206594, upload-time = "2025-11-04T18:29:59.329Z" },
{ url = "https://files.pythonhosted.org/packages/39/67/249770896bc32bb91b22c30256961f935d0915cbcf6e289a7fc961d9b14c/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91fa8a452553a62e5fb3fbab471e7faf7b3bec3c87a2f355ebf3d7aab290fe4f", size = 208307, upload-time = "2025-11-04T18:30:00.377Z" },
{ url = "https://files.pythonhosted.org/packages/07/0a/e041a248cd72f2f4c07e155913e0a3ede4c86cf21a40ae6cd79f135f2847/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74ec101f69624695eec4ce7c953192d97748254abe78fb01b591f06d529e1952", size = 377844, upload-time = "2025-11-04T18:30:01.389Z" },
{ url = "https://files.pythonhosted.org/packages/d8/71/6f7773e4ffda73a358ce4bba69b3e8bee9d40a7a06315e4c1cd7a3ea9d02/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9bbf7896580848326c1f9bd7531f264e561f98db7e08e15aa75963d83832c717", size = 471572, upload-time = "2025-11-04T18:30:02.486Z" },
{ url = "https://files.pythonhosted.org/packages/65/29/af6769a4289c07acc71e7bda1d64fb31800563147d73142686e185e82348/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7567917da613b8f8d591c1674e411fd3404bea41ef2b9a0e0a1e049c0f9406d7", size = 381842, upload-time = "2025-11-04T18:30:03.799Z" },
{ url = "https://files.pythonhosted.org/packages/0b/dd/0a86195ee7a1a96c088aefc8504385e881cf56f4563ed81bafe21cbf1fb0/ormsgpack-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e418256c5d8622b8bc92861936f7c6a0131355e7bcad88a42102ae8227f8a1c", size = 113008, upload-time = "2025-11-04T18:30:04.777Z" },
{ url = "https://files.pythonhosted.org/packages/4c/57/fafc79e32f3087f6f26f509d80b8167516326bfea38d30502627c01617e0/ormsgpack-1.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:433ace29aa02713554f714c62a4e4dcad0c9e32674ba4f66742c91a4c3b1b969", size = 106648, upload-time = "2025-11-04T18:30:05.708Z" },
{ url = "https://files.pythonhosted.org/packages/b3/cf/5d58d9b132128d2fe5d586355dde76af386554abef00d608f66b913bff1f/ormsgpack-1.12.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e57164be4ca34b64e210ec515059193280ac84df4d6f31a6fcbfb2fc8436de55", size = 369803, upload-time = "2025-11-04T18:30:06.728Z" },
{ url = "https://files.pythonhosted.org/packages/67/42/968a2da361eaff2e4cbb17c82c7599787babf16684110ad70409646cc1e4/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:904f96289deaa92fc6440b122edc27c5bdc28234edd63717f6d853d88c823a83", size = 195991, upload-time = "2025-11-04T18:30:07.713Z" },
{ url = "https://files.pythonhosted.org/packages/03/f0/9696c6c6cf8ad35170f0be8d0ef3523cc258083535f6c8071cb8235ebb8b/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b291d086e524a1062d57d1b7b5a8bcaaf29caebf0212fec12fd86240bd33633", size = 208316, upload-time = "2025-11-04T18:30:08.663Z" },
]
[[package]]
+3 -1
View File
@@ -38,8 +38,10 @@ Each checkpointer should conform to `langgraph.checkpoint.base.BaseCheckpointSav
- `.put_writes` - Store intermediate writes linked to a checkpoint (i.e. pending writes).
- `.get_tuple` - Fetch a checkpoint tuple using for a given configuration (`thread_id` and `checkpoint_id`).
- `.list` - List checkpoints that match a given configuration and filter criteria.
- `.delete_thread()` - Delete all checkpoints and writes associated with a thread.
- `.get_next_version()` - Generate the next version ID for a channel.
If the checkpointer will be used with asynchronous graph execution (i.e. executing the graph via `.ainvoke`, `.astream`, `.abatch`), checkpointer must implement asynchronous versions of the above methods (`.aput`, `.aput_writes`, `.aget_tuple`, `.alist`).
If the checkpointer will be used with asynchronous graph execution (i.e. executing the graph via `.ainvoke`, `.astream`, `.abatch`), checkpointer must implement asynchronous versions of the above methods (`.aput`, `.aput_writes`, `.aget_tuple`, `.alist`). Similarly, the checkpointer must implement `.adelete_thread()` if asynchronous thread cleanup is desired. The base class provides a default implementation of `.get_next_version()` that generates an integer sequence starting from 1, but this method should be overridden for custom versioning schemes.
## Usage
@@ -60,23 +60,28 @@ class Checkpoint(TypedDict):
"""State snapshot at a given point in time."""
v: int
"""The version of the checkpoint format. Currently 1."""
"""The version of the checkpoint format. Currently `1`."""
id: str
"""The ID of the checkpoint. This is both unique and monotonically
increasing, so can be used for sorting checkpoints from first to last."""
"""The ID of the checkpoint.
This is both unique and monotonically increasing, so can be used for sorting
checkpoints from first to last."""
ts: str
"""The timestamp of the checkpoint in ISO 8601 format."""
channel_values: dict[str, Any]
"""The values of the channels at the time of the checkpoint.
Mapping from channel name to deserialized channel snapshot value.
"""
channel_versions: ChannelVersions
"""The versions of the channels at the time of the checkpoint.
The keys are channel names and the values are monotonically increasing
version strings for each channel.
"""
versions_seen: dict[str, ChannelVersions]
"""Map from node ID to map from channel name to version seen.
This keeps track of the versions of the channels that each node has seen.
Used to determine which nodes to execute next.
"""
@@ -352,8 +357,9 @@ class BaseCheckpointSaver(Generic[V]):
def get_next_version(self, current: V | None, channel: None) -> V:
"""Generate the next version ID for a channel.
Default is to use integer versions, incrementing by `1`. If you override, you can use `str`/`int`/`float`
versions, as long as they are monotonically increasing.
Default is to use integer versions, incrementing by `1`.
If you override, you can use `str`/`int`/`float` versions, as long as they are monotonically increasing.
Args:
current: The current version identifier (`int`, `float`, or `str`).
@@ -33,7 +33,7 @@ class InMemorySaver(
):
"""An in-memory checkpoint saver.
This checkpoint saver stores checkpoints in memory using a defaultdict.
This checkpoint saver stores checkpoints in memory using a `defaultdict`.
Note:
Only use `InMemorySaver` for debugging or testing purposes.
@@ -44,22 +44,23 @@ class InMemorySaver(
Args:
serde: The serializer to use for serializing and deserializing checkpoints.
Examples:
Example:
```python
import asyncio
import asyncio
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph
builder = StateGraph(int)
builder.add_node("add_one", lambda x: x + 1)
builder.set_entry_point("add_one")
builder.set_finish_point("add_one")
builder = StateGraph(int)
builder.add_node("add_one", lambda x: x + 1)
builder.set_entry_point("add_one")
builder.set_finish_point("add_one")
memory = InMemorySaver()
graph = builder.compile(checkpointer=memory)
coro = graph.ainvoke(1, {"configurable": {"thread_id": "thread-1"}})
asyncio.run(coro) # Output: 2
memory = InMemorySaver()
graph = builder.compile(checkpointer=memory)
coro = graph.ainvoke(1, {"configurable": {"thread_id": "thread-1"}})
asyncio.run(coro) # Output: 2
```
"""
# thread ID -> checkpoint NS -> checkpoint ID -> checkpoint mapping
@@ -96,7 +97,8 @@ class InMemorySaver(
self.stack.enter_context(self.blobs) # type: ignore[arg-type]
def __enter__(self) -> InMemorySaver:
return self.stack.__enter__()
self.stack.__enter__()
return self
def __exit__(
self,
@@ -107,7 +109,8 @@ class InMemorySaver(
return self.stack.__exit__(exc_type, exc_value, traceback)
async def __aenter__(self) -> InMemorySaver:
return self.stack.__enter__()
self.stack.__enter__()
return self
async def __aexit__(
self,
@@ -15,9 +15,7 @@ class UntypedSerializerProtocol(Protocol):
class SerializerProtocol(Protocol):
"""Protocol for serialization and deserialization of objects.
- `dumps`: Serialize an object to bytes.
- `dumps_typed`: Serialize an object to a tuple `(type, bytes)`.
- `loads`: Deserialize an object from bytes.
- `loads_typed`: Deserialize an object from a tuple `(type, bytes)`.
Valid implementations include the `pickle`, `json` and `orjson` modules.
@@ -52,12 +50,13 @@ def maybe_add_typed_methods(
class CipherProtocol(Protocol):
"""Protocol for encryption and decryption of data.
- `encrypt`: Encrypt plaintext.
- `decrypt`: Decrypt ciphertext.
"""
def encrypt(self, plaintext: bytes) -> tuple[str, bytes]:
"""Encrypt plaintext. Returns a tuple (cipher name, ciphertext)."""
"""Encrypt plaintext. Returns a tuple `(cipher name, ciphertext)`."""
...
def decrypt(self, ciphername: str, ciphertext: bytes) -> bytes:
@@ -41,10 +41,12 @@ logger = logging.getLogger(__name__)
class JsonPlusSerializer(SerializerProtocol):
"""Serializer that uses ormsgpack, with optional fallbacks.
Security note: this serializer is intended for use within the BaseCheckpointSaver
class and called within the Pregel loop. It should not be used on untrusted
python objects. If an attacker can write directly to your checkpoint database,
they may be able to trigger code execution when data is deserialized.
!!! warning
Security note: This serializer is intended for use within the `BaseCheckpointSaver`
class and called within the Pregel loop. It should not be used on untrusted
python objects. If an attacker can write directly to your checkpoint database,
they may be able to trigger code execution when data is deserialized.
"""
def __init__(
@@ -640,6 +642,7 @@ _option = (
| ormsgpack.OPT_PASSTHROUGH_DATETIME
| ormsgpack.OPT_PASSTHROUGH_ENUM
| ormsgpack.OPT_PASSTHROUGH_UUID
| ormsgpack.OPT_REPLACE_SURROGATES
)
+6 -3
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-checkpoint"
version = "3.0.0"
version = "3.0.1"
description = "Library with base interfaces for LangGraph checkpoint savers."
authors = []
requires-python = ">=3.10"
@@ -13,11 +13,14 @@ license = "MIT"
license-files = ['LICENSE']
dependencies = [
"langchain-core>=0.2.38",
"ormsgpack>=1.10.0",
"ormsgpack>=1.12.0",
]
[project.urls]
Repository = "https://www.github.com/langchain-ai/langgraph"
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint"
Twitter = "https://x.com/LangChainAI"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
[dependency-groups]
test = [
+12 -12
View File
@@ -149,20 +149,20 @@ def test_serde_jsonplus() -> None:
assert serde.loads_typed(serde.dumps_typed(value)) == value
surrogates = [
"Hello\ud83d\ude00",
"Python\ud83d\udc0d",
"Surrogate\ud834\udd1e",
"Example\ud83c\udf89",
"String\ud83c\udfa7",
"With\ud83c\udf08",
"Surrogates\ud83d\ude0e",
"Embedded\ud83d\udcbb",
"In\ud83c\udf0e",
"The\ud83d\udcd6",
"Text\ud83d\udcac",
"Hello??",
"Python??",
"Surrogate??",
"Example??",
"String??",
"With??",
"Surrogates??",
"Embedded??",
"In??",
"The??",
"Text??",
"收花🙄·到",
]
serde = JsonPlusSerializer(pickle_fallback=True)
serde = JsonPlusSerializer(pickle_fallback=False)
assert serde.loads_typed(serde.dumps_typed(surrogates)) == surrogates
+9 -2
View File
@@ -188,7 +188,14 @@ class TestMemorySaver:
assert len(search_results_4) == 0
def test_memory_saver() -> None:
async def test_memory_saver() -> None:
from langgraph.checkpoint.memory import InMemorySaver
assert isinstance(InMemorySaver(), InMemorySaver)
memory_saver = InMemorySaver()
assert isinstance(memory_saver, InMemorySaver)
async with memory_saver as async_memory_saver:
assert async_memory_saver is memory_saver
with memory_saver as sync_memory_saver:
assert sync_memory_saver is memory_saver
+421 -369
View File
@@ -51,75 +51,100 @@ wheels = [
[[package]]
name = "certifi"
version = "2025.8.3"
version = "2025.10.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" }
sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" },
{ url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.3"
version = "3.4.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" },
{ url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" },
{ url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" },
{ url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" },
{ url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" },
{ url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" },
{ url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" },
{ url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" },
{ url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" },
{ url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825, upload-time = "2025-08-09T07:55:50.305Z" },
{ url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452, upload-time = "2025-08-09T07:55:51.461Z" },
{ url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" },
{ url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" },
{ url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" },
{ url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" },
{ url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" },
{ url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" },
{ url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" },
{ url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" },
{ url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" },
{ url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616, upload-time = "2025-08-09T07:56:05.658Z" },
{ url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108, upload-time = "2025-08-09T07:56:07.176Z" },
{ url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" },
{ url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" },
{ url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" },
{ url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" },
{ url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" },
{ url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" },
{ url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" },
{ url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" },
{ url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" },
{ url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" },
{ url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" },
{ url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" },
{ url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" },
{ url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" },
{ url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" },
{ url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" },
{ url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" },
{ url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" },
{ url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" },
{ url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
{ url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
{ url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
{ url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
{ url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
{ url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
{ url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
{ url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
{ url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
{ url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
{ url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
{ url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
{ url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
{ url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
{ url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
{ url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" },
{ url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" },
{ url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" },
{ url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" },
{ url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" },
{ url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" },
{ url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" },
{ url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" },
{ url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" },
{ url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" },
{ url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" },
{ url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" },
{ url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" },
{ url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" },
{ url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" },
{ url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" },
{ url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
{ url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
{ url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
{ url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
{ url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
{ url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
{ url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
{ url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
{ url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
{ url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
{ url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
{ url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
{ url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
{ url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
{ url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
{ url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
{ url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
{ url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
{ url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
{ url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
{ url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
{ url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
{ url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
{ url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
{ url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
{ url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
{ url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
{ url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
{ url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
{ url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
{ url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
{ url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
]
[[package]]
@@ -204,20 +229,20 @@ wheels = [
[[package]]
name = "idna"
version = "3.10"
version = "3.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
]
[[package]]
name = "iniconfig"
version = "2.1.0"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]
[[package]]
@@ -243,7 +268,7 @@ wheels = [
[[package]]
name = "langchain-core"
version = "0.3.76"
version = "1.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jsonpatch" },
@@ -254,14 +279,14 @@ dependencies = [
{ name = "tenacity" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4f/4d/5e2ea7754ee0a1f524c412801c6ba9ad49318ecb58b0d524903c3d9efe0a/langchain_core-0.3.76.tar.gz", hash = "sha256:71136a122dd1abae2c289c5809d035cf12b5f2bb682d8a4c1078cd94feae7419", size = 573568, upload-time = "2025-09-10T14:49:39.863Z" }
sdist = { url = "https://files.pythonhosted.org/packages/41/15/dfe0c2af463d63296fe18608a06570ce3a4b245253d4f26c301481380f7d/langchain_core-1.0.3.tar.gz", hash = "sha256:10744945d21168fb40d1162a5f1cf69bf0137ff6ad2b12c87c199a5297410887", size = 770278, upload-time = "2025-11-03T14:32:09.712Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/b5/501c0ffcb09c734457ceaa86bc7b1dd37b6a261147bd653add03b838aacb/langchain_core-0.3.76-py3-none-any.whl", hash = "sha256:46e0eb48c7ac532432d51f8ca1ece1804c82afe9ae3dcf027b867edadf82b3ec", size = 447508, upload-time = "2025-09-10T14:49:38.179Z" },
{ url = "https://files.pythonhosted.org/packages/f2/1b/b0a37674bdcbd2931944e12ea742fd167098de5212ee2391e91dce631162/langchain_core-1.0.3-py3-none-any.whl", hash = "sha256:64f1bd45f04b174bbfd54c135a8adc52f4902b347c15a117d6383b412bf558a5", size = 469927, upload-time = "2025-11-03T14:32:08.322Z" },
]
[[package]]
name = "langgraph-checkpoint"
version = "3.0.0"
version = "3.0.1"
source = { editable = "." }
dependencies = [
{ name = "langchain-core" },
@@ -274,7 +299,7 @@ dev = [
{ name = "dataclasses-json" },
{ name = "mypy" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "pandas" },
{ name = "pandas-stubs" },
{ name = "pytest" },
@@ -292,7 +317,7 @@ lint = [
test = [
{ name = "dataclasses-json" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "pandas" },
{ name = "pandas-stubs" },
{ name = "pytest" },
@@ -305,7 +330,7 @@ test = [
[package.metadata]
requires-dist = [
{ name = "langchain-core", specifier = ">=0.2.38" },
{ name = "ormsgpack", specifier = ">=1.10.0" },
{ name = "ormsgpack", specifier = ">=1.12.0" },
]
[package.metadata.requires-dev]
@@ -342,7 +367,7 @@ test = [
[[package]]
name = "langsmith"
version = "0.4.31"
version = "0.4.40"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -353,9 +378,9 @@ dependencies = [
{ name = "requests-toolbelt" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/f5/edbdf89a162ee025348b3b2080fb3b88f4a1040a5a186f32d34aca913994/langsmith-0.4.31.tar.gz", hash = "sha256:5fb3729e22bd9a225391936cb9d1080322e6c375bb776514af06b56d6c46ed3e", size = 959698, upload-time = "2025-09-25T04:18:19.55Z" }
sdist = { url = "https://files.pythonhosted.org/packages/5d/68/988272a0f0ddf1f07c6e34176104f687b2f45a2d59a16371b17ac739d12c/langsmith-0.4.40.tar.gz", hash = "sha256:bd7704bd72c560dbcf5f72f81b3a02ace27ae8f15e0d5e46f9c4568b4908d0c2", size = 950190, upload-time = "2025-11-04T01:21:56.76Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3e/8e/e7a43d907a147e1f87eebdd6737483f9feba52a5d4b20f69d0bd6f2fa22f/langsmith-0.4.31-py3-none-any.whl", hash = "sha256:64f340bdead21defe5f4a6ca330c11073e35444989169f669508edf45a19025f", size = 386347, upload-time = "2025-09-25T04:18:16.69Z" },
{ url = "https://files.pythonhosted.org/packages/b3/8a/de38127895f185f677778930478c6884b087c2cab5744dd7e27c0188fbec/langsmith-0.4.40-py3-none-any.whl", hash = "sha256:1ec1a4981c39d4cee85b623ac86dd79e14b1b236ddc6357aec72a58940ecbfff", size = 399341, upload-time = "2025-11-04T01:21:54.407Z" },
]
[[package]]
@@ -491,219 +516,223 @@ wheels = [
[[package]]
name = "numpy"
version = "2.3.3"
version = "2.3.4"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.12'",
"python_full_version == '3.11.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" }
sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253, upload-time = "2025-09-09T15:56:02.094Z" },
{ url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980, upload-time = "2025-09-09T15:56:05.926Z" },
{ url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709, upload-time = "2025-09-09T15:56:07.95Z" },
{ url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923, upload-time = "2025-09-09T15:56:09.443Z" },
{ url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591, upload-time = "2025-09-09T15:56:11.234Z" },
{ url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714, upload-time = "2025-09-09T15:56:14.637Z" },
{ url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592, upload-time = "2025-09-09T15:56:17.285Z" },
{ url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474, upload-time = "2025-09-09T15:56:20.943Z" },
{ url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57", size = 6599794, upload-time = "2025-09-09T15:56:23.258Z" },
{ url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa", size = 13088104, upload-time = "2025-09-09T15:56:25.476Z" },
{ url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7", size = 10460772, upload-time = "2025-09-09T15:56:27.679Z" },
{ url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" },
{ url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" },
{ url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" },
{ url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" },
{ url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" },
{ url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" },
{ url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" },
{ url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" },
{ url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" },
{ url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" },
{ url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" },
{ url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" },
{ url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" },
{ url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" },
{ url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" },
{ url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" },
{ url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" },
{ url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" },
{ url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" },
{ url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" },
{ url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" },
{ url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" },
{ url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" },
{ url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" },
{ url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" },
{ url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" },
{ url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" },
{ url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" },
{ url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" },
{ url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" },
{ url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" },
{ url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" },
{ url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" },
{ url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" },
{ url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" },
{ url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" },
{ url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" },
{ url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" },
{ url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" },
{ url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" },
{ url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" },
{ url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" },
{ url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" },
{ url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" },
{ url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" },
{ url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" },
{ url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" },
{ url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" },
{ url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" },
{ url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" },
{ url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" },
{ url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" },
{ url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" },
{ url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" },
{ url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" },
{ url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019, upload-time = "2025-09-09T15:58:42.838Z" },
{ url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288, upload-time = "2025-09-09T15:58:45.425Z" },
{ url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425, upload-time = "2025-09-09T15:58:48.6Z" },
{ url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053, upload-time = "2025-09-09T15:58:50.401Z" },
{ url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354, upload-time = "2025-09-09T15:58:52.704Z" },
{ url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413, upload-time = "2025-09-09T15:58:55.027Z" },
{ url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" },
{ url = "https://files.pythonhosted.org/packages/60/e7/0e07379944aa8afb49a556a2b54587b828eb41dc9adc56fb7615b678ca53/numpy-2.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e78aecd2800b32e8347ce49316d3eaf04aed849cd5b38e0af39f829a4e59f5eb", size = 21259519, upload-time = "2025-10-15T16:15:19.012Z" },
{ url = "https://files.pythonhosted.org/packages/d0/cb/5a69293561e8819b09e34ed9e873b9a82b5f2ade23dce4c51dc507f6cfe1/numpy-2.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7fd09cc5d65bda1e79432859c40978010622112e9194e581e3415a3eccc7f43f", size = 14452796, upload-time = "2025-10-15T16:15:23.094Z" },
{ url = "https://files.pythonhosted.org/packages/e4/04/ff11611200acd602a1e5129e36cfd25bf01ad8e5cf927baf2e90236eb02e/numpy-2.3.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1b219560ae2c1de48ead517d085bc2d05b9433f8e49d0955c82e8cd37bd7bf36", size = 5381639, upload-time = "2025-10-15T16:15:25.572Z" },
{ url = "https://files.pythonhosted.org/packages/ea/77/e95c757a6fe7a48d28a009267408e8aa382630cc1ad1db7451b3bc21dbb4/numpy-2.3.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:bafa7d87d4c99752d07815ed7a2c0964f8ab311eb8168f41b910bd01d15b6032", size = 6914296, upload-time = "2025-10-15T16:15:27.079Z" },
{ url = "https://files.pythonhosted.org/packages/a3/d2/137c7b6841c942124eae921279e5c41b1c34bab0e6fc60c7348e69afd165/numpy-2.3.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36dc13af226aeab72b7abad501d370d606326a0029b9f435eacb3b8c94b8a8b7", size = 14591904, upload-time = "2025-10-15T16:15:29.044Z" },
{ url = "https://files.pythonhosted.org/packages/bb/32/67e3b0f07b0aba57a078c4ab777a9e8e6bc62f24fb53a2337f75f9691699/numpy-2.3.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7b2f9a18b5ff9824a6af80de4f37f4ec3c2aab05ef08f51c77a093f5b89adda", size = 16939602, upload-time = "2025-10-15T16:15:31.106Z" },
{ url = "https://files.pythonhosted.org/packages/95/22/9639c30e32c93c4cee3ccdb4b09c2d0fbff4dcd06d36b357da06146530fb/numpy-2.3.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9984bd645a8db6ca15d850ff996856d8762c51a2239225288f08f9050ca240a0", size = 16372661, upload-time = "2025-10-15T16:15:33.546Z" },
{ url = "https://files.pythonhosted.org/packages/12/e9/a685079529be2b0156ae0c11b13d6be647743095bb51d46589e95be88086/numpy-2.3.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:64c5825affc76942973a70acf438a8ab618dbd692b84cd5ec40a0a0509edc09a", size = 18884682, upload-time = "2025-10-15T16:15:36.105Z" },
{ url = "https://files.pythonhosted.org/packages/cf/85/f6f00d019b0cc741e64b4e00ce865a57b6bed945d1bbeb1ccadbc647959b/numpy-2.3.4-cp311-cp311-win32.whl", hash = "sha256:ed759bf7a70342f7817d88376eb7142fab9fef8320d6019ef87fae05a99874e1", size = 6570076, upload-time = "2025-10-15T16:15:38.225Z" },
{ url = "https://files.pythonhosted.org/packages/7d/10/f8850982021cb90e2ec31990291f9e830ce7d94eef432b15066e7cbe0bec/numpy-2.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:faba246fb30ea2a526c2e9645f61612341de1a83fb1e0c5edf4ddda5a9c10996", size = 13089358, upload-time = "2025-10-15T16:15:40.404Z" },
{ url = "https://files.pythonhosted.org/packages/d1/ad/afdd8351385edf0b3445f9e24210a9c3971ef4de8fd85155462fc4321d79/numpy-2.3.4-cp311-cp311-win_arm64.whl", hash = "sha256:4c01835e718bcebe80394fd0ac66c07cbb90147ebbdad3dcecd3f25de2ae7e2c", size = 10462292, upload-time = "2025-10-15T16:15:42.896Z" },
{ url = "https://files.pythonhosted.org/packages/96/7a/02420400b736f84317e759291b8edaeee9dc921f72b045475a9cbdb26b17/numpy-2.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11", size = 20957727, upload-time = "2025-10-15T16:15:44.9Z" },
{ url = "https://files.pythonhosted.org/packages/18/90/a014805d627aa5750f6f0e878172afb6454552da929144b3c07fcae1bb13/numpy-2.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9", size = 14187262, upload-time = "2025-10-15T16:15:47.761Z" },
{ url = "https://files.pythonhosted.org/packages/c7/e4/0a94b09abe89e500dc748e7515f21a13e30c5c3fe3396e6d4ac108c25fca/numpy-2.3.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667", size = 5115992, upload-time = "2025-10-15T16:15:50.144Z" },
{ url = "https://files.pythonhosted.org/packages/88/dd/db77c75b055c6157cbd4f9c92c4458daef0dd9cbe6d8d2fe7f803cb64c37/numpy-2.3.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef", size = 6648672, upload-time = "2025-10-15T16:15:52.442Z" },
{ url = "https://files.pythonhosted.org/packages/e1/e6/e31b0d713719610e406c0ea3ae0d90760465b086da8783e2fd835ad59027/numpy-2.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e", size = 14284156, upload-time = "2025-10-15T16:15:54.351Z" },
{ url = "https://files.pythonhosted.org/packages/f9/58/30a85127bfee6f108282107caf8e06a1f0cc997cb6b52cdee699276fcce4/numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a", size = 16641271, upload-time = "2025-10-15T16:15:56.67Z" },
{ url = "https://files.pythonhosted.org/packages/06/f2/2e06a0f2adf23e3ae29283ad96959267938d0efd20a2e25353b70065bfec/numpy-2.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16", size = 16059531, upload-time = "2025-10-15T16:15:59.412Z" },
{ url = "https://files.pythonhosted.org/packages/b0/e7/b106253c7c0d5dc352b9c8fab91afd76a93950998167fa3e5afe4ef3a18f/numpy-2.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786", size = 18578983, upload-time = "2025-10-15T16:16:01.804Z" },
{ url = "https://files.pythonhosted.org/packages/73/e3/04ecc41e71462276ee867ccbef26a4448638eadecf1bc56772c9ed6d0255/numpy-2.3.4-cp312-cp312-win32.whl", hash = "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc", size = 6291380, upload-time = "2025-10-15T16:16:03.938Z" },
{ url = "https://files.pythonhosted.org/packages/3d/a8/566578b10d8d0e9955b1b6cd5db4e9d4592dd0026a941ff7994cedda030a/numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32", size = 12787999, upload-time = "2025-10-15T16:16:05.801Z" },
{ url = "https://files.pythonhosted.org/packages/58/22/9c903a957d0a8071b607f5b1bff0761d6e608b9a965945411f867d515db1/numpy-2.3.4-cp312-cp312-win_arm64.whl", hash = "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db", size = 10197412, upload-time = "2025-10-15T16:16:07.854Z" },
{ url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload-time = "2025-10-15T16:16:10.304Z" },
{ url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload-time = "2025-10-15T16:16:12.595Z" },
{ url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload-time = "2025-10-15T16:16:14.877Z" },
{ url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload-time = "2025-10-15T16:16:16.805Z" },
{ url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload-time = "2025-10-15T16:16:18.764Z" },
{ url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload-time = "2025-10-15T16:16:21.072Z" },
{ url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload-time = "2025-10-15T16:16:23.369Z" },
{ url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload-time = "2025-10-15T16:16:27.496Z" },
{ url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload-time = "2025-10-15T16:16:29.811Z" },
{ url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload-time = "2025-10-15T16:16:31.589Z" },
{ url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload-time = "2025-10-15T16:16:33.902Z" },
{ url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload-time = "2025-10-15T16:16:36.101Z" },
{ url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload-time = "2025-10-15T16:16:39.124Z" },
{ url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload-time = "2025-10-15T16:16:41.168Z" },
{ url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload-time = "2025-10-15T16:16:43.777Z" },
{ url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload-time = "2025-10-15T16:16:46.081Z" },
{ url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload-time = "2025-10-15T16:16:48.455Z" },
{ url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload-time = "2025-10-15T16:16:51.114Z" },
{ url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload-time = "2025-10-15T16:16:53.429Z" },
{ url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload-time = "2025-10-15T16:16:55.992Z" },
{ url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload-time = "2025-10-15T16:16:57.943Z" },
{ url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload-time = "2025-10-15T16:17:00.048Z" },
{ url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload-time = "2025-10-15T16:17:02.509Z" },
{ url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload-time = "2025-10-15T16:17:04.873Z" },
{ url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload-time = "2025-10-15T16:17:07.499Z" },
{ url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload-time = "2025-10-15T16:17:09.774Z" },
{ url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload-time = "2025-10-15T16:17:11.937Z" },
{ url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload-time = "2025-10-15T16:17:14.391Z" },
{ url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload-time = "2025-10-15T16:17:17.058Z" },
{ url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload-time = "2025-10-15T16:17:19.379Z" },
{ url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload-time = "2025-10-15T16:17:22.886Z" },
{ url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload-time = "2025-10-15T16:17:24.783Z" },
{ url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload-time = "2025-10-15T16:17:26.935Z" },
{ url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload-time = "2025-10-15T16:17:29.638Z" },
{ url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload-time = "2025-10-15T16:17:32.384Z" },
{ url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload-time = "2025-10-15T16:17:34.515Z" },
{ url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload-time = "2025-10-15T16:17:36.128Z" },
{ url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload-time = "2025-10-15T16:17:38.884Z" },
{ url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload-time = "2025-10-15T16:17:41.564Z" },
{ url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload-time = "2025-10-15T16:17:43.901Z" },
{ url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload-time = "2025-10-15T16:17:46.247Z" },
{ url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload-time = "2025-10-15T16:17:48.872Z" },
{ url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload-time = "2025-10-15T16:17:50.938Z" },
{ url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload-time = "2025-10-15T16:17:53.48Z" },
{ url = "https://files.pythonhosted.org/packages/b1/b6/64898f51a86ec88ca1257a59c1d7fd077b60082a119affefcdf1dd0df8ca/numpy-2.3.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6e274603039f924c0fe5cb73438fa9246699c78a6df1bd3decef9ae592ae1c05", size = 21131552, upload-time = "2025-10-15T16:17:55.845Z" },
{ url = "https://files.pythonhosted.org/packages/ce/4c/f135dc6ebe2b6a3c77f4e4838fa63d350f85c99462012306ada1bd4bc460/numpy-2.3.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d149aee5c72176d9ddbc6803aef9c0f6d2ceeea7626574fc68518da5476fa346", size = 14377796, upload-time = "2025-10-15T16:17:58.308Z" },
{ url = "https://files.pythonhosted.org/packages/d0/a4/f33f9c23fcc13dd8412fc8614559b5b797e0aba9d8e01dfa8bae10c84004/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:6d34ed9db9e6395bb6cd33286035f73a59b058169733a9db9f85e650b88df37e", size = 5306904, upload-time = "2025-10-15T16:18:00.596Z" },
{ url = "https://files.pythonhosted.org/packages/28/af/c44097f25f834360f9fb960fa082863e0bad14a42f36527b2a121abdec56/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:fdebe771ca06bb8d6abce84e51dca9f7921fe6ad34a0c914541b063e9a68928b", size = 6819682, upload-time = "2025-10-15T16:18:02.32Z" },
{ url = "https://files.pythonhosted.org/packages/c5/8c/cd283b54c3c2b77e188f63e23039844f56b23bba1712318288c13fe86baf/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e92defe6c08211eb77902253b14fe5b480ebc5112bc741fd5e9cd0608f847", size = 14422300, upload-time = "2025-10-15T16:18:04.271Z" },
{ url = "https://files.pythonhosted.org/packages/b0/f0/8404db5098d92446b3e3695cf41c6f0ecb703d701cb0b7566ee2177f2eee/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13b9062e4f5c7ee5c7e5be96f29ba71bc5a37fed3d1d77c37390ae00724d296d", size = 16760806, upload-time = "2025-10-15T16:18:06.668Z" },
{ url = "https://files.pythonhosted.org/packages/95/8e/2844c3959ce9a63acc7c8e50881133d86666f0420bcde695e115ced0920f/numpy-2.3.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:81b3a59793523e552c4a96109dde028aa4448ae06ccac5a76ff6532a85558a7f", size = 12973130, upload-time = "2025-10-15T16:18:09.397Z" },
]
[[package]]
name = "orjson"
version = "3.11.3"
version = "3.11.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/be/4d/8df5f83256a809c22c4d6792ce8d43bb503be0fb7a8e4da9025754b09658/orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a", size = 5482394, upload-time = "2025-08-26T17:46:43.171Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c6/fe/ed708782d6709cc60eb4c2d8a361a440661f74134675c72990f2c48c785f/orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d", size = 5945188, upload-time = "2025-10-24T15:50:38.027Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/64/4a3cef001c6cd9c64256348d4c13a7b09b857e3e1cbb5185917df67d8ced/orjson-3.11.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:29cb1f1b008d936803e2da3d7cba726fc47232c45df531b29edf0b232dd737e7", size = 238600, upload-time = "2025-08-26T17:44:36.875Z" },
{ url = "https://files.pythonhosted.org/packages/10/ce/0c8c87f54f79d051485903dc46226c4d3220b691a151769156054df4562b/orjson-3.11.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97dceed87ed9139884a55db8722428e27bd8452817fbf1869c58b49fecab1120", size = 123526, upload-time = "2025-08-26T17:44:39.574Z" },
{ url = "https://files.pythonhosted.org/packages/ef/d0/249497e861f2d438f45b3ab7b7b361484237414945169aa285608f9f7019/orjson-3.11.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58533f9e8266cb0ac298e259ed7b4d42ed3fa0b78ce76860626164de49e0d467", size = 128075, upload-time = "2025-08-26T17:44:40.672Z" },
{ url = "https://files.pythonhosted.org/packages/e5/64/00485702f640a0fd56144042a1ea196469f4a3ae93681871564bf74fa996/orjson-3.11.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c212cfdd90512fe722fa9bd620de4d46cda691415be86b2e02243242ae81873", size = 130483, upload-time = "2025-08-26T17:44:41.788Z" },
{ url = "https://files.pythonhosted.org/packages/64/81/110d68dba3909171bf3f05619ad0cf187b430e64045ae4e0aa7ccfe25b15/orjson-3.11.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff835b5d3e67d9207343effb03760c00335f8b5285bfceefd4dc967b0e48f6a", size = 132539, upload-time = "2025-08-26T17:44:43.12Z" },
{ url = "https://files.pythonhosted.org/packages/79/92/dba25c22b0ddfafa1e6516a780a00abac28d49f49e7202eb433a53c3e94e/orjson-3.11.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5aa4682912a450c2db89cbd92d356fef47e115dffba07992555542f344d301b", size = 135390, upload-time = "2025-08-26T17:44:44.199Z" },
{ url = "https://files.pythonhosted.org/packages/44/1d/ca2230fd55edbd87b58a43a19032d63a4b180389a97520cc62c535b726f9/orjson-3.11.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d18dd34ea2e860553a579df02041845dee0af8985dff7f8661306f95504ddf", size = 132966, upload-time = "2025-08-26T17:44:45.719Z" },
{ url = "https://files.pythonhosted.org/packages/6e/b9/96bbc8ed3e47e52b487d504bd6861798977445fbc410da6e87e302dc632d/orjson-3.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8b11701bc43be92ea42bd454910437b355dfb63696c06fe953ffb40b5f763b4", size = 131349, upload-time = "2025-08-26T17:44:46.862Z" },
{ url = "https://files.pythonhosted.org/packages/c4/3c/418fbd93d94b0df71cddf96b7fe5894d64a5d890b453ac365120daec30f7/orjson-3.11.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90368277087d4af32d38bd55f9da2ff466d25325bf6167c8f382d8ee40cb2bbc", size = 404087, upload-time = "2025-08-26T17:44:48.079Z" },
{ url = "https://files.pythonhosted.org/packages/5b/a9/2bfd58817d736c2f63608dec0c34857339d423eeed30099b126562822191/orjson-3.11.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd7ff459fb393358d3a155d25b275c60b07a2c83dcd7ea962b1923f5a1134569", size = 146067, upload-time = "2025-08-26T17:44:49.302Z" },
{ url = "https://files.pythonhosted.org/packages/33/ba/29023771f334096f564e48d82ed855a0ed3320389d6748a9c949e25be734/orjson-3.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8d902867b699bcd09c176a280b1acdab57f924489033e53d0afe79817da37e6", size = 135506, upload-time = "2025-08-26T17:44:50.558Z" },
{ url = "https://files.pythonhosted.org/packages/39/62/b5a1eca83f54cb3aa11a9645b8a22f08d97dbd13f27f83aae7c6666a0a05/orjson-3.11.3-cp310-cp310-win32.whl", hash = "sha256:bb93562146120bb51e6b154962d3dadc678ed0fce96513fa6bc06599bb6f6edc", size = 136352, upload-time = "2025-08-26T17:44:51.698Z" },
{ url = "https://files.pythonhosted.org/packages/e3/c0/7ebfaa327d9a9ed982adc0d9420dbce9a3fec45b60ab32c6308f731333fa/orjson-3.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:976c6f1975032cc327161c65d4194c549f2589d88b105a5e3499429a54479770", size = 131539, upload-time = "2025-08-26T17:44:52.974Z" },
{ url = "https://files.pythonhosted.org/packages/cd/8b/360674cd817faef32e49276187922a946468579fcaf37afdfb6c07046e92/orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f", size = 238238, upload-time = "2025-08-26T17:44:54.214Z" },
{ url = "https://files.pythonhosted.org/packages/05/3d/5fa9ea4b34c1a13be7d9046ba98d06e6feb1d8853718992954ab59d16625/orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91", size = 127713, upload-time = "2025-08-26T17:44:55.596Z" },
{ url = "https://files.pythonhosted.org/packages/e5/5f/e18367823925e00b1feec867ff5f040055892fc474bf5f7875649ecfa586/orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904", size = 123241, upload-time = "2025-08-26T17:44:57.185Z" },
{ url = "https://files.pythonhosted.org/packages/0f/bd/3c66b91c4564759cf9f473251ac1650e446c7ba92a7c0f9f56ed54f9f0e6/orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6", size = 127895, upload-time = "2025-08-26T17:44:58.349Z" },
{ url = "https://files.pythonhosted.org/packages/82/b5/dc8dcd609db4766e2967a85f63296c59d4722b39503e5b0bf7fd340d387f/orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d", size = 130303, upload-time = "2025-08-26T17:44:59.491Z" },
{ url = "https://files.pythonhosted.org/packages/48/c2/d58ec5fd1270b2aa44c862171891adc2e1241bd7dab26c8f46eb97c6c6f1/orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038", size = 132366, upload-time = "2025-08-26T17:45:00.654Z" },
{ url = "https://files.pythonhosted.org/packages/73/87/0ef7e22eb8dd1ef940bfe3b9e441db519e692d62ed1aae365406a16d23d0/orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb", size = 135180, upload-time = "2025-08-26T17:45:02.424Z" },
{ url = "https://files.pythonhosted.org/packages/bb/6a/e5bf7b70883f374710ad74faf99bacfc4b5b5a7797c1d5e130350e0e28a3/orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2", size = 132741, upload-time = "2025-08-26T17:45:03.663Z" },
{ url = "https://files.pythonhosted.org/packages/bd/0c/4577fd860b6386ffaa56440e792af01c7882b56d2766f55384b5b0e9d39b/orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55", size = 131104, upload-time = "2025-08-26T17:45:04.939Z" },
{ url = "https://files.pythonhosted.org/packages/66/4b/83e92b2d67e86d1c33f2ea9411742a714a26de63641b082bdbf3d8e481af/orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1", size = 403887, upload-time = "2025-08-26T17:45:06.228Z" },
{ url = "https://files.pythonhosted.org/packages/6d/e5/9eea6a14e9b5ceb4a271a1fd2e1dec5f2f686755c0fab6673dc6ff3433f4/orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824", size = 145855, upload-time = "2025-08-26T17:45:08.338Z" },
{ url = "https://files.pythonhosted.org/packages/45/78/8d4f5ad0c80ba9bf8ac4d0fc71f93a7d0dc0844989e645e2074af376c307/orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f", size = 135361, upload-time = "2025-08-26T17:45:09.625Z" },
{ url = "https://files.pythonhosted.org/packages/0b/5f/16386970370178d7a9b438517ea3d704efcf163d286422bae3b37b88dbb5/orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204", size = 136190, upload-time = "2025-08-26T17:45:10.962Z" },
{ url = "https://files.pythonhosted.org/packages/09/60/db16c6f7a41dd8ac9fb651f66701ff2aeb499ad9ebc15853a26c7c152448/orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b", size = 131389, upload-time = "2025-08-26T17:45:12.285Z" },
{ url = "https://files.pythonhosted.org/packages/3e/2a/bb811ad336667041dea9b8565c7c9faf2f59b47eb5ab680315eea612ef2e/orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e", size = 126120, upload-time = "2025-08-26T17:45:13.515Z" },
{ url = "https://files.pythonhosted.org/packages/3d/b0/a7edab2a00cdcb2688e1c943401cb3236323e7bfd2839815c6131a3742f4/orjson-3.11.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8c752089db84333e36d754c4baf19c0e1437012242048439c7e80eb0e6426e3b", size = 238259, upload-time = "2025-08-26T17:45:15.093Z" },
{ url = "https://files.pythonhosted.org/packages/e1/c6/ff4865a9cc398a07a83342713b5932e4dc3cb4bf4bc04e8f83dedfc0d736/orjson-3.11.3-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9b8761b6cf04a856eb544acdd82fc594b978f12ac3602d6374a7edb9d86fd2c2", size = 127633, upload-time = "2025-08-26T17:45:16.417Z" },
{ url = "https://files.pythonhosted.org/packages/6e/e6/e00bea2d9472f44fe8794f523e548ce0ad51eb9693cf538a753a27b8bda4/orjson-3.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b13974dc8ac6ba22feaa867fc19135a3e01a134b4f7c9c28162fed4d615008a", size = 123061, upload-time = "2025-08-26T17:45:17.673Z" },
{ url = "https://files.pythonhosted.org/packages/54/31/9fbb78b8e1eb3ac605467cb846e1c08d0588506028b37f4ee21f978a51d4/orjson-3.11.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f83abab5bacb76d9c821fd5c07728ff224ed0e52d7a71b7b3de822f3df04e15c", size = 127956, upload-time = "2025-08-26T17:45:19.172Z" },
{ url = "https://files.pythonhosted.org/packages/36/88/b0604c22af1eed9f98d709a96302006915cfd724a7ebd27d6dd11c22d80b/orjson-3.11.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6fbaf48a744b94091a56c62897b27c31ee2da93d826aa5b207131a1e13d4064", size = 130790, upload-time = "2025-08-26T17:45:20.586Z" },
{ url = "https://files.pythonhosted.org/packages/0e/9d/1c1238ae9fffbfed51ba1e507731b3faaf6b846126a47e9649222b0fd06f/orjson-3.11.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc779b4f4bba2847d0d2940081a7b6f7b5877e05408ffbb74fa1faf4a136c424", size = 132385, upload-time = "2025-08-26T17:45:22.036Z" },
{ url = "https://files.pythonhosted.org/packages/a3/b5/c06f1b090a1c875f337e21dd71943bc9d84087f7cdf8c6e9086902c34e42/orjson-3.11.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd4b909ce4c50faa2192da6bb684d9848d4510b736b0611b6ab4020ea6fd2d23", size = 135305, upload-time = "2025-08-26T17:45:23.4Z" },
{ url = "https://files.pythonhosted.org/packages/a0/26/5f028c7d81ad2ebbf84414ba6d6c9cac03f22f5cd0d01eb40fb2d6a06b07/orjson-3.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524b765ad888dc5518bbce12c77c2e83dee1ed6b0992c1790cc5fb49bb4b6667", size = 132875, upload-time = "2025-08-26T17:45:25.182Z" },
{ url = "https://files.pythonhosted.org/packages/fe/d4/b8df70d9cfb56e385bf39b4e915298f9ae6c61454c8154a0f5fd7efcd42e/orjson-3.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84fd82870b97ae3cdcea9d8746e592b6d40e1e4d4527835fc520c588d2ded04f", size = 130940, upload-time = "2025-08-26T17:45:27.209Z" },
{ url = "https://files.pythonhosted.org/packages/da/5e/afe6a052ebc1a4741c792dd96e9f65bf3939d2094e8b356503b68d48f9f5/orjson-3.11.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbecb9709111be913ae6879b07bafd4b0785b44c1eb5cac8ac76da048b3885a1", size = 403852, upload-time = "2025-08-26T17:45:28.478Z" },
{ url = "https://files.pythonhosted.org/packages/f8/90/7bbabafeb2ce65915e9247f14a56b29c9334003536009ef5b122783fe67e/orjson-3.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dba358d55aee552bd868de348f4736ca5a4086d9a62e2bfbbeeb5629fe8b0cc", size = 146293, upload-time = "2025-08-26T17:45:29.86Z" },
{ url = "https://files.pythonhosted.org/packages/27/b3/2d703946447da8b093350570644a663df69448c9d9330e5f1d9cce997f20/orjson-3.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eabcf2e84f1d7105f84580e03012270c7e97ecb1fb1618bda395061b2a84a049", size = 135470, upload-time = "2025-08-26T17:45:31.243Z" },
{ url = "https://files.pythonhosted.org/packages/38/70/b14dcfae7aff0e379b0119c8a812f8396678919c431efccc8e8a0263e4d9/orjson-3.11.3-cp312-cp312-win32.whl", hash = "sha256:3782d2c60b8116772aea8d9b7905221437fdf53e7277282e8d8b07c220f96cca", size = 136248, upload-time = "2025-08-26T17:45:32.567Z" },
{ url = "https://files.pythonhosted.org/packages/35/b8/9e3127d65de7fff243f7f3e53f59a531bf6bb295ebe5db024c2503cc0726/orjson-3.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:79b44319268af2eaa3e315b92298de9a0067ade6e6003ddaef72f8e0bedb94f1", size = 131437, upload-time = "2025-08-26T17:45:34.949Z" },
{ url = "https://files.pythonhosted.org/packages/51/92/a946e737d4d8a7fd84a606aba96220043dcc7d6988b9e7551f7f6d5ba5ad/orjson-3.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:0e92a4e83341ef79d835ca21b8bd13e27c859e4e9e4d7b63defc6e58462a3710", size = 125978, upload-time = "2025-08-26T17:45:36.422Z" },
{ url = "https://files.pythonhosted.org/packages/fc/79/8932b27293ad35919571f77cb3693b5906cf14f206ef17546052a241fdf6/orjson-3.11.3-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:af40c6612fd2a4b00de648aa26d18186cd1322330bd3a3cc52f87c699e995810", size = 238127, upload-time = "2025-08-26T17:45:38.146Z" },
{ url = "https://files.pythonhosted.org/packages/1c/82/cb93cd8cf132cd7643b30b6c5a56a26c4e780c7a145db6f83de977b540ce/orjson-3.11.3-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:9f1587f26c235894c09e8b5b7636a38091a9e6e7fe4531937534749c04face43", size = 127494, upload-time = "2025-08-26T17:45:39.57Z" },
{ url = "https://files.pythonhosted.org/packages/a4/b8/2d9eb181a9b6bb71463a78882bcac1027fd29cf62c38a40cc02fc11d3495/orjson-3.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61dcdad16da5bb486d7227a37a2e789c429397793a6955227cedbd7252eb5a27", size = 123017, upload-time = "2025-08-26T17:45:40.876Z" },
{ url = "https://files.pythonhosted.org/packages/b4/14/a0e971e72d03b509190232356d54c0f34507a05050bd026b8db2bf2c192c/orjson-3.11.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c6d71478e2cbea0a709e8a06365fa63da81da6498a53e4c4f065881d21ae8f", size = 127898, upload-time = "2025-08-26T17:45:42.188Z" },
{ url = "https://files.pythonhosted.org/packages/8e/af/dc74536722b03d65e17042cc30ae586161093e5b1f29bccda24765a6ae47/orjson-3.11.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94112e0098470b665cb0ed06efb187154b63649403b8d5e9aedeb482b4548c", size = 130742, upload-time = "2025-08-26T17:45:43.511Z" },
{ url = "https://files.pythonhosted.org/packages/62/e6/7a3b63b6677bce089fe939353cda24a7679825c43a24e49f757805fc0d8a/orjson-3.11.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b756575aaa2a855a75192f356bbda11a89169830e1439cfb1a3e1a6dde7be", size = 132377, upload-time = "2025-08-26T17:45:45.525Z" },
{ url = "https://files.pythonhosted.org/packages/fc/cd/ce2ab93e2e7eaf518f0fd15e3068b8c43216c8a44ed82ac2b79ce5cef72d/orjson-3.11.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9416cc19a349c167ef76135b2fe40d03cea93680428efee8771f3e9fb66079d", size = 135313, upload-time = "2025-08-26T17:45:46.821Z" },
{ url = "https://files.pythonhosted.org/packages/d0/b4/f98355eff0bd1a38454209bbc73372ce351ba29933cb3e2eba16c04b9448/orjson-3.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b822caf5b9752bc6f246eb08124c3d12bf2175b66ab74bac2ef3bbf9221ce1b2", size = 132908, upload-time = "2025-08-26T17:45:48.126Z" },
{ url = "https://files.pythonhosted.org/packages/eb/92/8f5182d7bc2a1bed46ed960b61a39af8389f0ad476120cd99e67182bfb6d/orjson-3.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:414f71e3bdd5573893bf5ecdf35c32b213ed20aa15536fe2f588f946c318824f", size = 130905, upload-time = "2025-08-26T17:45:49.414Z" },
{ url = "https://files.pythonhosted.org/packages/1a/60/c41ca753ce9ffe3d0f67b9b4c093bdd6e5fdb1bc53064f992f66bb99954d/orjson-3.11.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:828e3149ad8815dc14468f36ab2a4b819237c155ee1370341b91ea4c8672d2ee", size = 403812, upload-time = "2025-08-26T17:45:51.085Z" },
{ url = "https://files.pythonhosted.org/packages/dd/13/e4a4f16d71ce1868860db59092e78782c67082a8f1dc06a3788aef2b41bc/orjson-3.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac9e05f25627ffc714c21f8dfe3a579445a5c392a9c8ae7ba1d0e9fb5333f56e", size = 146277, upload-time = "2025-08-26T17:45:52.851Z" },
{ url = "https://files.pythonhosted.org/packages/8d/8b/bafb7f0afef9344754a3a0597a12442f1b85a048b82108ef2c956f53babd/orjson-3.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e44fbe4000bd321d9f3b648ae46e0196d21577cf66ae684a96ff90b1f7c93633", size = 135418, upload-time = "2025-08-26T17:45:54.806Z" },
{ url = "https://files.pythonhosted.org/packages/60/d4/bae8e4f26afb2c23bea69d2f6d566132584d1c3a5fe89ee8c17b718cab67/orjson-3.11.3-cp313-cp313-win32.whl", hash = "sha256:2039b7847ba3eec1f5886e75e6763a16e18c68a63efc4b029ddf994821e2e66b", size = 136216, upload-time = "2025-08-26T17:45:57.182Z" },
{ url = "https://files.pythonhosted.org/packages/88/76/224985d9f127e121c8cad882cea55f0ebe39f97925de040b75ccd4b33999/orjson-3.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:29be5ac4164aa8bdcba5fa0700a3c9c316b411d8ed9d39ef8a882541bd452fae", size = 131362, upload-time = "2025-08-26T17:45:58.56Z" },
{ url = "https://files.pythonhosted.org/packages/e2/cf/0dce7a0be94bd36d1346be5067ed65ded6adb795fdbe3abd234c8d576d01/orjson-3.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:18bd1435cb1f2857ceb59cfb7de6f92593ef7b831ccd1b9bfb28ca530e539dce", size = 125989, upload-time = "2025-08-26T17:45:59.95Z" },
{ url = "https://files.pythonhosted.org/packages/ef/77/d3b1fef1fc6aaeed4cbf3be2b480114035f4df8fa1a99d2dac1d40d6e924/orjson-3.11.3-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cf4b81227ec86935568c7edd78352a92e97af8da7bd70bdfdaa0d2e0011a1ab4", size = 238115, upload-time = "2025-08-26T17:46:01.669Z" },
{ url = "https://files.pythonhosted.org/packages/e4/6d/468d21d49bb12f900052edcfbf52c292022d0a323d7828dc6376e6319703/orjson-3.11.3-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:bc8bc85b81b6ac9fc4dae393a8c159b817f4c2c9dee5d12b773bddb3b95fc07e", size = 127493, upload-time = "2025-08-26T17:46:03.466Z" },
{ url = "https://files.pythonhosted.org/packages/67/46/1e2588700d354aacdf9e12cc2d98131fb8ac6f31ca65997bef3863edb8ff/orjson-3.11.3-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:88dcfc514cfd1b0de038443c7b3e6a9797ffb1b3674ef1fd14f701a13397f82d", size = 122998, upload-time = "2025-08-26T17:46:04.803Z" },
{ url = "https://files.pythonhosted.org/packages/3b/94/11137c9b6adb3779f1b34fd98be51608a14b430dbc02c6d41134fbba484c/orjson-3.11.3-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d61cd543d69715d5fc0a690c7c6f8dcc307bc23abef9738957981885f5f38229", size = 132915, upload-time = "2025-08-26T17:46:06.237Z" },
{ url = "https://files.pythonhosted.org/packages/10/61/dccedcf9e9bcaac09fdabe9eaee0311ca92115699500efbd31950d878833/orjson-3.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2b7b153ed90ababadbef5c3eb39549f9476890d339cf47af563aea7e07db2451", size = 130907, upload-time = "2025-08-26T17:46:07.581Z" },
{ url = "https://files.pythonhosted.org/packages/0e/fd/0e935539aa7b08b3ca0f817d73034f7eb506792aae5ecc3b7c6e679cdf5f/orjson-3.11.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7909ae2460f5f494fecbcd10613beafe40381fd0316e35d6acb5f3a05bfda167", size = 403852, upload-time = "2025-08-26T17:46:08.982Z" },
{ url = "https://files.pythonhosted.org/packages/4a/2b/50ae1a5505cd1043379132fdb2adb8a05f37b3e1ebffe94a5073321966fd/orjson-3.11.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2030c01cbf77bc67bee7eef1e7e31ecf28649353987775e3583062c752da0077", size = 146309, upload-time = "2025-08-26T17:46:10.576Z" },
{ url = "https://files.pythonhosted.org/packages/cd/1d/a473c158e380ef6f32753b5f39a69028b25ec5be331c2049a2201bde2e19/orjson-3.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a0169ebd1cbd94b26c7a7ad282cf5c2744fce054133f959e02eb5265deae1872", size = 135424, upload-time = "2025-08-26T17:46:12.386Z" },
{ url = "https://files.pythonhosted.org/packages/da/09/17d9d2b60592890ff7382e591aa1d9afb202a266b180c3d4049b1ec70e4a/orjson-3.11.3-cp314-cp314-win32.whl", hash = "sha256:0c6d7328c200c349e3a4c6d8c83e0a5ad029bdc2d417f234152bf34842d0fc8d", size = 136266, upload-time = "2025-08-26T17:46:13.853Z" },
{ url = "https://files.pythonhosted.org/packages/15/58/358f6846410a6b4958b74734727e582ed971e13d335d6c7ce3e47730493e/orjson-3.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:317bbe2c069bbc757b1a2e4105b64aacd3bc78279b66a6b9e51e846e4809f804", size = 131351, upload-time = "2025-08-26T17:46:15.27Z" },
{ url = "https://files.pythonhosted.org/packages/28/01/d6b274a0635be0468d4dbd9cafe80c47105937a0d42434e805e67cd2ed8b/orjson-3.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:e8f6a7a27d7b7bec81bd5924163e9af03d49bbb63013f107b48eb5d16db711bc", size = 125985, upload-time = "2025-08-26T17:46:16.67Z" },
{ url = "https://files.pythonhosted.org/packages/e0/30/5aed63d5af1c8b02fbd2a8d83e2a6c8455e30504c50dbf08c8b51403d873/orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1", size = 243870, upload-time = "2025-10-24T15:48:28.908Z" },
{ url = "https://files.pythonhosted.org/packages/44/1f/da46563c08bef33c41fd63c660abcd2184b4d2b950c8686317d03b9f5f0c/orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44", size = 130622, upload-time = "2025-10-24T15:48:31.361Z" },
{ url = "https://files.pythonhosted.org/packages/02/bd/b551a05d0090eab0bf8008a13a14edc0f3c3e0236aa6f5b697760dd2817b/orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c", size = 129344, upload-time = "2025-10-24T15:48:32.71Z" },
{ url = "https://files.pythonhosted.org/packages/87/6c/9ddd5e609f443b2548c5e7df3c44d0e86df2c68587a0e20c50018cdec535/orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23", size = 136633, upload-time = "2025-10-24T15:48:34.128Z" },
{ url = "https://files.pythonhosted.org/packages/95/f2/9f04f2874c625a9fb60f6918c33542320661255323c272e66f7dcce14df2/orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea", size = 137695, upload-time = "2025-10-24T15:48:35.654Z" },
{ url = "https://files.pythonhosted.org/packages/d2/c2/c7302afcbdfe8a891baae0e2cee091583a30e6fa613e8bdf33b0e9c8a8c7/orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba", size = 136879, upload-time = "2025-10-24T15:48:37.483Z" },
{ url = "https://files.pythonhosted.org/packages/c6/3a/b31c8f0182a3e27f48e703f46e61bb769666cd0dac4700a73912d07a1417/orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff", size = 136374, upload-time = "2025-10-24T15:48:38.624Z" },
{ url = "https://files.pythonhosted.org/packages/29/d0/fd9ab96841b090d281c46df566b7f97bc6c8cd9aff3f3ebe99755895c406/orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac", size = 140519, upload-time = "2025-10-24T15:48:39.756Z" },
{ url = "https://files.pythonhosted.org/packages/d6/ce/36eb0f15978bb88e33a3480e1a3fb891caa0f189ba61ce7713e0ccdadabf/orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79", size = 406522, upload-time = "2025-10-24T15:48:41.198Z" },
{ url = "https://files.pythonhosted.org/packages/85/11/e8af3161a288f5c6a00c188fc729c7ba193b0cbc07309a1a29c004347c30/orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827", size = 149790, upload-time = "2025-10-24T15:48:42.664Z" },
{ url = "https://files.pythonhosted.org/packages/ea/96/209d52db0cf1e10ed48d8c194841e383e23c2ced5a2ee766649fe0e32d02/orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b", size = 140040, upload-time = "2025-10-24T15:48:44.042Z" },
{ url = "https://files.pythonhosted.org/packages/ef/0e/526db1395ccb74c3d59ac1660b9a325017096dc5643086b38f27662b4add/orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3", size = 135955, upload-time = "2025-10-24T15:48:45.495Z" },
{ url = "https://files.pythonhosted.org/packages/e6/69/18a778c9de3702b19880e73c9866b91cc85f904b885d816ba1ab318b223c/orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc", size = 131577, upload-time = "2025-10-24T15:48:46.609Z" },
{ url = "https://files.pythonhosted.org/packages/63/1d/1ea6005fffb56715fd48f632611e163d1604e8316a5bad2288bee9a1c9eb/orjson-3.11.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e59d23cd93ada23ec59a96f215139753fbfe3a4d989549bcb390f8c00370b39", size = 243498, upload-time = "2025-10-24T15:48:48.101Z" },
{ url = "https://files.pythonhosted.org/packages/37/d7/ffed10c7da677f2a9da307d491b9eb1d0125b0307019c4ad3d665fd31f4f/orjson-3.11.4-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5c3aedecfc1beb988c27c79d52ebefab93b6c3921dbec361167e6559aba2d36d", size = 128961, upload-time = "2025-10-24T15:48:49.571Z" },
{ url = "https://files.pythonhosted.org/packages/a2/96/3e4d10a18866d1368f73c8c44b7fe37cc8a15c32f2a7620be3877d4c55a3/orjson-3.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9e5301f1c2caa2a9a4a303480d79c9ad73560b2e7761de742ab39fe59d9175", size = 130321, upload-time = "2025-10-24T15:48:50.713Z" },
{ url = "https://files.pythonhosted.org/packages/eb/1f/465f66e93f434f968dd74d5b623eb62c657bdba2332f5a8be9f118bb74c7/orjson-3.11.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8873812c164a90a79f65368f8f96817e59e35d0cc02786a5356f0e2abed78040", size = 129207, upload-time = "2025-10-24T15:48:52.193Z" },
{ url = "https://files.pythonhosted.org/packages/28/43/d1e94837543321c119dff277ae8e348562fe8c0fafbb648ef7cb0c67e521/orjson-3.11.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d7feb0741ebb15204e748f26c9638e6665a5fa93c37a2c73d64f1669b0ddc63", size = 136323, upload-time = "2025-10-24T15:48:54.806Z" },
{ url = "https://files.pythonhosted.org/packages/bf/04/93303776c8890e422a5847dd012b4853cdd88206b8bbd3edc292c90102d1/orjson-3.11.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ee5487fefee21e6910da4c2ee9eef005bee568a0879834df86f888d2ffbdd9", size = 137440, upload-time = "2025-10-24T15:48:56.326Z" },
{ url = "https://files.pythonhosted.org/packages/1e/ef/75519d039e5ae6b0f34d0336854d55544ba903e21bf56c83adc51cd8bf82/orjson-3.11.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d40d46f348c0321df01507f92b95a377240c4ec31985225a6668f10e2676f9a", size = 136680, upload-time = "2025-10-24T15:48:57.476Z" },
{ url = "https://files.pythonhosted.org/packages/b5/18/bf8581eaae0b941b44efe14fee7b7862c3382fbc9a0842132cfc7cf5ecf4/orjson-3.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95713e5fc8af84d8edc75b785d2386f653b63d62b16d681687746734b4dfc0be", size = 136160, upload-time = "2025-10-24T15:48:59.631Z" },
{ url = "https://files.pythonhosted.org/packages/c4/35/a6d582766d351f87fc0a22ad740a641b0a8e6fc47515e8614d2e4790ae10/orjson-3.11.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad73ede24f9083614d6c4ca9a85fe70e33be7bf047ec586ee2363bc7418fe4d7", size = 140318, upload-time = "2025-10-24T15:49:00.834Z" },
{ url = "https://files.pythonhosted.org/packages/76/b3/5a4801803ab2e2e2d703bce1a56540d9f99a9143fbec7bf63d225044fef8/orjson-3.11.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:842289889de515421f3f224ef9c1f1efb199a32d76d8d2ca2706fa8afe749549", size = 406330, upload-time = "2025-10-24T15:49:02.327Z" },
{ url = "https://files.pythonhosted.org/packages/80/55/a8f682f64833e3a649f620eafefee175cbfeb9854fc5b710b90c3bca45df/orjson-3.11.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3b2427ed5791619851c52a1261b45c233930977e7de8cf36de05636c708fa905", size = 149580, upload-time = "2025-10-24T15:49:03.517Z" },
{ url = "https://files.pythonhosted.org/packages/ad/e4/c132fa0c67afbb3eb88274fa98df9ac1f631a675e7877037c611805a4413/orjson-3.11.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c36e524af1d29982e9b190573677ea02781456b2e537d5840e4538a5ec41907", size = 139846, upload-time = "2025-10-24T15:49:04.761Z" },
{ url = "https://files.pythonhosted.org/packages/54/06/dc3491489efd651fef99c5908e13951abd1aead1257c67f16135f95ce209/orjson-3.11.4-cp311-cp311-win32.whl", hash = "sha256:87255b88756eab4a68ec61837ca754e5d10fa8bc47dc57f75cedfeaec358d54c", size = 135781, upload-time = "2025-10-24T15:49:05.969Z" },
{ url = "https://files.pythonhosted.org/packages/79/b7/5e5e8d77bd4ea02a6ac54c42c818afb01dd31961be8a574eb79f1d2cfb1e/orjson-3.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:e2d5d5d798aba9a0e1fede8d853fa899ce2cb930ec0857365f700dffc2c7af6a", size = 131391, upload-time = "2025-10-24T15:49:07.355Z" },
{ url = "https://files.pythonhosted.org/packages/0f/dc/9484127cc1aa213be398ed735f5f270eedcb0c0977303a6f6ddc46b60204/orjson-3.11.4-cp311-cp311-win_arm64.whl", hash = "sha256:6bb6bb41b14c95d4f2702bce9975fda4516f1db48e500102fc4d8119032ff045", size = 126252, upload-time = "2025-10-24T15:49:08.869Z" },
{ url = "https://files.pythonhosted.org/packages/63/51/6b556192a04595b93e277a9ff71cd0cc06c21a7df98bcce5963fa0f5e36f/orjson-3.11.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d4371de39319d05d3f482f372720b841c841b52f5385bd99c61ed69d55d9ab50", size = 243571, upload-time = "2025-10-24T15:49:10.008Z" },
{ url = "https://files.pythonhosted.org/packages/1c/2c/2602392ddf2601d538ff11848b98621cd465d1a1ceb9db9e8043181f2f7b/orjson-3.11.4-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e41fd3b3cac850eaae78232f37325ed7d7436e11c471246b87b2cd294ec94853", size = 128891, upload-time = "2025-10-24T15:49:11.297Z" },
{ url = "https://files.pythonhosted.org/packages/4e/47/bf85dcf95f7a3a12bf223394a4f849430acd82633848d52def09fa3f46ad/orjson-3.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600e0e9ca042878c7fdf189cf1b028fe2c1418cc9195f6cb9824eb6ed99cb938", size = 130137, upload-time = "2025-10-24T15:49:12.544Z" },
{ url = "https://files.pythonhosted.org/packages/b4/4d/a0cb31007f3ab6f1fd2a1b17057c7c349bc2baf8921a85c0180cc7be8011/orjson-3.11.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7bbf9b333f1568ef5da42bc96e18bf30fd7f8d54e9ae066d711056add508e415", size = 129152, upload-time = "2025-10-24T15:49:13.754Z" },
{ url = "https://files.pythonhosted.org/packages/f7/ef/2811def7ce3d8576b19e3929fff8f8f0d44bc5eb2e0fdecb2e6e6cc6c720/orjson-3.11.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806363144bb6e7297b8e95870e78d30a649fdc4e23fc84daa80c8ebd366ce44", size = 136834, upload-time = "2025-10-24T15:49:15.307Z" },
{ url = "https://files.pythonhosted.org/packages/00/d4/9aee9e54f1809cec8ed5abd9bc31e8a9631d19460e3b8470145d25140106/orjson-3.11.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad355e8308493f527d41154e9053b86a5be892b3b359a5c6d5d95cda23601cb2", size = 137519, upload-time = "2025-10-24T15:49:16.557Z" },
{ url = "https://files.pythonhosted.org/packages/db/ea/67bfdb5465d5679e8ae8d68c11753aaf4f47e3e7264bad66dc2f2249e643/orjson-3.11.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a7517482667fb9f0ff1b2f16fe5829296ed7a655d04d68cd9711a4d8a4e708", size = 136749, upload-time = "2025-10-24T15:49:17.796Z" },
{ url = "https://files.pythonhosted.org/packages/01/7e/62517dddcfce6d53a39543cd74d0dccfcbdf53967017c58af68822100272/orjson-3.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97eb5942c7395a171cbfecc4ef6701fc3c403e762194683772df4c54cfbb2210", size = 136325, upload-time = "2025-10-24T15:49:19.347Z" },
{ url = "https://files.pythonhosted.org/packages/18/ae/40516739f99ab4c7ec3aaa5cc242d341fcb03a45d89edeeaabc5f69cb2cf/orjson-3.11.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:149d95d5e018bdd822e3f38c103b1a7c91f88d38a88aada5c4e9b3a73a244241", size = 140204, upload-time = "2025-10-24T15:49:20.545Z" },
{ url = "https://files.pythonhosted.org/packages/82/18/ff5734365623a8916e3a4037fcef1cd1782bfc14cf0992afe7940c5320bf/orjson-3.11.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:624f3951181eb46fc47dea3d221554e98784c823e7069edb5dbd0dc826ac909b", size = 406242, upload-time = "2025-10-24T15:49:21.884Z" },
{ url = "https://files.pythonhosted.org/packages/e1/43/96436041f0a0c8c8deca6a05ebeaf529bf1de04839f93ac5e7c479807aec/orjson-3.11.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:03bfa548cf35e3f8b3a96c4e8e41f753c686ff3d8e182ce275b1751deddab58c", size = 150013, upload-time = "2025-10-24T15:49:23.185Z" },
{ url = "https://files.pythonhosted.org/packages/1b/48/78302d98423ed8780479a1e682b9aecb869e8404545d999d34fa486e573e/orjson-3.11.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:525021896afef44a68148f6ed8a8bf8375553d6066c7f48537657f64823565b9", size = 139951, upload-time = "2025-10-24T15:49:24.428Z" },
{ url = "https://files.pythonhosted.org/packages/4a/7b/ad613fdcdaa812f075ec0875143c3d37f8654457d2af17703905425981bf/orjson-3.11.4-cp312-cp312-win32.whl", hash = "sha256:b58430396687ce0f7d9eeb3dd47761ca7d8fda8e9eb92b3077a7a353a75efefa", size = 136049, upload-time = "2025-10-24T15:49:25.973Z" },
{ url = "https://files.pythonhosted.org/packages/b9/3c/9cf47c3ff5f39b8350fb21ba65d789b6a1129d4cbb3033ba36c8a9023520/orjson-3.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:c6dbf422894e1e3c80a177133c0dda260f81428f9de16d61041949f6a2e5c140", size = 131461, upload-time = "2025-10-24T15:49:27.259Z" },
{ url = "https://files.pythonhosted.org/packages/c6/3b/e2425f61e5825dc5b08c2a5a2b3af387eaaca22a12b9c8c01504f8614c36/orjson-3.11.4-cp312-cp312-win_arm64.whl", hash = "sha256:d38d2bc06d6415852224fcc9c0bfa834c25431e466dc319f0edd56cca81aa96e", size = 126167, upload-time = "2025-10-24T15:49:28.511Z" },
{ url = "https://files.pythonhosted.org/packages/23/15/c52aa7112006b0f3d6180386c3a46ae057f932ab3425bc6f6ac50431cca1/orjson-3.11.4-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2d6737d0e616a6e053c8b4acc9eccea6b6cce078533666f32d140e4f85002534", size = 243525, upload-time = "2025-10-24T15:49:29.737Z" },
{ url = "https://files.pythonhosted.org/packages/ec/38/05340734c33b933fd114f161f25a04e651b0c7c33ab95e9416ade5cb44b8/orjson-3.11.4-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:afb14052690aa328cc118a8e09f07c651d301a72e44920b887c519b313d892ff", size = 128871, upload-time = "2025-10-24T15:49:31.109Z" },
{ url = "https://files.pythonhosted.org/packages/55/b9/ae8d34899ff0c012039b5a7cb96a389b2476e917733294e498586b45472d/orjson-3.11.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38aa9e65c591febb1b0aed8da4d469eba239d434c218562df179885c94e1a3ad", size = 130055, upload-time = "2025-10-24T15:49:33.382Z" },
{ url = "https://files.pythonhosted.org/packages/33/aa/6346dd5073730451bee3681d901e3c337e7ec17342fb79659ec9794fc023/orjson-3.11.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2cf4dfaf9163b0728d061bebc1e08631875c51cd30bf47cb9e3293bfbd7dcd5", size = 129061, upload-time = "2025-10-24T15:49:34.935Z" },
{ url = "https://files.pythonhosted.org/packages/39/e4/8eea51598f66a6c853c380979912d17ec510e8e66b280d968602e680b942/orjson-3.11.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89216ff3dfdde0e4070932e126320a1752c9d9a758d6a32ec54b3b9334991a6a", size = 136541, upload-time = "2025-10-24T15:49:36.923Z" },
{ url = "https://files.pythonhosted.org/packages/9a/47/cb8c654fa9adcc60e99580e17c32b9e633290e6239a99efa6b885aba9dbc/orjson-3.11.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9daa26ca8e97fae0ce8aa5d80606ef8f7914e9b129b6b5df9104266f764ce436", size = 137535, upload-time = "2025-10-24T15:49:38.307Z" },
{ url = "https://files.pythonhosted.org/packages/43/92/04b8cc5c2b729f3437ee013ce14a60ab3d3001465d95c184758f19362f23/orjson-3.11.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c8b2769dc31883c44a9cd126560327767f848eb95f99c36c9932f51090bfce9", size = 136703, upload-time = "2025-10-24T15:49:40.795Z" },
{ url = "https://files.pythonhosted.org/packages/aa/fd/d0733fcb9086b8be4ebcfcda2d0312865d17d0d9884378b7cffb29d0763f/orjson-3.11.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1469d254b9884f984026bd9b0fa5bbab477a4bfe558bba6848086f6d43eb5e73", size = 136293, upload-time = "2025-10-24T15:49:42.347Z" },
{ url = "https://files.pythonhosted.org/packages/c2/d7/3c5514e806837c210492d72ae30ccf050ce3f940f45bf085bab272699ef4/orjson-3.11.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:68e44722541983614e37117209a194e8c3ad07838ccb3127d96863c95ec7f1e0", size = 140131, upload-time = "2025-10-24T15:49:43.638Z" },
{ url = "https://files.pythonhosted.org/packages/9c/dd/ba9d32a53207babf65bd510ac4d0faaa818bd0df9a9c6f472fe7c254f2e3/orjson-3.11.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e7805fda9672c12be2f22ae124dcd7b03928d6c197544fe12174b86553f3196", size = 406164, upload-time = "2025-10-24T15:49:45.498Z" },
{ url = "https://files.pythonhosted.org/packages/8e/f9/f68ad68f4af7c7bde57cd514eaa2c785e500477a8bc8f834838eb696a685/orjson-3.11.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04b69c14615fb4434ab867bf6f38b2d649f6f300af30a6705397e895f7aec67a", size = 149859, upload-time = "2025-10-24T15:49:46.981Z" },
{ url = "https://files.pythonhosted.org/packages/b6/d2/7f847761d0c26818395b3d6b21fb6bc2305d94612a35b0a30eae65a22728/orjson-3.11.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:639c3735b8ae7f970066930e58cf0ed39a852d417c24acd4a25fc0b3da3c39a6", size = 139926, upload-time = "2025-10-24T15:49:48.321Z" },
{ url = "https://files.pythonhosted.org/packages/9f/37/acd14b12dc62db9a0e1d12386271b8661faae270b22492580d5258808975/orjson-3.11.4-cp313-cp313-win32.whl", hash = "sha256:6c13879c0d2964335491463302a6ca5ad98105fc5db3565499dcb80b1b4bd839", size = 136007, upload-time = "2025-10-24T15:49:49.938Z" },
{ url = "https://files.pythonhosted.org/packages/c0/a9/967be009ddf0a1fffd7a67de9c36656b28c763659ef91352acc02cbe364c/orjson-3.11.4-cp313-cp313-win_amd64.whl", hash = "sha256:09bf242a4af98732db9f9a1ec57ca2604848e16f132e3f72edfd3c5c96de009a", size = 131314, upload-time = "2025-10-24T15:49:51.248Z" },
{ url = "https://files.pythonhosted.org/packages/cb/db/399abd6950fbd94ce125cb8cd1a968def95174792e127b0642781e040ed4/orjson-3.11.4-cp313-cp313-win_arm64.whl", hash = "sha256:a85f0adf63319d6c1ba06fb0dbf997fced64a01179cf17939a6caca662bf92de", size = 126152, upload-time = "2025-10-24T15:49:52.922Z" },
{ url = "https://files.pythonhosted.org/packages/25/e3/54ff63c093cc1697e758e4fceb53164dd2661a7d1bcd522260ba09f54533/orjson-3.11.4-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:42d43a1f552be1a112af0b21c10a5f553983c2a0938d2bbb8ecd8bc9fb572803", size = 243501, upload-time = "2025-10-24T15:49:54.288Z" },
{ url = "https://files.pythonhosted.org/packages/ac/7d/e2d1076ed2e8e0ae9badca65bf7ef22710f93887b29eaa37f09850604e09/orjson-3.11.4-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:26a20f3fbc6c7ff2cb8e89c4c5897762c9d88cf37330c6a117312365d6781d54", size = 128862, upload-time = "2025-10-24T15:49:55.961Z" },
{ url = "https://files.pythonhosted.org/packages/9f/37/ca2eb40b90621faddfa9517dfe96e25f5ae4d8057a7c0cdd613c17e07b2c/orjson-3.11.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e3f20be9048941c7ffa8fc523ccbd17f82e24df1549d1d1fe9317712d19938e", size = 130047, upload-time = "2025-10-24T15:49:57.406Z" },
{ url = "https://files.pythonhosted.org/packages/c7/62/1021ed35a1f2bad9040f05fa4cc4f9893410df0ba3eaa323ccf899b1c90a/orjson-3.11.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aac364c758dc87a52e68e349924d7e4ded348dedff553889e4d9f22f74785316", size = 129073, upload-time = "2025-10-24T15:49:58.782Z" },
{ url = "https://files.pythonhosted.org/packages/e8/3f/f84d966ec2a6fd5f73b1a707e7cd876813422ae4bf9f0145c55c9c6a0f57/orjson-3.11.4-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5c54a6d76e3d741dcc3f2707f8eeb9ba2a791d3adbf18f900219b62942803b1", size = 136597, upload-time = "2025-10-24T15:50:00.12Z" },
{ url = "https://files.pythonhosted.org/packages/32/78/4fa0aeca65ee82bbabb49e055bd03fa4edea33f7c080c5c7b9601661ef72/orjson-3.11.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f28485bdca8617b79d44627f5fb04336897041dfd9fa66d383a49d09d86798bc", size = 137515, upload-time = "2025-10-24T15:50:01.57Z" },
{ url = "https://files.pythonhosted.org/packages/c1/9d/0c102e26e7fde40c4c98470796d050a2ec1953897e2c8ab0cb95b0759fa2/orjson-3.11.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bfc2a484cad3585e4ba61985a6062a4c2ed5c7925db6d39f1fa267c9d166487f", size = 136703, upload-time = "2025-10-24T15:50:02.944Z" },
{ url = "https://files.pythonhosted.org/packages/df/ac/2de7188705b4cdfaf0b6c97d2f7849c17d2003232f6e70df98602173f788/orjson-3.11.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e34dbd508cb91c54f9c9788923daca129fe5b55c5b4eebe713bf5ed3791280cf", size = 136311, upload-time = "2025-10-24T15:50:04.441Z" },
{ url = "https://files.pythonhosted.org/packages/e0/52/847fcd1a98407154e944feeb12e3b4d487a0e264c40191fb44d1269cbaa1/orjson-3.11.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b13c478fa413d4b4ee606ec8e11c3b2e52683a640b006bb586b3041c2ca5f606", size = 140127, upload-time = "2025-10-24T15:50:07.398Z" },
{ url = "https://files.pythonhosted.org/packages/c1/ae/21d208f58bdb847dd4d0d9407e2929862561841baa22bdab7aea10ca088e/orjson-3.11.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:724ca721ecc8a831b319dcd72cfa370cc380db0bf94537f08f7edd0a7d4e1780", size = 406201, upload-time = "2025-10-24T15:50:08.796Z" },
{ url = "https://files.pythonhosted.org/packages/8d/55/0789d6de386c8366059db098a628e2ad8798069e94409b0d8935934cbcb9/orjson-3.11.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:977c393f2e44845ce1b540e19a786e9643221b3323dae190668a98672d43fb23", size = 149872, upload-time = "2025-10-24T15:50:10.234Z" },
{ url = "https://files.pythonhosted.org/packages/cc/1d/7ff81ea23310e086c17b41d78a72270d9de04481e6113dbe2ac19118f7fb/orjson-3.11.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e539e382cf46edec157ad66b0b0872a90d829a6b71f17cb633d6c160a223155", size = 139931, upload-time = "2025-10-24T15:50:11.623Z" },
{ url = "https://files.pythonhosted.org/packages/77/92/25b886252c50ed64be68c937b562b2f2333b45afe72d53d719e46a565a50/orjson-3.11.4-cp314-cp314-win32.whl", hash = "sha256:d63076d625babab9db5e7836118bdfa086e60f37d8a174194ae720161eb12394", size = 136065, upload-time = "2025-10-24T15:50:13.025Z" },
{ url = "https://files.pythonhosted.org/packages/63/b8/718eecf0bb7e9d64e4956afaafd23db9f04c776d445f59fe94f54bdae8f0/orjson-3.11.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a54d6635fa3aaa438ae32e8570b9f0de36f3f6562c308d2a2a452e8b0592db1", size = 131310, upload-time = "2025-10-24T15:50:14.46Z" },
{ url = "https://files.pythonhosted.org/packages/1a/bf/def5e25d4d8bfce296a9a7c8248109bf58622c21618b590678f945a2c59c/orjson-3.11.4-cp314-cp314-win_arm64.whl", hash = "sha256:78b999999039db3cf58f6d230f524f04f75f129ba3d1ca2ed121f8657e575d3d", size = 126151, upload-time = "2025-10-24T15:50:15.878Z" },
]
[[package]]
name = "ormsgpack"
version = "1.11.0"
version = "1.12.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/65/f8/224c342c0e03e131aaa1a1f19aa2244e167001783a433f4eed10eedd834b/ormsgpack-1.11.0.tar.gz", hash = "sha256:7c9988e78fedba3292541eb3bb274fa63044ef4da2ddb47259ea70c05dee4206", size = 49357, upload-time = "2025-10-08T17:29:15.621Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6c/67/d5ef41c3b4a94400be801984ef7c7fc9623e1a82b643e74eeec367e7462b/ormsgpack-1.12.0.tar.gz", hash = "sha256:94be818fdbb0285945839b88763b269987787cb2f7ef280cad5d6ec815b7e608", size = 49959, upload-time = "2025-11-04T18:30:10.083Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/3d/6996193cb2babc47fc92456223bef7d141065357ad4204eccf313f47a7b3/ormsgpack-1.11.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:03d4e658dd6e1882a552ce1d13cc7b49157414e7d56a4091fbe7823225b08cba", size = 367965, upload-time = "2025-10-08T17:28:06.736Z" },
{ url = "https://files.pythonhosted.org/packages/35/89/c83b805dd9caebb046f4ceeed3706d0902ed2dbbcf08b8464e89f2c52e05/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bb67eb913c2b703f0ed39607fc56e50724dd41f92ce080a586b4d6149eb3fe4", size = 195209, upload-time = "2025-10-08T17:28:08.395Z" },
{ url = "https://files.pythonhosted.org/packages/3a/17/427d9c4f77b120f0af01d7a71d8144771c9388c2a81f712048320e31353b/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e54175b92411f73a238e5653a998627f6660de3def37d9dd7213e0fd264ca56", size = 205868, upload-time = "2025-10-08T17:28:09.688Z" },
{ url = "https://files.pythonhosted.org/packages/82/32/a9ce218478bdbf3fee954159900e24b314ab3064f7b6a217ccb1e3464324/ormsgpack-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca2b197f4556e1823d1319869d4c5dc278be335286d2308b0ed88b59a5afcc25", size = 207391, upload-time = "2025-10-08T17:28:11.031Z" },
{ url = "https://files.pythonhosted.org/packages/7a/d3/4413fe7454711596fdf08adabdfa686580e4656702015108e4975f00a022/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc62388262f58c792fe1e450e1d9dbcc174ed2fb0b43db1675dd7c5ff2319d6a", size = 377078, upload-time = "2025-10-08T17:28:12.39Z" },
{ url = "https://files.pythonhosted.org/packages/f0/ad/13fae555a45e35ca1ca929a27c9ee0a3ecada931b9d44454658c543f9b9c/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c48bc10af74adfbc9113f3fb160dc07c61ad9239ef264c17e449eba3de343dc2", size = 470776, upload-time = "2025-10-08T17:28:13.484Z" },
{ url = "https://files.pythonhosted.org/packages/36/60/51178b093ffc4e2ef3381013a67223e7d56224434fba80047249f4a84b26/ormsgpack-1.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a608d3a1d4fa4acdc5082168a54513cff91f47764cef435e81a483452f5f7647", size = 380862, upload-time = "2025-10-08T17:28:14.747Z" },
{ url = "https://files.pythonhosted.org/packages/a6/e3/1cb6c161335e2ae7d711ecfb007a31a3936603626e347c13e5e53b7c7cf8/ormsgpack-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:97217b4f7f599ba45916b9c4c4b1d5656e8e2a4d91e2e191d72a7569d3c30923", size = 112058, upload-time = "2025-10-08T17:28:15.777Z" },
{ url = "https://files.pythonhosted.org/packages/a4/7c/90164d00e8e94b48eff8a17bc2f4be6b71ae356a00904bc69d5e8afe80fb/ormsgpack-1.11.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c7be823f47d8e36648d4bc90634b93f02b7d7cc7480081195f34767e86f181fb", size = 367964, upload-time = "2025-10-08T17:28:16.778Z" },
{ url = "https://files.pythonhosted.org/packages/7b/c2/fb6331e880a3446c1341e72c77bd5a46da3e92a8e2edf7ea84a4c6c14fff/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68accf15d1b013812755c0eb7a30e1fc2f81eb603a1a143bf0cda1b301cfa797", size = 195209, upload-time = "2025-10-08T17:28:17.796Z" },
{ url = "https://files.pythonhosted.org/packages/18/50/4943fb5df8cc02da6b7b1ee2c2a7fb13aebc9f963d69280b1bb02b1fb178/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:805d06fb277d9a4e503c0c707545b49cde66cbb2f84e5cf7c58d81dfc20d8658", size = 205869, upload-time = "2025-10-08T17:28:19.01Z" },
{ url = "https://files.pythonhosted.org/packages/1c/fa/e7e06835bfea9adeef43915143ce818098aecab0cbd3df584815adf3e399/ormsgpack-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1e57cdf003e77acc43643bda151dc01f97147a64b11cdee1380bb9698a7601c", size = 207391, upload-time = "2025-10-08T17:28:20.352Z" },
{ url = "https://files.pythonhosted.org/packages/33/f0/f28a19e938a14ec223396e94f4782fbcc023f8c91f2ab6881839d3550f32/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37fc05bdaabd994097c62e2f3e08f66b03f856a640ede6dc5ea340bd15b77f4d", size = 377081, upload-time = "2025-10-08T17:28:21.926Z" },
{ url = "https://files.pythonhosted.org/packages/4f/e3/73d1d7287637401b0b6637e30ba9121e1aa1d9f5ea185ed9834ca15d512c/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a6e9db6c73eb46b2e4d97bdffd1368a66f54e6806b563a997b19c004ef165e1d", size = 470779, upload-time = "2025-10-08T17:28:22.993Z" },
{ url = "https://files.pythonhosted.org/packages/9c/46/7ba7f9721e766dd0dfe4cedf444439447212abffe2d2f4538edeeec8ccbd/ormsgpack-1.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e9c44eae5ac0196ffc8b5ed497c75511056508f2303fa4d36b208eb820cf209e", size = 380865, upload-time = "2025-10-08T17:28:24.012Z" },
{ url = "https://files.pythonhosted.org/packages/a7/7d/bb92a0782bbe0626c072c0320001410cf3f6743ede7dc18f034b1a18edef/ormsgpack-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:11d0dfaf40ae7c6de4f7dbd1e4892e2e6a55d911ab1774357c481158d17371e4", size = 112058, upload-time = "2025-10-08T17:28:25.015Z" },
{ url = "https://files.pythonhosted.org/packages/28/1a/f07c6f74142815d67e1d9d98c5b2960007100408ade8242edac96d5d1c73/ormsgpack-1.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:0c63a3f7199a3099c90398a1bdf0cb577b06651a442dc5efe67f2882665e5b02", size = 105894, upload-time = "2025-10-08T17:28:25.93Z" },
{ url = "https://files.pythonhosted.org/packages/1e/16/2805ebfb3d2cbb6c661b5fae053960fc90a2611d0d93e2207e753e836117/ormsgpack-1.11.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3434d0c8d67de27d9010222de07fb6810fb9af3bb7372354ffa19257ac0eb83b", size = 368474, upload-time = "2025-10-08T17:28:27.532Z" },
{ url = "https://files.pythonhosted.org/packages/6f/39/6afae47822dca0ce4465d894c0bbb860a850ce29c157882dbdf77a5dd26e/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2da5bd097e8dbfa4eb0d4ccfe79acd6f538dee4493579e2debfe4fc8f4ca89b", size = 195321, upload-time = "2025-10-08T17:28:28.573Z" },
{ url = "https://files.pythonhosted.org/packages/f6/54/11eda6b59f696d2f16de469bfbe539c9f469c4b9eef5a513996b5879c6e9/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fdbaa0a5a8606a486960b60c24f2d5235d30ac7a8b98eeaea9854bffef14dc3d", size = 206036, upload-time = "2025-10-08T17:28:29.785Z" },
{ url = "https://files.pythonhosted.org/packages/1e/86/890430f704f84c4699ddad61c595d171ea2fd77a51fbc106f83981e83939/ormsgpack-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3682f24f800c1837017ee90ce321086b2cbaef88db7d4cdbbda1582aa6508159", size = 207615, upload-time = "2025-10-08T17:28:31.076Z" },
{ url = "https://files.pythonhosted.org/packages/b6/b9/77383e16c991c0ecb772205b966fc68d9c519e0b5f9c3913283cbed30ffe/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fcca21202bb05ccbf3e0e92f560ee59b9331182e4c09c965a28155efbb134993", size = 377195, upload-time = "2025-10-08T17:28:32.436Z" },
{ url = "https://files.pythonhosted.org/packages/20/e2/15f9f045d4947f3c8a5e0535259fddf027b17b1215367488b3565c573b9d/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c30e5c4655ba46152d722ec7468e8302195e6db362ec1ae2c206bc64f6030e43", size = 470960, upload-time = "2025-10-08T17:28:33.556Z" },
{ url = "https://files.pythonhosted.org/packages/b8/61/403ce188c4c495bc99dff921a0ad3d9d352dd6d3c4b629f3638b7f0cf79b/ormsgpack-1.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7138a341f9e2c08c59368f03d3be25e8b87b3baaf10d30fb1f6f6b52f3d47944", size = 381174, upload-time = "2025-10-08T17:28:34.781Z" },
{ url = "https://files.pythonhosted.org/packages/14/a8/94c94bc48c68da4374870a851eea03fc5a45eb041182ad4c5ed9acfc05a4/ormsgpack-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d4bd8589b78a11026d47f4edf13c1ceab9088bb12451f34396afe6497db28a27", size = 112314, upload-time = "2025-10-08T17:28:36.259Z" },
{ url = "https://files.pythonhosted.org/packages/19/d0/aa4cf04f04e4cc180ce7a8d8ddb5a7f3af883329cbc59645d94d3ba157a5/ormsgpack-1.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:e5e746a1223e70f111d4001dab9585ac8639eee8979ca0c8db37f646bf2961da", size = 106072, upload-time = "2025-10-08T17:28:37.518Z" },
{ url = "https://files.pythonhosted.org/packages/8b/35/e34722edb701d053cf2240f55974f17b7dbfd11fdef72bd2f1835bcebf26/ormsgpack-1.11.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e7b36ab7b45cb95217ae1f05f1318b14a3e5ef73cb00804c0f06233f81a14e8", size = 368502, upload-time = "2025-10-08T17:28:38.547Z" },
{ url = "https://files.pythonhosted.org/packages/2f/6a/c2fc369a79d6aba2aa28c8763856c95337ac7fcc0b2742185cd19397212a/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43402d67e03a9a35cc147c8c03f0c377cad016624479e1ee5b879b8425551484", size = 195344, upload-time = "2025-10-08T17:28:39.554Z" },
{ url = "https://files.pythonhosted.org/packages/8b/6a/0f8e24b7489885534c1a93bdba7c7c434b9b8638713a68098867db9f254c/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:64fd992f932764d6306b70ddc755c1bc3405c4c6a69f77a36acf7af1c8f5ada4", size = 206045, upload-time = "2025-10-08T17:28:40.561Z" },
{ url = "https://files.pythonhosted.org/packages/99/71/8b460ba264f3c6f82ef5b1920335720094e2bd943057964ce5287d6df83a/ormsgpack-1.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0362fb7fe4a29c046c8ea799303079a09372653a1ce5a5a588f3bbb8088368d0", size = 207641, upload-time = "2025-10-08T17:28:41.736Z" },
{ url = "https://files.pythonhosted.org/packages/50/cf/f369446abaf65972424ed2651f2df2b7b5c3b735c93fc7fa6cfb81e34419/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:de2f7a65a9d178ed57be49eba3d0fc9b833c32beaa19dbd4ba56014d3c20b152", size = 377211, upload-time = "2025-10-08T17:28:43.12Z" },
{ url = "https://files.pythonhosted.org/packages/2f/3f/948bb0047ce0f37c2efc3b9bb2bcfdccc61c63e0b9ce8088d4903ba39dcf/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f38cfae95461466055af966fc922d06db4e1654966385cda2828653096db34da", size = 470973, upload-time = "2025-10-08T17:28:44.465Z" },
{ url = "https://files.pythonhosted.org/packages/31/a4/92a8114d1d017c14aaa403445060f345df9130ca532d538094f38e535988/ormsgpack-1.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c88396189d238f183cea7831b07a305ab5c90d6d29b53288ae11200bd956357b", size = 381161, upload-time = "2025-10-08T17:28:46.063Z" },
{ url = "https://files.pythonhosted.org/packages/d0/64/5b76447da654798bfcfdfd64ea29447ff2b7f33fe19d0e911a83ad5107fc/ormsgpack-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:5403d1a945dd7c81044cebeca3f00a28a0f4248b33242a5d2d82111628043725", size = 112321, upload-time = "2025-10-08T17:28:47.393Z" },
{ url = "https://files.pythonhosted.org/packages/46/5e/89900d06db9ab81e7ec1fd56a07c62dfbdcda398c435718f4252e1dc52a0/ormsgpack-1.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c57357b8d43b49722b876edf317bdad9e6d52071b523fdd7394c30cd1c67d5a0", size = 106084, upload-time = "2025-10-08T17:28:48.305Z" },
{ url = "https://files.pythonhosted.org/packages/4c/0b/c659e8657085c8c13f6a0224789f422620cef506e26573b5434defe68483/ormsgpack-1.11.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d390907d90fd0c908211592c485054d7a80990697ef4dff4e436ac18e1aab98a", size = 368497, upload-time = "2025-10-08T17:28:49.297Z" },
{ url = "https://files.pythonhosted.org/packages/1b/0e/451e5848c7ed56bd287e8a2b5cb5926e54466f60936e05aec6cb299f9143/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6153c2e92e789509098e04c9aa116b16673bd88ec78fbe0031deeb34ab642d10", size = 195385, upload-time = "2025-10-08T17:28:50.314Z" },
{ url = "https://files.pythonhosted.org/packages/4c/28/90f78cbbe494959f2439c2ec571f08cd3464c05a6a380b0d621c622122a9/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2b2c2a065a94d742212b2018e1fecd8f8d72f3c50b53a97d1f407418093446d", size = 206114, upload-time = "2025-10-08T17:28:51.336Z" },
{ url = "https://files.pythonhosted.org/packages/fb/db/34163f4c0923bea32dafe42cd878dcc66795a3e85669bc4b01c1e2b92a7b/ormsgpack-1.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:110e65b5340f3d7ef8b0009deae3c6b169437e6b43ad5a57fd1748085d29d2ac", size = 207679, upload-time = "2025-10-08T17:28:53.627Z" },
{ url = "https://files.pythonhosted.org/packages/b6/14/04ee741249b16f380a9b4a0cc19d4134d0b7c74bab27a2117da09e525eb9/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c27e186fca96ab34662723e65b420919910acbbc50fc8e1a44e08f26268cb0e0", size = 377237, upload-time = "2025-10-08T17:28:56.12Z" },
{ url = "https://files.pythonhosted.org/packages/89/ff/53e588a6aaa833237471caec679582c2950f0e7e1a8ba28c1511b465c1f4/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d56b1f877c13d499052d37a3db2378a97d5e1588d264f5040b3412aee23d742c", size = 471021, upload-time = "2025-10-08T17:28:57.299Z" },
{ url = "https://files.pythonhosted.org/packages/a6/f9/f20a6d9ef2be04da3aad05e8f5699957e9a30c6d5c043a10a296afa7e890/ormsgpack-1.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c88e28cd567c0a3269f624b4ade28142d5e502c8e826115093c572007af5be0a", size = 381205, upload-time = "2025-10-08T17:28:58.872Z" },
{ url = "https://files.pythonhosted.org/packages/f8/64/96c07d084b479ac8b7821a77ffc8d3f29d8b5c95ebfdf8db1c03dff02762/ormsgpack-1.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:8811160573dc0a65f62f7e0792c4ca6b7108dfa50771edb93f9b84e2d45a08ae", size = 112374, upload-time = "2025-10-08T17:29:00Z" },
{ url = "https://files.pythonhosted.org/packages/88/a5/5dcc18b818d50213a3cadfe336bb6163a102677d9ce87f3d2f1a1bee0f8c/ormsgpack-1.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:23e30a8d3c17484cf74e75e6134322255bd08bc2b5b295cc9c442f4bae5f3c2d", size = 106056, upload-time = "2025-10-08T17:29:01.29Z" },
{ url = "https://files.pythonhosted.org/packages/19/2b/776d1b411d2be50f77a6e6e94a25825cca55dcacfe7415fd691a144db71b/ormsgpack-1.11.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2905816502adfaf8386a01dd85f936cd378d243f4f5ee2ff46f67f6298dc90d5", size = 368661, upload-time = "2025-10-08T17:29:02.382Z" },
{ url = "https://files.pythonhosted.org/packages/a9/0c/81a19e6115b15764db3d241788f9fac093122878aaabf872cc545b0c4650/ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c04402fb9a0a9b9f18fbafd6d5f8398ee99b3ec619fb63952d3a954bc9d47daa", size = 195539, upload-time = "2025-10-08T17:29:03.472Z" },
{ url = "https://files.pythonhosted.org/packages/97/86/e5b50247a61caec5718122feb2719ea9d451d30ac0516c288c1dbc6408e8/ormsgpack-1.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a025ec07ac52056ecfd9e57b5cbc6fff163f62cb9805012b56cda599157f8ef2", size = 207718, upload-time = "2025-10-08T17:29:04.545Z" },
{ url = "https://files.pythonhosted.org/packages/4d/0c/8f45fcd22c95190b05d4fba71375d8f783a9e3b0b6aaf476812b693e3868/ormsgpack-1.12.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e08904c232358b94a682ccfbb680bc47d3fd5c424bb7dccb65974dd20c95e8e1", size = 369156, upload-time = "2025-11-04T18:29:17.629Z" },
{ url = "https://files.pythonhosted.org/packages/d2/f8/c7adc093d4ceb05e38786906815f868210f808326c27f8124b4d9466a26b/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9ed7a4b0037d69c8ba7e670e03ee65ae8d5c5114a409e73c5770d7fb5e4b895", size = 195743, upload-time = "2025-11-04T18:29:18.964Z" },
{ url = "https://files.pythonhosted.org/packages/fe/b8/bf002648fa6c150ed6157837b00303edafb35f65c352429463f83214de18/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db2928525b684f3f2af0367aef7ae8d20cde37fc5349c700017129d493a755aa", size = 206472, upload-time = "2025-11-04T18:29:19.951Z" },
{ url = "https://files.pythonhosted.org/packages/23/d6/1f445947c95a931bb189b7864a3f9dcbeebe7dcbc1b3c7387427b3779228/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45f911d9c5b23d11e49ff03fc8f9566745a2b1a7d9033733a1c0a2fa9301cd60", size = 207959, upload-time = "2025-11-04T18:29:21.282Z" },
{ url = "https://files.pythonhosted.org/packages/5c/9c/dd8ccd7553a5c1d0b4b69a0541611983a2f9bc2e8c5708a4bfffc0100468/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:98c54ae6fd682b2aceb264505af9b2255f3df9d84e6e4369bc44d2110f1f311d", size = 377659, upload-time = "2025-11-04T18:29:22.561Z" },
{ url = "https://files.pythonhosted.org/packages/3d/08/3282d8f6330e742d4cdbcfbe2c100b403ae5526ae82a1c1b6a11b7967e37/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:857ab987c3502de08258cc4baf0e87267cb2c80931601084e13df3c355b1ab9d", size = 471391, upload-time = "2025-11-04T18:29:23.663Z" },
{ url = "https://files.pythonhosted.org/packages/18/d4/94a2fbfd4837754bda7a099b6a23b9d40aba9e76e1af8b8fb8133612eb54/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27579d45dc502ee736238e1024559cb0a01aa72a3b68827448b8edf6a2dcdc9c", size = 381501, upload-time = "2025-11-04T18:29:24.771Z" },
{ url = "https://files.pythonhosted.org/packages/d8/c6/1a9fa122cb5deb10b067bbaa43165b12291a914cc0ce364988ff17bbf405/ormsgpack-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c78379d054760875540cf2e81f28da1bb78d09fda3eabdbeb6c53b3e297158cb", size = 112715, upload-time = "2025-11-04T18:29:26.016Z" },
{ url = "https://files.pythonhosted.org/packages/1a/ba/3cae83cf36420c1c8dd294f16c852c03313aafe2439a165c4c6ac611b1d0/ormsgpack-1.12.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c40d86d77391b18dd34de5295e3de2b8ad818bcab9c9def4121c8ec5c9714ae4", size = 369159, upload-time = "2025-11-04T18:29:27.057Z" },
{ url = "https://files.pythonhosted.org/packages/97/d4/5e176309e01a8b9098d80201aac1eb7db9336c3b5b4fa6254a2bbb0d0fa0/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:777b7fab364dc0f200bb382a98a385c8222ffa6a2333d627d763797326202c86", size = 195744, upload-time = "2025-11-04T18:29:28.069Z" },
{ url = "https://files.pythonhosted.org/packages/4f/83/6d80c8c5571639c000a39f38f77752dfaf9d9e552d775331e8d280f66a4e/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b5089ad9dd5b3d3013b245a55e4abaea2f8ad70f4a78e1b002127b02340004", size = 206474, upload-time = "2025-11-04T18:29:29.034Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e6/940311e48dc0cfc3e212bd7007a21ed0825158638057687d804f2c5c2cca/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaf0c87cace7bc08fbf68c5cc66605b593df6427e9f4de235b2da358787e008", size = 207959, upload-time = "2025-11-04T18:29:30.315Z" },
{ url = "https://files.pythonhosted.org/packages/1a/e3/fbe94b0a311815343b86a95a0627e4901b11ff6fd522679ca29a2a88c99b/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f62d476fe28bc5675d9aff30341bfa9f41d7de332c5b63fbbe9aaf6bb7ec74d4", size = 377666, upload-time = "2025-11-04T18:29:31.38Z" },
{ url = "https://files.pythonhosted.org/packages/a3/3b/229cfa28076798ffb619aaa854b842de3f2ed5ea4e6509bf34d14c038c4d/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ded7810095b887e28434f32f5a345d354e88cf851bab3c5435aeb86a718618d2", size = 471394, upload-time = "2025-11-04T18:29:32.521Z" },
{ url = "https://files.pythonhosted.org/packages/6b/bd/4eae4ab35586e4175c07acb5f98aec83aa9d8987f71ea0443aa900191bdf/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f72a1dea0c4ae7c4101dcfbe8133f274a9d769d0b87fe5188db4fab07ffabaee", size = 381506, upload-time = "2025-11-04T18:29:33.533Z" },
{ url = "https://files.pythonhosted.org/packages/dd/51/f9d56d6d015cbfa1ce9a4358ca30a41744644f0cf606e060d7203efe5af8/ormsgpack-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f479bfef847255d7d0b12c7a198f6a21490155da2da3062e082ba370893d4a1", size = 112707, upload-time = "2025-11-04T18:29:34.898Z" },
{ url = "https://files.pythonhosted.org/packages/f4/07/bb189ef7072979f2f96e8716e952172efdce9c54930aa0814bec73aee19b/ormsgpack-1.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:3583ca410e4502144b2594170542e4bbef7b15643fd1208703ae820f11029036", size = 106533, upload-time = "2025-11-04T18:29:36.112Z" },
{ url = "https://files.pythonhosted.org/packages/a2/f2/c1036b2775fcc0cfa5fd618c53bcd3b862ee07298fb627f03af4c7982f84/ormsgpack-1.12.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e0c1e08b64d99076fee155276097489b82cc56e8d5951c03c721a65a32f44494", size = 369538, upload-time = "2025-11-04T18:29:37.125Z" },
{ url = "https://files.pythonhosted.org/packages/d9/ca/526c4ae02f3cb34621af91bf8282a10d666757c2e0c6ff391ff5d403d607/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd43bcb299131690b8e0677af172020b2ada8e625169034b42ac0c13adf84aa", size = 195872, upload-time = "2025-11-04T18:29:38.34Z" },
{ url = "https://files.pythonhosted.org/packages/7f/0f/83bb7968e9715f6a85be53d041b1e6324a05428f56b8b980dac866886871/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0149d595341e22ead340bf281b2995c4cc7dc8d522a6b5f575fe17aa407604", size = 206469, upload-time = "2025-11-04T18:29:39.749Z" },
{ url = "https://files.pythonhosted.org/packages/02/e3/9e93ca1065f2d4af035804a842b1ff3025bab580c7918239bb225cd1fee2/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19a1b27d169deb553c80fd10b589fc2be1fc14cee779fae79fcaf40db04de2b", size = 208273, upload-time = "2025-11-04T18:29:40.769Z" },
{ url = "https://files.pythonhosted.org/packages/b3/d8/6d6ef901b3a8b8f3ab8836b135a56eb7f66c559003e251d9530bedb12627/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f28896942d655064940dfe06118b7ce1e3468d051483148bf02c99ec157483a", size = 377839, upload-time = "2025-11-04T18:29:42.092Z" },
{ url = "https://files.pythonhosted.org/packages/4c/72/fcb704bfa4c2c3a37b647d597cc45a13cffc9d50baac635a9ad620731d29/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9396efcfa48b4abbc06e44c5dbc3c4574a8381a80cb4cd01eea15d28b38c554e", size = 471446, upload-time = "2025-11-04T18:29:43.133Z" },
{ url = "https://files.pythonhosted.org/packages/84/f8/402e4e3eb997c2ee534c99bec4b5bb359c2a1f9edadf043e254a71e11378/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96586ed537a5fb386a162c4f9f7d8e6f76e07b38a990d50c73f11131e00ff040", size = 381783, upload-time = "2025-11-04T18:29:44.466Z" },
{ url = "https://files.pythonhosted.org/packages/f0/8d/5897b700360bc00911b70ae5ef1134ee7abf5baa81a92a4be005917d3dfd/ormsgpack-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e70387112fb3870e4844de090014212cdcf1342f5022047aecca01ec7de05d7a", size = 112943, upload-time = "2025-11-04T18:29:45.468Z" },
{ url = "https://files.pythonhosted.org/packages/5b/44/1e73649f79bb96d6cf9e5bcbac68b6216d238bba80af351c4c0cbcf7ee15/ormsgpack-1.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:d71290a23de5d4829610c42665d816c661ecad8979883f3f06b2e3ab9639962e", size = 106688, upload-time = "2025-11-04T18:29:46.411Z" },
{ url = "https://files.pythonhosted.org/packages/2e/e8/35f11ce9313111488b26b3035e4cbe55caa27909c0b6c8b5b5cd59f9661e/ormsgpack-1.12.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:766f2f3b512d85cd375b26a8b1329b99843560b50b93d3880718e634ad4a5de5", size = 369574, upload-time = "2025-11-04T18:29:47.431Z" },
{ url = "https://files.pythonhosted.org/packages/61/b0/77461587f412d4e598d3687bafe23455ed0f26269f44be20252eddaa624e/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84b285b1f3f185aad7da45641b873b30acfd13084cf829cf668c4c6480a81583", size = 195893, upload-time = "2025-11-04T18:29:48.735Z" },
{ url = "https://files.pythonhosted.org/packages/c6/67/e197ceb04c3b550589e5407fc9fdae10f4e2e2eba5fdac921a269e02e974/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e23604fc79fe110292cb365f4c8232e64e63a34f470538be320feae3921f271b", size = 206503, upload-time = "2025-11-04T18:29:49.99Z" },
{ url = "https://files.pythonhosted.org/packages/0b/b1/7fa8ba82a25cef678983c7976f85edeef5014f5c26495f338258e6a3cf1c/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc32b156c113a0fae2975051417d8d9a7a5247c34b2d7239410c46b75ce9348a", size = 208257, upload-time = "2025-11-04T18:29:51.007Z" },
{ url = "https://files.pythonhosted.org/packages/ce/b1/759e999390000d2589e6d0797f7265e6ec28378547075d28d3736248ab63/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:94ac500dd10c20fa8b8a23bc55606250bfe711bf9716828d9f3d44dfd1f25668", size = 377852, upload-time = "2025-11-04T18:29:52.103Z" },
{ url = "https://files.pythonhosted.org/packages/51/e7/0af737c94272494d9d84a3c29cc42c973ef7fd2342917020906596db863c/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c5201ff7ec24f721f813a182885a17064cffdbe46b2412685a52e6374a872c8f", size = 471456, upload-time = "2025-11-04T18:29:53.336Z" },
{ url = "https://files.pythonhosted.org/packages/f4/ba/c81f0aa4f19fbf457213395945b672e6fde3ce777e3587456e7f0fca2147/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a9740bb3839c9368aacae1cbcfc474ee6976458f41cc135372b7255d5206c953", size = 381813, upload-time = "2025-11-04T18:29:54.394Z" },
{ url = "https://files.pythonhosted.org/packages/ce/15/429c72d64323503fd42cc4ca8398930ded8aa8b3470df8a86b3bbae7a35c/ormsgpack-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ed37f29772432048b58174e920a1d4c4cde0404a5d448d3d8bbcc95d86a6918", size = 112949, upload-time = "2025-11-04T18:29:55.371Z" },
{ url = "https://files.pythonhosted.org/packages/55/b9/e72c451a40f8c57bfc229e0b8e536ecea7203c8f0a839676df2ffb605c62/ormsgpack-1.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:b03994bbec5d6d42e03d6604e327863f885bde67aa61e06107ce1fa5bdd3e71d", size = 106689, upload-time = "2025-11-04T18:29:56.262Z" },
{ url = "https://files.pythonhosted.org/packages/13/16/13eab1a75da531b359105fdee90dda0b6bd1ca0a09880250cf91d8bdfdea/ormsgpack-1.12.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0f3981ba3cba80656012090337e548e597799e14b41e3d0b595ab5ab05a23d7f", size = 369620, upload-time = "2025-11-04T18:29:57.255Z" },
{ url = "https://files.pythonhosted.org/packages/a0/c1/cbcc38b7af4ce58d8893e56d3595c0c8dcd117093bf048f889cf351bdba0/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:901f6f55184d6776dbd5183cbce14caf05bf7f467eef52faf9b094686980bf71", size = 195925, upload-time = "2025-11-04T18:29:58.34Z" },
{ url = "https://files.pythonhosted.org/packages/5c/59/4fa4dc0681490e12b75333440a1c0fd9741b0ebff272b1db4a29d35c2021/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13b15412571422b711b40f45e3fe6d993ea3314b5e97d1a853fe99226c5effc", size = 206594, upload-time = "2025-11-04T18:29:59.329Z" },
{ url = "https://files.pythonhosted.org/packages/39/67/249770896bc32bb91b22c30256961f935d0915cbcf6e289a7fc961d9b14c/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91fa8a452553a62e5fb3fbab471e7faf7b3bec3c87a2f355ebf3d7aab290fe4f", size = 208307, upload-time = "2025-11-04T18:30:00.377Z" },
{ url = "https://files.pythonhosted.org/packages/07/0a/e041a248cd72f2f4c07e155913e0a3ede4c86cf21a40ae6cd79f135f2847/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74ec101f69624695eec4ce7c953192d97748254abe78fb01b591f06d529e1952", size = 377844, upload-time = "2025-11-04T18:30:01.389Z" },
{ url = "https://files.pythonhosted.org/packages/d8/71/6f7773e4ffda73a358ce4bba69b3e8bee9d40a7a06315e4c1cd7a3ea9d02/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9bbf7896580848326c1f9bd7531f264e561f98db7e08e15aa75963d83832c717", size = 471572, upload-time = "2025-11-04T18:30:02.486Z" },
{ url = "https://files.pythonhosted.org/packages/65/29/af6769a4289c07acc71e7bda1d64fb31800563147d73142686e185e82348/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7567917da613b8f8d591c1674e411fd3404bea41ef2b9a0e0a1e049c0f9406d7", size = 381842, upload-time = "2025-11-04T18:30:03.799Z" },
{ url = "https://files.pythonhosted.org/packages/0b/dd/0a86195ee7a1a96c088aefc8504385e881cf56f4563ed81bafe21cbf1fb0/ormsgpack-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e418256c5d8622b8bc92861936f7c6a0131355e7bcad88a42102ae8227f8a1c", size = 113008, upload-time = "2025-11-04T18:30:04.777Z" },
{ url = "https://files.pythonhosted.org/packages/4c/57/fafc79e32f3087f6f26f509d80b8167516326bfea38d30502627c01617e0/ormsgpack-1.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:433ace29aa02713554f714c62a4e4dcad0c9e32674ba4f66742c91a4c3b1b969", size = 106648, upload-time = "2025-11-04T18:30:05.708Z" },
{ url = "https://files.pythonhosted.org/packages/b3/cf/5d58d9b132128d2fe5d586355dde76af386554abef00d608f66b913bff1f/ormsgpack-1.12.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e57164be4ca34b64e210ec515059193280ac84df4d6f31a6fcbfb2fc8436de55", size = 369803, upload-time = "2025-11-04T18:30:06.728Z" },
{ url = "https://files.pythonhosted.org/packages/67/42/968a2da361eaff2e4cbb17c82c7599787babf16684110ad70409646cc1e4/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:904f96289deaa92fc6440b122edc27c5bdc28234edd63717f6d853d88c823a83", size = 195991, upload-time = "2025-11-04T18:30:07.713Z" },
{ url = "https://files.pythonhosted.org/packages/03/f0/9696c6c6cf8ad35170f0be8d0ef3523cc258083535f6c8071cb8235ebb8b/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b291d086e524a1062d57d1b7b5a8bcaaf29caebf0212fec12fd86240bd33633", size = 208316, upload-time = "2025-11-04T18:30:08.663Z" },
]
[[package]]
@@ -717,51 +746,64 @@ wheels = [
[[package]]
name = "pandas"
version = "2.3.2"
version = "2.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "python-dateutil" },
{ name = "pytz" },
{ name = "tzdata" },
]
sdist = { url = "https://files.pythonhosted.org/packages/79/8e/0e90233ac205ad182bd6b422532695d2b9414944a280488105d598c70023/pandas-2.3.2.tar.gz", hash = "sha256:ab7b58f8f82706890924ccdfb5f48002b83d2b5a3845976a9fb705d36c34dcdb", size = 4488684, upload-time = "2025-08-21T10:28:29.257Z" }
sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/16/a8eeb70aad84ccbf14076793f90e0031eded63c1899aeae9fdfbf37881f4/pandas-2.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52bc29a946304c360561974c6542d1dd628ddafa69134a7131fdfd6a5d7a1a35", size = 11539648, upload-time = "2025-08-21T10:26:36.236Z" },
{ url = "https://files.pythonhosted.org/packages/47/f1/c5bdaea13bf3708554d93e948b7ea74121ce6e0d59537ca4c4f77731072b/pandas-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:220cc5c35ffaa764dd5bb17cf42df283b5cb7fdf49e10a7b053a06c9cb48ee2b", size = 10786923, upload-time = "2025-08-21T10:26:40.518Z" },
{ url = "https://files.pythonhosted.org/packages/bb/10/811fa01476d29ffed692e735825516ad0e56d925961819e6126b4ba32147/pandas-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c05e15111221384019897df20c6fe893b2f697d03c811ee67ec9e0bb5a3424", size = 11726241, upload-time = "2025-08-21T10:26:43.175Z" },
{ url = "https://files.pythonhosted.org/packages/c4/6a/40b043b06e08df1ea1b6d20f0e0c2f2c4ec8c4f07d1c92948273d943a50b/pandas-2.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc03acc273c5515ab69f898df99d9d4f12c4d70dbfc24c3acc6203751d0804cf", size = 12349533, upload-time = "2025-08-21T10:26:46.611Z" },
{ url = "https://files.pythonhosted.org/packages/e2/ea/2e081a2302e41a9bca7056659fdd2b85ef94923723e41665b42d65afd347/pandas-2.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d25c20a03e8870f6339bcf67281b946bd20b86f1a544ebbebb87e66a8d642cba", size = 13202407, upload-time = "2025-08-21T10:26:49.068Z" },
{ url = "https://files.pythonhosted.org/packages/f4/12/7ff9f6a79e2ee8869dcf70741ef998b97ea20050fe25f83dc759764c1e32/pandas-2.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21bb612d148bb5860b7eb2c10faacf1a810799245afd342cf297d7551513fbb6", size = 13837212, upload-time = "2025-08-21T10:26:51.832Z" },
{ url = "https://files.pythonhosted.org/packages/d8/df/5ab92fcd76455a632b3db34a746e1074d432c0cdbbd28d7cd1daba46a75d/pandas-2.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:b62d586eb25cb8cb70a5746a378fc3194cb7f11ea77170d59f889f5dfe3cec7a", size = 11338099, upload-time = "2025-08-21T10:26:54.382Z" },
{ url = "https://files.pythonhosted.org/packages/7a/59/f3e010879f118c2d400902d2d871c2226cef29b08c09fb8dc41111730400/pandas-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1333e9c299adcbb68ee89a9bb568fc3f20f9cbb419f1dd5225071e6cddb2a743", size = 11563308, upload-time = "2025-08-21T10:26:56.656Z" },
{ url = "https://files.pythonhosted.org/packages/38/18/48f10f1cc5c397af59571d638d211f494dba481f449c19adbd282aa8f4ca/pandas-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76972bcbd7de8e91ad5f0ca884a9f2c477a2125354af624e022c49e5bd0dfff4", size = 10820319, upload-time = "2025-08-21T10:26:59.162Z" },
{ url = "https://files.pythonhosted.org/packages/95/3b/1e9b69632898b048e223834cd9702052bcf06b15e1ae716eda3196fb972e/pandas-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98bdd7c456a05eef7cd21fd6b29e3ca243591fe531c62be94a2cc987efb5ac2", size = 11790097, upload-time = "2025-08-21T10:27:02.204Z" },
{ url = "https://files.pythonhosted.org/packages/8b/ef/0e2ffb30b1f7fbc9a588bd01e3c14a0d96854d09a887e15e30cc19961227/pandas-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d81573b3f7db40d020983f78721e9bfc425f411e616ef019a10ebf597aedb2e", size = 12397958, upload-time = "2025-08-21T10:27:05.409Z" },
{ url = "https://files.pythonhosted.org/packages/23/82/e6b85f0d92e9afb0e7f705a51d1399b79c7380c19687bfbf3d2837743249/pandas-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e190b738675a73b581736cc8ec71ae113d6c3768d0bd18bffa5b9a0927b0b6ea", size = 13225600, upload-time = "2025-08-21T10:27:07.791Z" },
{ url = "https://files.pythonhosted.org/packages/e8/f1/f682015893d9ed51611948bd83683670842286a8edd4f68c2c1c3b231eef/pandas-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c253828cb08f47488d60f43c5fc95114c771bbfff085da54bfc79cb4f9e3a372", size = 13879433, upload-time = "2025-08-21T10:27:10.347Z" },
{ url = "https://files.pythonhosted.org/packages/a7/e7/ae86261695b6c8a36d6a4c8d5f9b9ede8248510d689a2f379a18354b37d7/pandas-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:9467697b8083f9667b212633ad6aa4ab32436dcbaf4cd57325debb0ddef2012f", size = 11336557, upload-time = "2025-08-21T10:27:12.983Z" },
{ url = "https://files.pythonhosted.org/packages/ec/db/614c20fb7a85a14828edd23f1c02db58a30abf3ce76f38806155d160313c/pandas-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fbb977f802156e7a3f829e9d1d5398f6192375a3e2d1a9ee0803e35fe70a2b9", size = 11587652, upload-time = "2025-08-21T10:27:15.888Z" },
{ url = "https://files.pythonhosted.org/packages/99/b0/756e52f6582cade5e746f19bad0517ff27ba9c73404607c0306585c201b3/pandas-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b9b52693123dd234b7c985c68b709b0b009f4521000d0525f2b95c22f15944b", size = 10717686, upload-time = "2025-08-21T10:27:18.486Z" },
{ url = "https://files.pythonhosted.org/packages/37/4c/dd5ccc1e357abfeee8353123282de17997f90ff67855f86154e5a13b81e5/pandas-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bd281310d4f412733f319a5bc552f86d62cddc5f51d2e392c8787335c994175", size = 11278722, upload-time = "2025-08-21T10:27:21.149Z" },
{ url = "https://files.pythonhosted.org/packages/d3/a4/f7edcfa47e0a88cda0be8b068a5bae710bf264f867edfdf7b71584ace362/pandas-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d31a6b4354e3b9b8a2c848af75d31da390657e3ac6f30c05c82068b9ed79b9", size = 11987803, upload-time = "2025-08-21T10:27:23.767Z" },
{ url = "https://files.pythonhosted.org/packages/f6/61/1bce4129f93ab66f1c68b7ed1c12bac6a70b1b56c5dab359c6bbcd480b52/pandas-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df4df0b9d02bb873a106971bb85d448378ef14b86ba96f035f50bbd3688456b4", size = 12766345, upload-time = "2025-08-21T10:27:26.6Z" },
{ url = "https://files.pythonhosted.org/packages/8e/46/80d53de70fee835531da3a1dae827a1e76e77a43ad22a8cd0f8142b61587/pandas-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:213a5adf93d020b74327cb2c1b842884dbdd37f895f42dcc2f09d451d949f811", size = 13439314, upload-time = "2025-08-21T10:27:29.213Z" },
{ url = "https://files.pythonhosted.org/packages/28/30/8114832daff7489f179971dbc1d854109b7f4365a546e3ea75b6516cea95/pandas-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c13b81a9347eb8c7548f53fd9a4f08d4dfe996836543f805c987bafa03317ae", size = 10983326, upload-time = "2025-08-21T10:27:31.901Z" },
{ url = "https://files.pythonhosted.org/packages/27/64/a2f7bf678af502e16b472527735d168b22b7824e45a4d7e96a4fbb634b59/pandas-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0c6ecbac99a354a051ef21c5307601093cb9e0f4b1855984a084bfec9302699e", size = 11531061, upload-time = "2025-08-21T10:27:34.647Z" },
{ url = "https://files.pythonhosted.org/packages/54/4c/c3d21b2b7769ef2f4c2b9299fcadd601efa6729f1357a8dbce8dd949ed70/pandas-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6f048aa0fd080d6a06cc7e7537c09b53be6642d330ac6f54a600c3ace857ee9", size = 10668666, upload-time = "2025-08-21T10:27:37.203Z" },
{ url = "https://files.pythonhosted.org/packages/50/e2/f775ba76ecfb3424d7f5862620841cf0edb592e9abd2d2a5387d305fe7a8/pandas-2.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0064187b80a5be6f2f9c9d6bdde29372468751dfa89f4211a3c5871854cfbf7a", size = 11332835, upload-time = "2025-08-21T10:27:40.188Z" },
{ url = "https://files.pythonhosted.org/packages/8f/52/0634adaace9be2d8cac9ef78f05c47f3a675882e068438b9d7ec7ef0c13f/pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b", size = 12057211, upload-time = "2025-08-21T10:27:43.117Z" },
{ url = "https://files.pythonhosted.org/packages/0b/9d/2df913f14b2deb9c748975fdb2491da1a78773debb25abbc7cbc67c6b549/pandas-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:114c2fe4f4328cf98ce5716d1532f3ab79c5919f95a9cfee81d9140064a2e4d6", size = 12749277, upload-time = "2025-08-21T10:27:45.474Z" },
{ url = "https://files.pythonhosted.org/packages/87/af/da1a2417026bd14d98c236dba88e39837182459d29dcfcea510b2ac9e8a1/pandas-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:48fa91c4dfb3b2b9bfdb5c24cd3567575f4e13f9636810462ffed8925352be5a", size = 13415256, upload-time = "2025-08-21T10:27:49.885Z" },
{ url = "https://files.pythonhosted.org/packages/22/3c/f2af1ce8840ef648584a6156489636b5692c162771918aa95707c165ad2b/pandas-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:12d039facec710f7ba305786837d0225a3444af7bbd9c15c32ca2d40d157ed8b", size = 10982579, upload-time = "2025-08-21T10:28:08.435Z" },
{ url = "https://files.pythonhosted.org/packages/f3/98/8df69c4097a6719e357dc249bf437b8efbde808038268e584421696cbddf/pandas-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c624b615ce97864eb588779ed4046186f967374185c047070545253a52ab2d57", size = 12028163, upload-time = "2025-08-21T10:27:52.232Z" },
{ url = "https://files.pythonhosted.org/packages/0e/23/f95cbcbea319f349e10ff90db488b905c6883f03cbabd34f6b03cbc3c044/pandas-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0cee69d583b9b128823d9514171cabb6861e09409af805b54459bd0c821a35c2", size = 11391860, upload-time = "2025-08-21T10:27:54.673Z" },
{ url = "https://files.pythonhosted.org/packages/ad/1b/6a984e98c4abee22058aa75bfb8eb90dce58cf8d7296f8bc56c14bc330b0/pandas-2.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2319656ed81124982900b4c37f0e0c58c015af9a7bbc62342ba5ad07ace82ba9", size = 11309830, upload-time = "2025-08-21T10:27:56.957Z" },
{ url = "https://files.pythonhosted.org/packages/15/d5/f0486090eb18dd8710bf60afeaf638ba6817047c0c8ae5c6a25598665609/pandas-2.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37205ad6f00d52f16b6d09f406434ba928c1a1966e2771006a9033c736d30d2", size = 11883216, upload-time = "2025-08-21T10:27:59.302Z" },
{ url = "https://files.pythonhosted.org/packages/10/86/692050c119696da19e20245bbd650d8dfca6ceb577da027c3a73c62a047e/pandas-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:837248b4fc3a9b83b9c6214699a13f069dc13510a6a6d7f9ba33145d2841a012", size = 12699743, upload-time = "2025-08-21T10:28:02.447Z" },
{ url = "https://files.pythonhosted.org/packages/cd/d7/612123674d7b17cf345aad0a10289b2a384bff404e0463a83c4a3a59d205/pandas-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d2c3554bd31b731cd6490d94a28f3abb8dd770634a9e06eb6d2911b9827db370", size = 13186141, upload-time = "2025-08-21T10:28:05.377Z" },
{ url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" },
{ url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" },
{ url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" },
{ url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" },
{ url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" },
{ url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" },
{ url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" },
{ url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" },
{ url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" },
{ url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" },
{ url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" },
{ url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" },
{ url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" },
{ url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" },
{ url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" },
{ url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" },
{ url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" },
{ url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" },
{ url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" },
{ url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" },
{ url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" },
{ url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" },
{ url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" },
{ url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" },
{ url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" },
{ url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" },
{ url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" },
{ url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" },
{ url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" },
{ url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" },
{ url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" },
{ url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" },
{ url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" },
{ url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" },
{ url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" },
{ url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" },
{ url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" },
{ url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" },
{ url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" },
{ url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" },
{ url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" },
{ url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" },
{ url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" },
{ url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" },
{ url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" },
{ url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" },
{ url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" },
]
[[package]]
@@ -770,7 +812,7 @@ version = "2.3.2.250926"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "types-pytz" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1b/3b/32be58a125db39d0b5f62cc93795f32b5bb2915bd5c4a46f0e35171985e2/pandas_stubs-2.3.2.250926.tar.gz", hash = "sha256:c64b9932760ceefb96a3222b953e6a251321a9832a28548be6506df473a66406", size = 102147, upload-time = "2025-09-26T19:50:39.522Z" }
@@ -798,7 +840,7 @@ wheels = [
[[package]]
name = "pydantic"
version = "2.12.2"
version = "2.12.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
@@ -806,9 +848,9 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8d/35/d319ed522433215526689bad428a94058b6dd12190ce7ddd78618ac14b28/pydantic-2.12.2.tar.gz", hash = "sha256:7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd", size = 816358, upload-time = "2025-10-14T15:02:21.842Z" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/1e/4f0a3233767010308f2fd6bd0814597e3f63f1dc98304a9112b8759df4ff/pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74", size = 819383, upload-time = "2025-10-17T15:04:21.222Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6c/98/468cb649f208a6f1279448e6e5247b37ae79cf5e4041186f1e2ef3d16345/pydantic-2.12.2-py3-none-any.whl", hash = "sha256:25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae", size = 460628, upload-time = "2025-10-14T15:02:19.623Z" },
{ url = "https://files.pythonhosted.org/packages/a1/6b/83661fa77dcefa195ad5f8cd9af3d1a7450fd57cc883ad04d65446ac2029/pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf", size = 462431, upload-time = "2025-10-17T15:04:19.346Z" },
]
[[package]]
@@ -1078,14 +1120,14 @@ wheels = [
[[package]]
name = "redis"
version = "6.4.0"
version = "7.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "async-timeout", marker = "python_full_version < '3.11.3'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0d/d6/e8b92798a5bd67d659d51a18170e91c16ac3b59738d91894651ee255ed49/redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010", size = 4647399, upload-time = "2025-08-07T08:10:11.441Z" }
sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322, upload-time = "2025-10-27T14:34:00.33Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/02/89e2ed7e85db6c93dfa9e8f691c5087df4e3551ab39081a4d7c6d1f90e05/redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f", size = 279847, upload-time = "2025-08-07T08:10:09.84Z" },
{ url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938, upload-time = "2025-10-27T14:33:58.553Z" },
]
[[package]]
@@ -1117,28 +1159,28 @@ wheels = [
[[package]]
name = "ruff"
version = "0.13.2"
version = "0.14.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/02/df/8d7d8c515d33adfc540e2edf6c6021ea1c5a58a678d8cfce9fae59aabcab/ruff-0.13.2.tar.gz", hash = "sha256:cb12fffd32fb16d32cef4ed16d8c7cdc27ed7c944eaa98d99d01ab7ab0b710ff", size = 5416417, upload-time = "2025-09-25T14:54:09.936Z" }
sdist = { url = "https://files.pythonhosted.org/packages/75/62/50b7727004dfe361104dfbf898c45a9a2fdfad8c72c04ae62900224d6ecf/ruff-0.14.3.tar.gz", hash = "sha256:4ff876d2ab2b161b6de0aa1f5bd714e8e9b4033dc122ee006925fbacc4f62153", size = 5558687, upload-time = "2025-10-31T00:26:26.878Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/84/5716a7fa4758e41bf70e603e13637c42cfb9dbf7ceb07180211b9bbf75ef/ruff-0.13.2-py3-none-linux_armv6l.whl", hash = "sha256:3796345842b55f033a78285e4f1641078f902020d8450cade03aad01bffd81c3", size = 12343254, upload-time = "2025-09-25T14:53:27.784Z" },
{ url = "https://files.pythonhosted.org/packages/9b/77/c7042582401bb9ac8eff25360e9335e901d7a1c0749a2b28ba4ecb239991/ruff-0.13.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ff7e4dda12e683e9709ac89e2dd436abf31a4d8a8fc3d89656231ed808e231d2", size = 13040891, upload-time = "2025-09-25T14:53:31.38Z" },
{ url = "https://files.pythonhosted.org/packages/c6/15/125a7f76eb295cb34d19c6778e3a82ace33730ad4e6f28d3427e134a02e0/ruff-0.13.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c75e9d2a2fafd1fdd895d0e7e24b44355984affdde1c412a6f6d3f6e16b22d46", size = 12243588, upload-time = "2025-09-25T14:53:33.543Z" },
{ url = "https://files.pythonhosted.org/packages/9e/eb/0093ae04a70f81f8be7fd7ed6456e926b65d238fc122311293d033fdf91e/ruff-0.13.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cceac74e7bbc53ed7d15d1042ffe7b6577bf294611ad90393bf9b2a0f0ec7cb6", size = 12491359, upload-time = "2025-09-25T14:53:35.892Z" },
{ url = "https://files.pythonhosted.org/packages/43/fe/72b525948a6956f07dad4a6f122336b6a05f2e3fd27471cea612349fedb9/ruff-0.13.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae3f469b5465ba6d9721383ae9d49310c19b452a161b57507764d7ef15f4b07", size = 12162486, upload-time = "2025-09-25T14:53:38.171Z" },
{ url = "https://files.pythonhosted.org/packages/6a/e3/0fac422bbbfb2ea838023e0d9fcf1f30183d83ab2482800e2cb892d02dfe/ruff-0.13.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f8f9e3cd6714358238cd6626b9d43026ed19c0c018376ac1ef3c3a04ffb42d8", size = 13871203, upload-time = "2025-09-25T14:53:41.943Z" },
{ url = "https://files.pythonhosted.org/packages/6b/82/b721c8e3ec5df6d83ba0e45dcf00892c4f98b325256c42c38ef136496cbf/ruff-0.13.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c6ed79584a8f6cbe2e5d7dbacf7cc1ee29cbdb5df1172e77fbdadc8bb85a1f89", size = 14929635, upload-time = "2025-09-25T14:53:43.953Z" },
{ url = "https://files.pythonhosted.org/packages/c4/a0/ad56faf6daa507b83079a1ad7a11694b87d61e6bf01c66bd82b466f21821/ruff-0.13.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aed130b2fde049cea2019f55deb939103123cdd191105f97a0599a3e753d61b0", size = 14338783, upload-time = "2025-09-25T14:53:46.205Z" },
{ url = "https://files.pythonhosted.org/packages/47/77/ad1d9156db8f99cd01ee7e29d74b34050e8075a8438e589121fcd25c4b08/ruff-0.13.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1887c230c2c9d65ed1b4e4cfe4d255577ea28b718ae226c348ae68df958191aa", size = 13355322, upload-time = "2025-09-25T14:53:48.164Z" },
{ url = "https://files.pythonhosted.org/packages/64/8b/e87cfca2be6f8b9f41f0bb12dc48c6455e2d66df46fe61bb441a226f1089/ruff-0.13.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bcb10276b69b3cfea3a102ca119ffe5c6ba3901e20e60cf9efb53fa417633c3", size = 13354427, upload-time = "2025-09-25T14:53:50.486Z" },
{ url = "https://files.pythonhosted.org/packages/7f/df/bf382f3fbead082a575edb860897287f42b1b3c694bafa16bc9904c11ed3/ruff-0.13.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:afa721017aa55a555b2ff7944816587f1cb813c2c0a882d158f59b832da1660d", size = 13537637, upload-time = "2025-09-25T14:53:52.887Z" },
{ url = "https://files.pythonhosted.org/packages/51/70/1fb7a7c8a6fc8bd15636288a46e209e81913b87988f26e1913d0851e54f4/ruff-0.13.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1dbc875cf3720c64b3990fef8939334e74cb0ca65b8dbc61d1f439201a38101b", size = 12340025, upload-time = "2025-09-25T14:53:54.88Z" },
{ url = "https://files.pythonhosted.org/packages/4c/27/1e5b3f1c23ca5dd4106d9d580e5c13d9acb70288bff614b3d7b638378cc9/ruff-0.13.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939a1b2a960e9742e9a347e5bbc9b3c3d2c716f86c6ae273d9cbd64f193f22", size = 12133449, upload-time = "2025-09-25T14:53:57.089Z" },
{ url = "https://files.pythonhosted.org/packages/2d/09/b92a5ccee289f11ab128df57d5911224197d8d55ef3bd2043534ff72ca54/ruff-0.13.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:50e2d52acb8de3804fc5f6e2fa3ae9bdc6812410a9e46837e673ad1f90a18736", size = 13051369, upload-time = "2025-09-25T14:53:59.124Z" },
{ url = "https://files.pythonhosted.org/packages/89/99/26c9d1c7d8150f45e346dc045cc49f23e961efceb4a70c47dea0960dea9a/ruff-0.13.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3196bc13ab2110c176b9a4ae5ff7ab676faaa1964b330a1383ba20e1e19645f2", size = 13523644, upload-time = "2025-09-25T14:54:01.622Z" },
{ url = "https://files.pythonhosted.org/packages/f7/00/e7f1501e81e8ec290e79527827af1d88f541d8d26151751b46108978dade/ruff-0.13.2-py3-none-win32.whl", hash = "sha256:7c2a0b7c1e87795fec3404a485096bcd790216c7c146a922d121d8b9c8f1aaac", size = 12245990, upload-time = "2025-09-25T14:54:03.647Z" },
{ url = "https://files.pythonhosted.org/packages/ee/bd/d9f33a73de84fafd0146c6fba4f497c4565fe8fa8b46874b8e438869abc2/ruff-0.13.2-py3-none-win_amd64.whl", hash = "sha256:17d95fb32218357c89355f6f6f9a804133e404fc1f65694372e02a557edf8585", size = 13324004, upload-time = "2025-09-25T14:54:06.05Z" },
{ url = "https://files.pythonhosted.org/packages/c3/12/28fa2f597a605884deb0f65c1b1ae05111051b2a7030f5d8a4ff7f4599ba/ruff-0.13.2-py3-none-win_arm64.whl", hash = "sha256:da711b14c530412c827219312b7d7fbb4877fb31150083add7e8c5336549cea7", size = 12484437, upload-time = "2025-09-25T14:54:08.022Z" },
{ url = "https://files.pythonhosted.org/packages/ce/8e/0c10ff1ea5d4360ab8bfca4cb2c9d979101a391f3e79d2616c9bf348cd26/ruff-0.14.3-py3-none-linux_armv6l.whl", hash = "sha256:876b21e6c824f519446715c1342b8e60f97f93264012de9d8d10314f8a79c371", size = 12535613, upload-time = "2025-10-31T00:25:44.302Z" },
{ url = "https://files.pythonhosted.org/packages/d3/c8/6724f4634c1daf52409fbf13fefda64aa9c8f81e44727a378b7b73dc590b/ruff-0.14.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6fd8c79b457bedd2abf2702b9b472147cd860ed7855c73a5247fa55c9117654", size = 12855812, upload-time = "2025-10-31T00:25:47.793Z" },
{ url = "https://files.pythonhosted.org/packages/de/03/db1bce591d55fd5f8a08bb02517fa0b5097b2ccabd4ea1ee29aa72b67d96/ruff-0.14.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:71ff6edca490c308f083156938c0c1a66907151263c4abdcb588602c6e696a14", size = 11944026, upload-time = "2025-10-31T00:25:49.657Z" },
{ url = "https://files.pythonhosted.org/packages/0b/75/4f8dbd48e03272715d12c87dc4fcaaf21b913f0affa5f12a4e9c6f8a0582/ruff-0.14.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:786ee3ce6139772ff9272aaf43296d975c0217ee1b97538a98171bf0d21f87ed", size = 12356818, upload-time = "2025-10-31T00:25:51.949Z" },
{ url = "https://files.pythonhosted.org/packages/ec/9b/506ec5b140c11d44a9a4f284ea7c14ebf6f8b01e6e8917734a3325bff787/ruff-0.14.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cd6291d0061811c52b8e392f946889916757610d45d004e41140d81fb6cd5ddc", size = 12336745, upload-time = "2025-10-31T00:25:54.248Z" },
{ url = "https://files.pythonhosted.org/packages/c7/e1/c560d254048c147f35e7f8131d30bc1f63a008ac61595cf3078a3e93533d/ruff-0.14.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a497ec0c3d2c88561b6d90f9c29f5ae68221ac00d471f306fa21fa4264ce5fcd", size = 13101684, upload-time = "2025-10-31T00:25:56.253Z" },
{ url = "https://files.pythonhosted.org/packages/a5/32/e310133f8af5cd11f8cc30f52522a3ebccc5ea5bff4b492f94faceaca7a8/ruff-0.14.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e231e1be58fc568950a04fbe6887c8e4b85310e7889727e2b81db205c45059eb", size = 14535000, upload-time = "2025-10-31T00:25:58.397Z" },
{ url = "https://files.pythonhosted.org/packages/a2/a1/7b0470a22158c6d8501eabc5e9b6043c99bede40fa1994cadf6b5c2a61c7/ruff-0.14.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:469e35872a09c0e45fecf48dd960bfbce056b5db2d5e6b50eca329b4f853ae20", size = 14156450, upload-time = "2025-10-31T00:26:00.889Z" },
{ url = "https://files.pythonhosted.org/packages/0a/96/24bfd9d1a7f532b560dcee1a87096332e461354d3882124219bcaff65c09/ruff-0.14.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d6bc90307c469cb9d28b7cfad90aaa600b10d67c6e22026869f585e1e8a2db0", size = 13568414, upload-time = "2025-10-31T00:26:03.291Z" },
{ url = "https://files.pythonhosted.org/packages/a7/e7/138b883f0dfe4ad5b76b58bf4ae675f4d2176ac2b24bdd81b4d966b28c61/ruff-0.14.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2f8a0bbcffcfd895df39c9a4ecd59bb80dca03dc43f7fb63e647ed176b741e", size = 13315293, upload-time = "2025-10-31T00:26:05.708Z" },
{ url = "https://files.pythonhosted.org/packages/33/f4/c09bb898be97b2eb18476b7c950df8815ef14cf956074177e9fbd40b7719/ruff-0.14.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:678fdd7c7d2d94851597c23ee6336d25f9930b460b55f8598e011b57c74fd8c5", size = 13539444, upload-time = "2025-10-31T00:26:08.09Z" },
{ url = "https://files.pythonhosted.org/packages/9c/aa/b30a1db25fc6128b1dd6ff0741fa4abf969ded161599d07ca7edd0739cc0/ruff-0.14.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1ec1ac071e7e37e0221d2f2dbaf90897a988c531a8592a6a5959f0603a1ecf5e", size = 12252581, upload-time = "2025-10-31T00:26:10.297Z" },
{ url = "https://files.pythonhosted.org/packages/da/13/21096308f384d796ffe3f2960b17054110a9c3828d223ca540c2b7cc670b/ruff-0.14.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afcdc4b5335ef440d19e7df9e8ae2ad9f749352190e96d481dc501b753f0733e", size = 12307503, upload-time = "2025-10-31T00:26:12.646Z" },
{ url = "https://files.pythonhosted.org/packages/cb/cc/a350bac23f03b7dbcde3c81b154706e80c6f16b06ff1ce28ed07dc7b07b0/ruff-0.14.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7bfc42f81862749a7136267a343990f865e71fe2f99cf8d2958f684d23ce3dfa", size = 12675457, upload-time = "2025-10-31T00:26:15.044Z" },
{ url = "https://files.pythonhosted.org/packages/cb/76/46346029fa2f2078826bc88ef7167e8c198e58fe3126636e52f77488cbba/ruff-0.14.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a65e448cfd7e9c59fae8cf37f9221585d3354febaad9a07f29158af1528e165f", size = 13403980, upload-time = "2025-10-31T00:26:17.81Z" },
{ url = "https://files.pythonhosted.org/packages/9f/a4/35f1ef68c4e7b236d4a5204e3669efdeefaef21f0ff6a456792b3d8be438/ruff-0.14.3-py3-none-win32.whl", hash = "sha256:f3d91857d023ba93e14ed2d462ab62c3428f9bbf2b4fbac50a03ca66d31991f7", size = 12500045, upload-time = "2025-10-31T00:26:20.503Z" },
{ url = "https://files.pythonhosted.org/packages/03/15/51960ae340823c9859fb60c63301d977308735403e2134e17d1d2858c7fb/ruff-0.14.3-py3-none-win_amd64.whl", hash = "sha256:d7b7006ac0756306db212fd37116cce2bd307e1e109375e1c6c106002df0ae5f", size = 13594005, upload-time = "2025-10-31T00:26:22.533Z" },
{ url = "https://files.pythonhosted.org/packages/b7/73/4de6579bac8e979fca0a77e54dec1f1e011a0d268165eb8a9bc0982a6564/ruff-0.14.3-py3-none-win_arm64.whl", hash = "sha256:26eb477ede6d399d898791d01961e16b86f02bc2486d0d1a7a9bb2379d055dc1", size = 12590017, upload-time = "2025-10-31T00:26:24.52Z" },
]
[[package]]
@@ -1170,41 +1212,51 @@ wheels = [
[[package]]
name = "tomli"
version = "2.2.1"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" },
{ url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" },
{ url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" },
{ url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" },
{ url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" },
{ url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" },
{ url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" },
{ url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" },
{ url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" },
{ url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" },
{ url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" },
{ url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" },
{ url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" },
{ url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" },
{ url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" },
{ url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" },
{ url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" },
{ url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" },
{ url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" },
{ url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" },
{ url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" },
{ url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" },
{ url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" },
{ url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" },
{ url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" },
{ url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" },
{ url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" },
{ url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" },
{ url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" },
{ url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" },
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
{ url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" },
{ url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" },
{ url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" },
{ url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" },
{ url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" },
{ url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" },
{ url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" },
{ url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" },
{ url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" },
{ url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" },
{ url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" },
{ url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" },
{ url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" },
{ url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" },
{ url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" },
{ url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" },
{ url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" },
{ url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" },
{ url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" },
{ url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" },
{ url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" },
{ url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" },
{ url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" },
{ url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" },
{ url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" },
{ url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" },
{ url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" },
{ url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" },
{ url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" },
{ url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" },
{ url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" },
{ url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" },
{ url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" },
{ url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" },
{ url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" },
{ url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" },
{ url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" },
{ url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" },
{ url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" },
{ url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" },
{ url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
]
[[package]]
@@ -1,13 +1,12 @@
from collections.abc import Sequence
from typing import Annotated, Literal, TypedDict
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import END, StateGraph, add_messages
from langgraph.prebuilt import ToolNode
tools = [TavilySearchResults(max_results=1)]
tools = []
model_oai = ChatOpenAI(temperature=0)
@@ -5,5 +5,5 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langgraph==0.6.0"
"langgraph==1.0.2"
]
@@ -5,5 +5,5 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langchain-openai==0.3.0"
"langchain-openai==1.0.1"
]
@@ -6,9 +6,9 @@ readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langchain-openai==1.0.0a2",
"langgraph==1.0.0a2",
"langchain_community>=0.3.0",
"langchain-anthropic==1.0.0a5",
"langgraph==1.0.2"
]
[tool.uv]
prerelease = "allow"
prerelease = "allow"
+5 -1
View File
@@ -13,8 +13,9 @@ from pathlib import Path
import msgspec
from langgraph_cli.config import (
from langgraph_cli.schemas import (
AuthConfig,
CacheConfig,
CheckpointerConfig,
Config,
ConfigurableHeaderConfig,
@@ -22,6 +23,7 @@ from langgraph_cli.config import (
HttpConfig,
IndexConfig,
SecurityConfig,
SerdeConfig,
StoreConfig,
ThreadTTLConfig,
TTLConfig,
@@ -110,8 +112,10 @@ def add_descriptions_to_schema(schema, cls):
SecurityConfig,
HttpConfig,
CorsConfig,
CacheConfig,
ThreadTTLConfig,
CheckpointerConfig,
SerdeConfig,
TTLConfig,
ConfigurableHeaderConfig,
]:
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.4.4"
__version__ = "0.4.7"
+102 -522
View File
@@ -4,10 +4,12 @@ import pathlib
import re
import textwrap
from collections import Counter
from typing import Any, Literal, NamedTuple, TypedDict
from typing import Literal, NamedTuple
import click
from langgraph_cli.schemas import Config, Distros
MIN_NODE_VERSION = "20"
DEFAULT_NODE_VERSION = "20"
@@ -17,504 +19,6 @@ DEFAULT_PYTHON_VERSION = "3.11"
DEFAULT_IMAGE_DISTRO = "debian"
Distros = Literal["debian", "wolfi", "bullseye", "bookworm"]
MiddlewareOrders = Literal["auth_first", "middleware_first"]
class TTLConfig(TypedDict, total=False):
"""Configuration for TTL (time-to-live) behavior in the store."""
refresh_on_read: bool
"""Default behavior for refreshing TTLs on read operations (`GET` and `SEARCH`).
If `True`, TTLs will be refreshed on read operations (get/search) by default.
This can be overridden per-operation by explicitly setting `refresh_ttl`.
Defaults to `True` if not configured.
"""
default_ttl: float | None
"""Optional. Default TTL (time-to-live) in minutes for new items.
If provided, all new items will have this TTL unless explicitly overridden.
If omitted, items will have no TTL by default.
"""
sweep_interval_minutes: int | None
"""Optional. Interval in minutes between TTL sweep iterations.
If provided, the store will periodically delete expired items based on the TTL.
If omitted, no automatic sweeping will occur.
"""
class IndexConfig(TypedDict, total=False):
"""Configuration for indexing documents for semantic search in the store.
This governs how text is converted into embeddings and stored for vector-based lookups.
"""
dims: int
"""Required. Dimensionality of the embedding vectors you will store.
Must match the output dimension of your selected embedding model or custom embed function.
If mismatched, you will likely encounter shape/size errors when inserting or querying vectors.
Common embedding model output dimensions:
- openai:text-embedding-3-large: 3072
- openai:text-embedding-3-small: 1536
- openai:text-embedding-ada-002: 1536
- cohere:embed-english-v3.0: 1024
- cohere:embed-english-light-v3.0: 384
- cohere:embed-multilingual-v3.0: 1024
- cohere:embed-multilingual-light-v3.0: 384
"""
embed: str
"""Required. Identifier or reference to the embedding model or a custom embedding function.
The format can vary:
- "<provider>:<model_name>" for recognized providers (e.g., "openai:text-embedding-3-large")
- "path/to/module.py:function_name" for your own local embedding function
- "my_custom_embed" if it's a known alias in your system
Examples:
- "openai:text-embedding-3-large"
- "cohere:embed-multilingual-v3.0"
- "src/app.py:embeddings"
Note: Must return embeddings of dimension `dims`.
"""
fields: list[str] | None
"""Optional. List of JSON fields to extract before generating embeddings.
Defaults to ["$"], which means the entire JSON object is embedded as one piece of text.
If you provide multiple fields (e.g. ["title", "content"]), each is extracted and embedded separately,
often saving token usage if you only care about certain parts of the data.
Example:
fields=["title", "abstract", "author.biography"]
"""
class StoreConfig(TypedDict, total=False):
"""Configuration for the built-in long-term memory store.
This store can optionally perform semantic search. If you omit `index`,
the store will just handle traditional (non-embedded) data without vector lookups.
"""
index: IndexConfig | None
"""Optional. Defines the vector-based semantic search configuration.
If provided, the store will:
- Generate embeddings according to `index.embed`
- Enforce the embedding dimension given by `index.dims`
- Embed only specified JSON fields (if any) from `index.fields`
If omitted, no vector index is initialized.
"""
ttl: TTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the store will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
class ThreadTTLConfig(TypedDict, total=False):
"""Configure a default TTL for checkpointed data within threads."""
strategy: Literal["delete"]
"""Strategy to use for deleting checkpointed data.
Choices:
- "delete": Delete all checkpoints for a thread after TTL expires.
"""
default_ttl: float | None
"""Default TTL (time-to-live) in minutes for checkpointed data."""
sweep_interval_minutes: int | None
"""Interval in minutes between sweep iterations.
If omitted, a default interval will be used (typically ~ 5 minutes)."""
class CheckpointerConfig(TypedDict, total=False):
"""Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
ttl: ThreadTTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the checkpointer will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
class SecurityConfig(TypedDict, total=False):
"""Configuration for OpenAPI security definitions and requirements.
Useful for specifying global or path-level authentication and authorization flows
(e.g., OAuth2, API key headers, etc.).
"""
securitySchemes: dict[str, dict[str, Any]]
"""Describe each security scheme recognized by your OpenAPI spec.
Keys are scheme names (e.g. "OAuth2", "ApiKeyAuth") and values are their definitions.
Example:
{
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"read": "Read data", "write": "Write data"}
}
}
}
}
"""
security: list[dict[str, list[str]]]
"""Global security requirements across all endpoints.
Each element in the list maps a security scheme (e.g. "OAuth2") to a list of scopes (e.g. ["read", "write"]).
Example:
[
{"OAuth2": ["read", "write"]},
{"ApiKeyAuth": []}
]
"""
# path => {method => security}
paths: dict[str, dict[str, list[dict[str, list[str]]]]]
"""Path-specific security overrides.
Keys are path templates (e.g., "/items/{item_id}"), mapping to:
- Keys that are HTTP methods (e.g., "GET", "POST"),
- Values are lists of security definitions (just like `security`) for that method.
Example:
{
"/private_data": {
"GET": [{"OAuth2": ["read"]}],
"POST": [{"OAuth2": ["write"]}]
}
}
"""
class AuthConfig(TypedDict, total=False):
"""Configuration for custom authentication logic and how it integrates into the OpenAPI spec."""
path: str
"""Required. Path to an instance of the Auth() class that implements custom authentication.
Format: "path/to/file.py:my_auth"
"""
disable_studio_auth: bool
"""Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.
Defaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header
value is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom
authentication logic, regardless of origin of the request.
"""
openapi: SecurityConfig
"""The security configuration to include in your server's OpenAPI spec.
Example (OAuth2):
{
"securitySchemes": {
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"me": "Read user info", "items": "Manage items"}
}
}
}
},
"security": [
{"OAuth2": ["me"]}
]
}
"""
class CorsConfig(TypedDict, total=False):
"""Specifies Cross-Origin Resource Sharing (CORS) rules for your server.
If omitted, defaults are typically very restrictive (often no cross-origin requests).
Configure carefully if you want to allow usage from browsers hosted on other domains.
"""
allow_origins: list[str]
"""Optional. List of allowed origins (e.g., "https://example.com").
Default is often an empty list (no external origins).
Use "*" only if you trust all origins, as that bypasses most restrictions.
"""
allow_methods: list[str]
"""Optional. HTTP methods permitted for cross-origin requests (e.g. ["GET", "POST"]).
Default might be ["GET", "POST", "OPTIONS"] depending on your server framework.
"""
allow_headers: list[str]
"""Optional. HTTP headers that can be used in cross-origin requests (e.g. ["Content-Type", "Authorization"])."""
allow_credentials: bool
"""Optional. If `True`, cross-origin requests can include credentials (cookies, auth headers).
Default False to avoid accidentally exposing secured endpoints to untrusted sites.
"""
allow_origin_regex: str
"""Optional. A regex pattern for matching allowed origins, used if you have dynamic subdomains.
Example: "^https://.*\\.mycompany\\.com$"
"""
expose_headers: list[str]
"""Optional. List of headers that browsers are allowed to read from the response in cross-origin contexts."""
max_age: int
"""Optional. How many seconds the browser may cache preflight responses.
Default might be 600 (10 minutes). Larger values reduce preflight requests but can cause stale configurations.
"""
class ConfigurableHeaderConfig(TypedDict):
"""Customize which headers to include as configurable values in your runs.
By default, omits x-api-key, x-tenant-id, and x-service-key.
Exclusions (if provided) take precedence.
Each value can be a raw string with an optional wildcard.
"""
includes: list[str] | None
"""Headers to include (if not also matches against an 'exludes' pattern.
Examples:
- 'user-agent'
- 'x-configurable-*'
"""
excludes: list[str] | None
"""Headers to exclude. Applied before the 'includes' checks.
Examples:
- 'x-api-key'
- '*key*'
- '*token*'
"""
class HttpConfig(TypedDict, total=False):
"""Configuration for the built-in HTTP server that powers your deployment's routes and endpoints."""
app: str
"""Optional. Import path to a custom Starlette/FastAPI application to mount.
Format: "path/to/module.py:app_var"
If provided, it can override or extend the default routes.
"""
disable_assistants: bool
"""Optional. If `True`, /assistants routes are removed from the server.
Default is False (meaning /assistants is enabled).
"""
disable_threads: bool
"""Optional. If `True`, /threads routes are removed.
Default is False.
"""
disable_runs: bool
"""Optional. If `True`, /runs routes are removed.
Default is False.
"""
disable_store: bool
"""Optional. If `True`, /store routes are removed, disabling direct store interactions via HTTP.
Default is False.
"""
disable_mcp: bool
"""Optional. If `True`, /mcp routes are removed, disabling the MCP server.
Default is False.
"""
disable_meta: bool
"""Optional. Remove meta endpoints.
Set to True to disable the following endpoints: /openapi.json, /info, /metrics, /docs.
This will also make the /ok endpoint skip any DB or other checks, always returning {"ok": True}.
Default is False.
"""
cors: CorsConfig | None
"""Optional. Defines CORS restrictions. If omitted, no special rules are set and
cross-origin behavior depends on default server settings.
"""
configurable_headers: ConfigurableHeaderConfig | None
"""Optional. Defines how headers are treated for a run's configuration.
You can include or exclude headers as configurable values to condition your
agent's behavior or permissions on a request's headers."""
logging_headers: ConfigurableHeaderConfig | None
"""Optional. Defines which headers are excluded from logging."""
middleware_order: MiddlewareOrders | None
"""Optional. Defines the order in which to apply server customizations.
Choices:
- "auth_first": Authentication hooks (custom or default) are evaluated
before custom middleware.
- "middleware_first": Custom middleware is evaluated
before authentication hooks (custom or default).
Default is `middleware_first`.
"""
enable_custom_route_auth: bool
"""Optional. If `True`, authentication is enabled for custom routes,
not just the routes that are protected by default.
(Routes protected by default include /assistants, /threads, and /runs).
Default is False. This flag only affects authentication behavior
if `app` is provided and contains custom routes.
"""
class Config(TypedDict, total=False):
"""Top-level config for langgraph-cli or similar deployment tooling."""
python_version: str
"""Optional. Python version in 'major.minor' format (e.g. '3.11').
Must be at least 3.11 or greater for this deployment to function properly.
"""
node_version: str | None
"""Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.
Must be >= 20 if provided.
"""
api_version: str | None
"""Optional. Which semantic version of the LangGraph API server to use.
Defaults to latest. Check the
[changelog](https://docs.langchain.com/langgraph-platform/langgraph-server-changelog)
for more information."""
_INTERNAL_docker_tag: str | None
"""Optional. Internal use only.
"""
base_image: str | None
"""Optional. Base image to use for the LangGraph API server.
Defaults to langchain/langgraph-api or langchain/langgraphjs-api."""
image_distro: Distros | None
"""Optional. Linux distribution for the base image.
Must be one of 'wolfi', 'debian', 'bullseye', or 'bookworm'.
If omitted, defaults to 'debian' ('latest').
"""
pip_config_file: str | None
"""Optional. Path to a pip config file (e.g., "/etc/pip.conf" or "pip.ini") for controlling
package installation (custom indices, credentials, etc.).
Only relevant if Python dependencies are installed via pip. If omitted, default pip settings are used.
"""
pip_installer: str | None
"""Optional. Python package installer to use ('auto', 'pip', 'uv').
- 'auto' (default): Use uv for supported base images, otherwise pip
- 'pip': Force use of pip regardless of base image support
- 'uv': Force use of uv (will fail if base image doesn't support it)
"""
dockerfile_lines: list[str]
"""Optional. Additional Docker instructions that will be appended to your base Dockerfile.
Useful for installing OS packages, setting environment variables, etc.
Example:
dockerfile_lines=[
"RUN apt-get update && apt-get install -y libmagic-dev",
"ENV MY_CUSTOM_VAR=hello_world"
]
"""
dependencies: list[str]
"""List of Python dependencies to install, either from PyPI or local paths.
Examples:
- "." or "./src" if you have a local Python package
- str (aka "anthropic") for a PyPI package
- "git+https://github.com/org/repo.git@main" for a Git-based package
Defaults to an empty list, meaning no additional packages installed beyond your base environment.
"""
graphs: dict[str, str]
"""Optional. Named definitions of graphs, each pointing to a Python object.
Graphs can be StateGraph, @entrypoint, or any other Pregel object OR they can point to (async) context
managers that accept a single configuration argument (of type RunnableConfig) and return a pregel object
(instance of Stategraph, etc.).
Keys are graph names, values are "path/to/file.py:object_name".
Example:
{
"mygraph": "graphs/my_graph.py:graph_definition",
"anothergraph": "graphs/another.py:get_graph"
}
"""
env: dict[str, str] | str
"""Optional. Environment variables to set for your deployment.
- If given as a dict, keys are variable names and values are their values.
- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.
Example as a dict:
env={"API_TOKEN": "abc123", "DEBUG": "true"}
Example as a file path:
env=".env"
"""
store: StoreConfig | None
"""Optional. Configuration for the built-in long-term memory store, including semantic search indexing.
If omitted, no vector index is set up (the object store will still be present, however).
"""
checkpointer: CheckpointerConfig | None
"""Optional. Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
auth: AuthConfig | None
"""Optional. Custom authentication config, including the path to your Python auth logic and
the OpenAPI security definitions it uses.
"""
http: HttpConfig | None
"""Optional. Configuration for the built-in HTTP server, controlling which custom routes are exposed
and how cross-origin requests are handled.
"""
ui: dict[str, str] | None
"""Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file.
"""
keep_pkg_tools: bool | list[str] | None
"""Optional. Control whether to retain Python packaging tools in the final image.
Allowed tools are: "pip", "setuptools", "wheel".
You can also set to true to include all packaging tools.
"""
_BUILD_TOOLS = ("pip", "setuptools", "wheel")
@@ -650,6 +154,7 @@ def validate_config(config: Config) -> Config:
"env": config.get("env", {}),
"store": config.get("store"),
"auth": config.get("auth"),
"encryption": config.get("encryption"),
"http": config.get("http"),
"checkpointer": config.get("checkpointer"),
"ui": config.get("ui"),
@@ -724,6 +229,14 @@ def validate_config(config: Config) -> Config:
f"Invalid auth.path format: '{auth_conf['path']}'. "
"Must be in format './path/to/file.py:attribute_name'"
)
# Validate encryption config
if encryption_conf := config.get("encryption"):
if "path" in encryption_conf:
if ":" not in encryption_conf["path"]:
raise ValueError(
f"Invalid encryption.path format: '{encryption_conf['path']}'. "
"Must be in format './path/to/file.py:attribute_name'"
)
if http_conf := config.get("http"):
if "app" in http_conf:
if ":" not in http_conf["app"]:
@@ -1110,6 +623,49 @@ def _update_auth_path(
)
def _update_encryption_path(
config_path: pathlib.Path, config: Config, local_deps: LocalDeps
) -> None:
"""Update encryption.path to use Docker container paths."""
encryption_conf = config.get("encryption")
if not encryption_conf or not (path_str := encryption_conf.get("path")):
return
module_str, sep, attr_str = path_str.partition(":")
if not sep or not module_str.startswith("."):
return # Already validated or absolute path
resolved = config_path.parent / module_str
if not resolved.exists():
raise FileNotFoundError(
f"Encryption file not found: {resolved} (from {path_str})"
)
if not resolved.is_file():
raise IsADirectoryError(f"Encryption path must be a file: {resolved}")
# Check faux packages first (higher priority)
for faux_path, (_, destpath) in local_deps.faux_pkgs.items():
if resolved.is_relative_to(faux_path):
new_path = f"{destpath}/{resolved.relative_to(faux_path)}:{attr_str}"
encryption_conf["path"] = new_path
return
# Check real packages
for real_path in local_deps.real_pkgs:
if resolved.is_relative_to(real_path):
new_path = (
f"/deps/{real_path.name}/{resolved.relative_to(real_path)}:{attr_str}"
)
encryption_conf["path"] = new_path
return
raise ValueError(
f"Encryption file '{resolved}' not covered by dependencies.\n"
"Add its parent directory to the 'dependencies' array in your config.\n"
f"Current dependencies: {config['dependencies']}"
)
def _update_http_app_path(
config_path: pathlib.Path, config: Config, local_deps: LocalDeps
) -> None:
@@ -1305,6 +861,8 @@ def python_config_to_docker(
_update_graph_paths(config_path, config, local_deps)
# Rewrite auth path, so it points to the correct location in the Docker container
_update_auth_path(config_path, config, local_deps)
# Rewrite encryption path, so it points to the correct location in the Docker container
_update_encryption_path(config_path, config, local_deps)
# Rewrite HTTP app path, so it points to the correct location in the Docker container
_update_http_app_path(config_path, config, local_deps)
@@ -1395,6 +953,9 @@ ADD {relpath} /deps/{name}
if (auth_config := config.get("auth")) is not None:
env_vars.append(f"ENV LANGGRAPH_AUTH='{json.dumps(auth_config)}'")
if (encryption_config := config.get("encryption")) is not None:
env_vars.append(f"ENV LANGGRAPH_ENCRYPTION='{json.dumps(encryption_config)}'")
if (http_config := config.get("http")) is not None:
env_vars.append(f"ENV LANGGRAPH_HTTP='{json.dumps(http_config)}'")
@@ -1422,35 +983,51 @@ ADD {relpath} /deps/{name}
]
)
image_str = docker_tag(config, base_image, api_version)
docker_file_contents = [
f"FROM {image_str}",
"",
os.linesep.join(config["dockerfile_lines"]),
"",
installs,
"",
"# -- Installing all local dependencies --",
f"""RUN for dep in /deps/*; do \
# Prepare docker file contents
docker_file_contents = []
# Add syntax directive if we have additional contexts (requires BuildKit frontend.contexts capability)
if local_deps.additional_contexts:
docker_file_contents.extend(
[
"# syntax=docker/dockerfile:1.4",
"",
]
)
# Add main dockerfile content
docker_file_contents.extend(
[
f"FROM {image_str}",
"",
os.linesep.join(config["dockerfile_lines"]),
"",
installs,
"",
"# -- Installing all local dependencies --",
f"""RUN for dep in /deps/*; do \
echo "Installing $dep"; \
if [ -d "$dep" ]; then \
echo "Installing $dep"; \
(cd "$dep" && {global_reqs_pip_install} -e .); \
fi; \
done""",
"# -- End of local dependencies install --",
os.linesep.join(env_vars),
"",
js_inst_str,
"",
# Add pip cleanup after all installations are complete
_get_pip_cleanup_lines(
install_cmd=install_cmd,
to_uninstall=build_tools_to_uninstall,
pip_installer=pip_installer,
),
"",
f"WORKDIR {local_deps.working_dir}" if local_deps.working_dir else "",
]
"# -- End of local dependencies install --",
os.linesep.join(env_vars),
"",
js_inst_str,
"",
# Add pip cleanup after all installations are complete
_get_pip_cleanup_lines(
install_cmd=install_cmd,
to_uninstall=build_tools_to_uninstall,
pip_installer=pip_installer,
),
"",
f"WORKDIR {local_deps.working_dir}" if local_deps.working_dir else "",
]
)
additional_contexts: dict[str, str] = {}
for p in local_deps.additional_contexts:
@@ -1502,6 +1079,9 @@ def node_config_to_docker(
if (auth_config := config.get("auth")) is not None:
env_vars.append(f"ENV LANGGRAPH_AUTH='{json.dumps(auth_config)}'")
if (encryption_config := config.get("encryption")) is not None:
env_vars.append(f"ENV LANGGRAPH_ENCRYPTION='{json.dumps(encryption_config)}'")
if (http_config := config.get("http")) is not None:
env_vars.append(f"ENV LANGGRAPH_HTTP='{json.dumps(http_config)}'")
+639
View File
@@ -0,0 +1,639 @@
from typing import Any, Literal, TypedDict
Distros = Literal["debian", "wolfi", "bullseye", "bookworm"]
MiddlewareOrders = Literal["auth_first", "middleware_first"]
class TTLConfig(TypedDict, total=False):
"""Configuration for TTL (time-to-live) behavior in the store."""
refresh_on_read: bool
"""Default behavior for refreshing TTLs on read operations (`GET` and `SEARCH`).
If `True`, TTLs will be refreshed on read operations (get/search) by default.
This can be overridden per-operation by explicitly setting `refresh_ttl`.
Defaults to `True` if not configured.
"""
default_ttl: float | None
"""Optional. Default TTL (time-to-live) in minutes for new items.
If provided, all new items will have this TTL unless explicitly overridden.
If omitted, items will have no TTL by default.
"""
sweep_interval_minutes: int | None
"""Optional. Interval in minutes between TTL sweep iterations.
If provided, the store will periodically delete expired items based on the TTL.
If omitted, no automatic sweeping will occur.
"""
class IndexConfig(TypedDict, total=False):
"""Configuration for indexing documents for semantic search in the store.
This governs how text is converted into embeddings and stored for vector-based lookups.
"""
dims: int
"""Required. Dimensionality of the embedding vectors you will store.
Must match the output dimension of your selected embedding model or custom embed function.
If mismatched, you will likely encounter shape/size errors when inserting or querying vectors.
Common embedding model output dimensions:
- openai:text-embedding-3-large: 3072
- openai:text-embedding-3-small: 1536
- openai:text-embedding-ada-002: 1536
- cohere:embed-english-v3.0: 1024
- cohere:embed-english-light-v3.0: 384
- cohere:embed-multilingual-v3.0: 1024
- cohere:embed-multilingual-light-v3.0: 384
"""
embed: str
"""Required. Identifier or reference to the embedding model or a custom embedding function.
The format can vary:
- "<provider>:<model_name>" for recognized providers (e.g., "openai:text-embedding-3-large")
- "path/to/module.py:function_name" for your own local embedding function
- "my_custom_embed" if it's a known alias in your system
Examples:
- "openai:text-embedding-3-large"
- "cohere:embed-multilingual-v3.0"
- "src/app.py:embeddings"
Note: Must return embeddings of dimension `dims`.
"""
fields: list[str] | None
"""Optional. List of JSON fields to extract before generating embeddings.
Defaults to ["$"], which means the entire JSON object is embedded as one piece of text.
If you provide multiple fields (e.g. ["title", "content"]), each is extracted and embedded separately,
often saving token usage if you only care about certain parts of the data.
Example:
fields=["title", "abstract", "author.biography"]
"""
class StoreConfig(TypedDict, total=False):
"""Configuration for the built-in long-term memory store.
This store can optionally perform semantic search. If you omit `index`,
the store will just handle traditional (non-embedded) data without vector lookups.
"""
index: IndexConfig | None
"""Optional. Defines the vector-based semantic search configuration.
If provided, the store will:
- Generate embeddings according to `index.embed`
- Enforce the embedding dimension given by `index.dims`
- Embed only specified JSON fields (if any) from `index.fields`
If omitted, no vector index is initialized.
"""
ttl: TTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the store will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
class ThreadTTLConfig(TypedDict, total=False):
"""Configure a default TTL for checkpointed data within threads."""
strategy: Literal["delete"]
"""Strategy to use for deleting checkpointed data.
Choices:
- "delete": Delete all checkpoints for a thread after TTL expires.
"""
default_ttl: float | None
"""Default TTL (time-to-live) in minutes for checkpointed data."""
sweep_interval_minutes: int | None
"""Interval in minutes between sweep iterations.
If omitted, a default interval will be used (typically ~ 5 minutes)."""
class SerdeConfig(TypedDict, total=False):
"""Configuration for the built-in serde, which handles checkpointing of state.
If omitted, no serde is set up (the object store will still be present, however)."""
allowed_json_modules: list[list[str]] | bool | None
"""Optional. List of allowed python modules to de-serialize custom objects from.
If provided, only the specified modules will be allowed to be deserialized.
If omitted, no modules are allowed, and the object returned will simply be a json object OR
a deserialized langchain object.
Example:
{...
"serde": {
"allowed_json_modules": [
["my_agent", "my_file", "SomeType"],
]
}
}
If you set this to True, any module will be allowed to be deserialized.
Example:
{...
"serde": {
"allowed_json_modules": true
}
}
"""
pickle_fallback: bool
"""Optional. Whether to allow pickling as a fallback for deserialization.
If True, pickling will be allowed as a fallback for deserialization.
If False, pickling will not be allowed as a fallback for deserialization.
Defaults to True if not configured."""
class CheckpointerConfig(TypedDict, total=False):
"""Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
ttl: ThreadTTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the checkpointer will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
serde: SerdeConfig | None
"""Optional. Defines the serde configuration.
If provided, the checkpointer will apply serde settings according to the configuration.
If omitted, no serde behavior is configured.
This configuration requires server version 0.5 or later to take effect.
"""
class SecurityConfig(TypedDict, total=False):
"""Configuration for OpenAPI security definitions and requirements.
Useful for specifying global or path-level authentication and authorization flows
(e.g., OAuth2, API key headers, etc.).
"""
securitySchemes: dict[str, dict[str, Any]]
"""Describe each security scheme recognized by your OpenAPI spec.
Keys are scheme names (e.g. "OAuth2", "ApiKeyAuth") and values are their definitions.
Example:
{
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"read": "Read data", "write": "Write data"}
}
}
}
}
"""
security: list[dict[str, list[str]]]
"""Global security requirements across all endpoints.
Each element in the list maps a security scheme (e.g. "OAuth2") to a list of scopes (e.g. ["read", "write"]).
Example:
[
{"OAuth2": ["read", "write"]},
{"ApiKeyAuth": []}
]
"""
# path => {method => security}
paths: dict[str, dict[str, list[dict[str, list[str]]]]]
"""Path-specific security overrides.
Keys are path templates (e.g., "/items/{item_id}"), mapping to:
- Keys that are HTTP methods (e.g., "GET", "POST"),
- Values are lists of security definitions (just like `security`) for that method.
Example:
{
"/private_data": {
"GET": [{"OAuth2": ["read"]}],
"POST": [{"OAuth2": ["write"]}]
}
}
"""
class CacheConfig(TypedDict, total=False):
cache_keys: list[str]
"""Optional. List of header keys to use for caching.
Example:
["user_id", "workspace_id"]
"""
ttl_seconds: int
"""Optional. Time-to-live in seconds for cached items.
Example:
3600
"""
max_size: int
"""Optional. Maximum size of the cache.
Example:
100
"""
class AuthConfig(TypedDict, total=False):
"""Configuration for custom authentication logic and how it integrates into the OpenAPI spec."""
path: str
"""Required. Path to an instance of the Auth() class that implements custom authentication.
Format: "path/to/file.py:my_auth"
"""
disable_studio_auth: bool
"""Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.
Defaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header
value is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom
authentication logic, regardless of origin of the request.
"""
openapi: SecurityConfig
"""The security configuration to include in your server's OpenAPI spec.
Example (OAuth2):
{
"securitySchemes": {
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"me": "Read user info", "items": "Manage items"}
}
}
}
},
"security": [
{"OAuth2": ["me"]}
]
}
"""
cache: CacheConfig
"""Optional. Cache configuration for the server.
Example:
{
"cache_keys": ["user_id", "workspace_id"],
"ttl_seconds": 3600,
"max_size": 100
}
"""
class EncryptionConfig(TypedDict, total=False):
"""Configuration for custom at-rest encryption logic.
Allows you to implement custom encryption for sensitive data stored in the database,
including metadata fields and checkpoint blobs.
"""
path: str
"""Required. Path to an instance of the Encryption() class that implements custom encryption handlers.
Format: "path/to/file.py:my_encryption"
Example:
{
"encryption": {
"path": "./encryption.py:my_encryption"
}
}
"""
class CorsConfig(TypedDict, total=False):
"""Specifies Cross-Origin Resource Sharing (CORS) rules for your server.
If omitted, defaults are typically very restrictive (often no cross-origin requests).
Configure carefully if you want to allow usage from browsers hosted on other domains.
"""
allow_origins: list[str]
"""Optional. List of allowed origins (e.g., "https://example.com").
Default is often an empty list (no external origins).
Use "*" only if you trust all origins, as that bypasses most restrictions.
"""
allow_methods: list[str]
"""Optional. HTTP methods permitted for cross-origin requests (e.g. ["GET", "POST"]).
Default might be ["GET", "POST", "OPTIONS"] depending on your server framework.
"""
allow_headers: list[str]
"""Optional. HTTP headers that can be used in cross-origin requests (e.g. ["Content-Type", "Authorization"])."""
allow_credentials: bool
"""Optional. If `True`, cross-origin requests can include credentials (cookies, auth headers).
Default False to avoid accidentally exposing secured endpoints to untrusted sites.
"""
allow_origin_regex: str
"""Optional. A regex pattern for matching allowed origins, used if you have dynamic subdomains.
Example: "^https://.*\\.mycompany\\.com$"
"""
expose_headers: list[str]
"""Optional. List of headers that browsers are allowed to read from the response in cross-origin contexts."""
max_age: int
"""Optional. How many seconds the browser may cache preflight responses.
Default might be 600 (10 minutes). Larger values reduce preflight requests but can cause stale configurations.
"""
class ConfigurableHeaderConfig(TypedDict):
"""Customize which headers to include as configurable values in your runs.
By default, omits x-api-key, x-tenant-id, and x-service-key.
Exclusions (if provided) take precedence.
Each value can be a raw string with an optional wildcard.
"""
includes: list[str] | None
"""Headers to include (if not also matches against an 'exludes' pattern.
Examples:
- 'user-agent'
- 'x-configurable-*'
"""
excludes: list[str] | None
"""Headers to exclude. Applied before the 'includes' checks.
Examples:
- 'x-api-key'
- '*key*'
- '*token*'
"""
class HttpConfig(TypedDict, total=False):
"""Configuration for the built-in HTTP server that powers your deployment's routes and endpoints."""
app: str
"""Optional. Import path to a custom Starlette/FastAPI application to mount.
Format: "path/to/module.py:app_var"
If provided, it can override or extend the default routes.
"""
disable_assistants: bool
"""Optional. If `True`, /assistants routes are removed from the server.
Default is False (meaning /assistants is enabled).
"""
disable_threads: bool
"""Optional. If `True`, /threads routes are removed.
Default is False.
"""
disable_runs: bool
"""Optional. If `True`, /runs routes are removed.
Default is False.
"""
disable_store: bool
"""Optional. If `True`, /store routes are removed, disabling direct store interactions via HTTP.
Default is False.
"""
disable_mcp: bool
"""Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.
Default is False.
"""
disable_a2a: bool
"""Optional. If `True`, /a2a routes are removed, disabling default support to expose the deployment as an agent-to-agent (A2A) server.
Default is False.
"""
disable_meta: bool
"""Optional. Remove meta endpoints.
Set to True to disable the following endpoints: /openapi.json, /info, /metrics, /docs.
This will also make the /ok endpoint skip any DB or other checks, always returning {"ok": True}.
Default is False.
"""
disable_ui: bool
"""Optional. If `True`, /ui routes are removed, disabling the UI server.
Default is False.
"""
disable_webhooks: bool
"""Optional. If `True`, webhooks are disabled. Runs created with an associated webhook will
still be executed, but the webhook event will not be sent.
Default is False.
"""
cors: CorsConfig | None
"""Optional. Defines CORS restrictions. If omitted, no special rules are set and
cross-origin behavior depends on default server settings.
"""
configurable_headers: ConfigurableHeaderConfig | None
"""Optional. Defines how headers are treated for a run's configuration.
You can include or exclude headers as configurable values to condition your
agent's behavior or permissions on a request's headers."""
logging_headers: ConfigurableHeaderConfig | None
"""Optional. Defines which headers are excluded from logging."""
middleware_order: MiddlewareOrders | None
"""Optional. Defines the order in which to apply server customizations.
Choices:
- "auth_first": Authentication hooks (custom or default) are evaluated
before custom middleware.
- "middleware_first": Custom middleware is evaluated
before authentication hooks (custom or default).
Default is `middleware_first`.
"""
enable_custom_route_auth: bool
"""Optional. If `True`, authentication is enabled for custom routes,
not just the routes that are protected by default.
(Routes protected by default include /assistants, /threads, and /runs).
Default is False. This flag only affects authentication behavior
if `app` is provided and contains custom routes.
"""
mount_prefix: str
"""Optional. URL prefix to prepend to all the routes.
Example:
"/api"
"""
class Config(TypedDict, total=False):
"""Top-level config for langgraph-cli or similar deployment tooling."""
python_version: str
"""Optional. Python version in 'major.minor' format (e.g. '3.11').
Must be at least 3.11 or greater for this deployment to function properly.
"""
node_version: str | None
"""Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.
Must be >= 20 if provided.
"""
api_version: str | None
"""Optional. Which semantic version of the LangGraph API server to use.
Defaults to latest. Check the
[changelog](https://docs.langchain.com/langgraph-platform/langgraph-server-changelog)
for more information."""
_INTERNAL_docker_tag: str | None
"""Optional. Internal use only.
"""
base_image: str | None
"""Optional. Base image to use for the LangGraph API server.
Defaults to langchain/langgraph-api or langchain/langgraphjs-api."""
image_distro: Distros | None
"""Optional. Linux distribution for the base image.
Must be one of 'wolfi', 'debian', 'bullseye', or 'bookworm'.
If omitted, defaults to 'debian' ('latest').
"""
pip_config_file: str | None
"""Optional. Path to a pip config file (e.g., "/etc/pip.conf" or "pip.ini") for controlling
package installation (custom indices, credentials, etc.).
Only relevant if Python dependencies are installed via pip. If omitted, default pip settings are used.
"""
pip_installer: str | None
"""Optional. Python package installer to use ('auto', 'pip', 'uv').
- 'auto' (default): Use uv for supported base images, otherwise pip
- 'pip': Force use of pip regardless of base image support
- 'uv': Force use of uv (will fail if base image doesn't support it)
"""
dockerfile_lines: list[str]
"""Optional. Additional Docker instructions that will be appended to your base Dockerfile.
Useful for installing OS packages, setting environment variables, etc.
Example:
dockerfile_lines=[
"RUN apt-get update && apt-get install -y libmagic-dev",
"ENV MY_CUSTOM_VAR=hello_world"
]
"""
dependencies: list[str]
"""List of Python dependencies to install, either from PyPI or local paths.
Examples:
- "." or "./src" if you have a local Python package
- str (aka "anthropic") for a PyPI package
- "git+https://github.com/org/repo.git@main" for a Git-based package
Defaults to an empty list, meaning no additional packages installed beyond your base environment.
"""
graphs: dict[str, str]
"""Optional. Named definitions of graphs, each pointing to a Python object.
Graphs can be StateGraph, @entrypoint, or any other Pregel object OR they can point to (async) context
managers that accept a single configuration argument (of type RunnableConfig) and return a pregel object
(instance of Stategraph, etc.).
Keys are graph names, values are "path/to/file.py:object_name".
Example:
{
"mygraph": "graphs/my_graph.py:graph_definition",
"anothergraph": "graphs/another.py:get_graph"
}
"""
env: dict[str, str] | str
"""Optional. Environment variables to set for your deployment.
- If given as a dict, keys are variable names and values are their values.
- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.
Example as a dict:
env={"API_TOKEN": "abc123", "DEBUG": "true"}
Example as a file path:
env=".env"
"""
store: StoreConfig | None
"""Optional. Configuration for the built-in long-term memory store, including semantic search indexing.
If omitted, no vector index is set up (the object store will still be present, however).
"""
checkpointer: CheckpointerConfig | None
"""Optional. Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
auth: AuthConfig | None
"""Optional. Custom authentication config, including the path to your Python auth logic and
the OpenAPI security definitions it uses.
"""
encryption: EncryptionConfig | None
"""Optional. Custom at-rest encryption config, including the path to your Python encryption logic.
Allows you to implement custom encryption for sensitive data stored in the database.
"""
http: HttpConfig | None
"""Optional. Configuration for the built-in HTTP server, controlling which custom routes are exposed
and how cross-origin requests are handled.
"""
ui: dict[str, str] | None
"""Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file.
"""
keep_pkg_tools: bool | list[str] | None
"""Optional. Control whether to retain Python packaging tools in the final image.
Allowed tools are: "pip", "setuptools", "wheel".
You can also set to true to include all packaging tools.
"""
__all__ = [
"Config",
"StoreConfig",
"CheckpointerConfig",
"AuthConfig",
"EncryptionConfig",
"HttpConfig",
"MiddlewareOrders",
"Distros",
"TTLConfig",
"IndexConfig",
]
+6 -3
View File
@@ -19,13 +19,16 @@ dependencies = [
path = "langgraph_cli/__init__.py"
[project.optional-dependencies]
inmem = [
"langgraph-api>=0.3,<0.5.0 ; python_version >= '3.11'",
"langgraph-api>=0.4,<0.6.0 ; python_version >= '3.11'",
"langgraph-runtime-inmem>=0.7 ; python_version >= '3.11'",
"python-dotenv>=0.8.0",
]
[project.urls]
Repository = "https://www.github.com/langchain-ai/langgraph"
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/cli"
Twitter = "https://x.com/LangChainAI"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
[project.scripts]
langgraph = "langgraph_cli.cli:cli"
@@ -68,4 +71,4 @@ lint.select = [
"UP", # pyupgrade
]
lint.ignore = ["E501", "B008"]
target-version = "py310"
target-version = "py310"
@@ -8,7 +8,7 @@ authors = [
license = { text = "MIT" }
requires-python = ">=3.11,<4.0"
dependencies = [
"langgraph>=0.6.0,<0.7.0",
"langgraph>=0.6.0,<2",
"langchain-core>=0.2.14",
]
+120 -1
View File
@@ -99,6 +99,17 @@
},
"description": "Optional. Additional Docker instructions that will be appended to your base Dockerfile.\n\nUseful for installing OS packages, setting environment variables, etc."
},
"encryption": {
"anyOf": [
{
"$ref": "#/$defs/EncryptionConfig"
},
{
"type": "null"
}
],
"description": "Optional. Custom at-rest encryption config, including the path to your Python encryption logic.\n\nAllows you to implement custom encryption for sensitive data stored in the database.\n"
},
"env": {
"anyOf": [
{
@@ -292,6 +303,17 @@
},
"description": "Optional. Additional Docker instructions that will be appended to your base Dockerfile.\n\nUseful for installing OS packages, setting environment variables, etc."
},
"encryption": {
"anyOf": [
{
"$ref": "#/$defs/EncryptionConfig"
},
{
"type": "null"
}
],
"description": "Optional. Custom at-rest encryption config, including the path to your Python encryption logic.\n\nAllows you to implement custom encryption for sensitive data stored in the database.\n"
},
"env": {
"anyOf": [
{
@@ -405,6 +427,10 @@
"description": "Configuration for custom authentication logic and how it integrates into the OpenAPI spec.",
"type": "object",
"properties": {
"cache": {
"$ref": "#/$defs/CacheConfig",
"description": "Optional. Cache configuration for the server.\n"
},
"disable_studio_auth": {
"type": "boolean",
"description": "Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.\n\nDefaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header\nvalue is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom\nauthentication logic, regardless of origin of the request.\n"
@@ -420,6 +446,29 @@
},
"required": []
},
"CacheConfig": {
"title": "CacheConfig",
"type": "object",
"properties": {
"cache_keys": {
"type": "array",
"items": {
"type": "string"
},
"description": "Optional. List of header keys to use for caching.\n"
},
"max_size": {
"type": "integer",
"description": "Optional. Maximum size of the cache.\n"
},
"ttl_seconds": {
"type": "integer",
"description": "Optional. Time-to-live in seconds for cached items.\n"
}
},
"required": [],
"description": "dict() -> new empty dictionary\ndict(mapping) -> new dictionary initialized from a mapping object's\n (key, value) pairs\ndict(iterable) -> new dictionary initialized as if via:\n d = {}\n for k, v in iterable:\n d[k] = v\ndict(**kwargs) -> new dictionary initialized with the name=value pairs\n in the keyword argument list. For example: dict(one=1, two=2)"
},
"SecurityConfig": {
"title": "SecurityConfig",
"description": "Configuration for OpenAPI security definitions and requirements.\n\nUseful for specifying global or path-level authentication and authorization flows\n(e.g., OAuth2, API key headers, etc.).",
@@ -472,6 +521,17 @@
"description": "Configuration for the built-in checkpointer, which handles checkpointing of state.\n\nIf omitted, no checkpointer is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"serde": {
"anyOf": [
{
"$ref": "#/$defs/SerdeConfig"
},
{
"type": "null"
}
],
"description": "Optional. Defines the serde configuration.\n\nIf provided, the checkpointer will apply serde settings according to the configuration.\nIf omitted, no serde behavior is configured.\n\nThis configuration requires server version 0.5 or later to take effect.\n"
},
"ttl": {
"anyOf": [
{
@@ -486,6 +546,38 @@
},
"required": []
},
"SerdeConfig": {
"title": "SerdeConfig",
"description": "Configuration for the built-in serde, which handles checkpointing of state.\n\nIf omitted, no serde is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"allowed_json_modules": {
"anyOf": [
{
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Optional. List of allowed python modules to de-serialize custom objects from.\n\nIf provided, only the specified modules will be allowed to be deserialized.\nIf omitted, no modules are allowed, and the object returned will simply be a json object OR\na deserialized langchain object.\n"
},
"pickle_fallback": {
"type": "boolean",
"description": "Optional. Whether to allow pickling as a fallback for deserialization.\n\nIf True, pickling will be allowed as a fallback for deserialization.\nIf False, pickling will not be allowed as a fallback for deserialization.\nDefaults to True if not configured."
}
},
"required": []
},
"ThreadTTLConfig": {
"title": "ThreadTTLConfig",
"description": "Configure a default TTL for checkpointed data within threads.",
@@ -522,6 +614,17 @@
},
"required": []
},
"EncryptionConfig": {
"title": "EncryptionConfig",
"description": "Configuration for custom at-rest encryption logic.\n\n Allows you to implement custom encryption for sensitive data stored in the database,\n including metadata fields and checkpoint blobs.",
"type": "object",
"properties": {
"path": {
"type": "string"
}
},
"required": []
},
"HttpConfig": {
"title": "HttpConfig",
"description": "Configuration for the built-in HTTP server that powers your deployment's routes and endpoints.",
@@ -553,13 +656,17 @@
],
"description": "Optional. Defines CORS restrictions. If omitted, no special rules are set and\ncross-origin behavior depends on default server settings.\n"
},
"disable_a2a": {
"type": "boolean",
"description": "Optional. If `True`, /a2a routes are removed, disabling default support to expose the deployment as an agent-to-agent (A2A) server.\n\nDefault is False.\n"
},
"disable_assistants": {
"type": "boolean",
"description": "Optional. If `True`, /assistants routes are removed from the server.\n\nDefault is False (meaning /assistants is enabled).\n"
},
"disable_mcp": {
"type": "boolean",
"description": "Optional. If `True`, /mcp routes are removed, disabling the MCP server.\n\nDefault is False.\n"
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.\n\nDefault is False.\n"
},
"disable_meta": {
"type": "boolean",
@@ -577,6 +684,14 @@
"type": "boolean",
"description": "Optional. If `True`, /threads routes are removed.\n\nDefault is False.\n"
},
"disable_ui": {
"type": "boolean",
"description": "Optional. If `True`, /ui routes are removed, disabling the UI server.\n\nDefault is False.\n"
},
"disable_webhooks": {
"type": "boolean",
"description": "Optional. If `True`, webhooks are disabled. Runs created with an associated webhook will\nstill be executed, but the webhook event will not be sent.\n\nDefault is False.\n"
},
"enable_custom_route_auth": {
"type": "boolean",
"description": "Optional. If `True`, authentication is enabled for custom routes,\nnot just the routes that are protected by default.\n(Routes protected by default include /assistants, /threads, and /runs).\n\nDefault is False. This flag only affects authentication behavior\nif `app` is provided and contains custom routes.\n"
@@ -605,6 +720,10 @@
}
],
"description": "Optional. Defines the order in which to apply server customizations.\n"
},
"mount_prefix": {
"type": "string",
"description": "Optional. URL prefix to prepend to all the routes.\n"
}
},
"required": []
+120 -1
View File
@@ -99,6 +99,17 @@
},
"description": "Optional. Additional Docker instructions that will be appended to your base Dockerfile.\n\nUseful for installing OS packages, setting environment variables, etc."
},
"encryption": {
"anyOf": [
{
"$ref": "#/$defs/EncryptionConfig"
},
{
"type": "null"
}
],
"description": "Optional. Custom at-rest encryption config, including the path to your Python encryption logic.\n\nAllows you to implement custom encryption for sensitive data stored in the database.\n"
},
"env": {
"anyOf": [
{
@@ -292,6 +303,17 @@
},
"description": "Optional. Additional Docker instructions that will be appended to your base Dockerfile.\n\nUseful for installing OS packages, setting environment variables, etc."
},
"encryption": {
"anyOf": [
{
"$ref": "#/$defs/EncryptionConfig"
},
{
"type": "null"
}
],
"description": "Optional. Custom at-rest encryption config, including the path to your Python encryption logic.\n\nAllows you to implement custom encryption for sensitive data stored in the database.\n"
},
"env": {
"anyOf": [
{
@@ -405,6 +427,10 @@
"description": "Configuration for custom authentication logic and how it integrates into the OpenAPI spec.",
"type": "object",
"properties": {
"cache": {
"$ref": "#/$defs/CacheConfig",
"description": "Optional. Cache configuration for the server.\n"
},
"disable_studio_auth": {
"type": "boolean",
"description": "Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.\n\nDefaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header\nvalue is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom\nauthentication logic, regardless of origin of the request.\n"
@@ -420,6 +446,29 @@
},
"required": []
},
"CacheConfig": {
"title": "CacheConfig",
"type": "object",
"properties": {
"cache_keys": {
"type": "array",
"items": {
"type": "string"
},
"description": "Optional. List of header keys to use for caching.\n"
},
"max_size": {
"type": "integer",
"description": "Optional. Maximum size of the cache.\n"
},
"ttl_seconds": {
"type": "integer",
"description": "Optional. Time-to-live in seconds for cached items.\n"
}
},
"required": [],
"description": "dict() -> new empty dictionary\ndict(mapping) -> new dictionary initialized from a mapping object's\n (key, value) pairs\ndict(iterable) -> new dictionary initialized as if via:\n d = {}\n for k, v in iterable:\n d[k] = v\ndict(**kwargs) -> new dictionary initialized with the name=value pairs\n in the keyword argument list. For example: dict(one=1, two=2)"
},
"SecurityConfig": {
"title": "SecurityConfig",
"description": "Configuration for OpenAPI security definitions and requirements.\n\nUseful for specifying global or path-level authentication and authorization flows\n(e.g., OAuth2, API key headers, etc.).",
@@ -472,6 +521,17 @@
"description": "Configuration for the built-in checkpointer, which handles checkpointing of state.\n\nIf omitted, no checkpointer is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"serde": {
"anyOf": [
{
"$ref": "#/$defs/SerdeConfig"
},
{
"type": "null"
}
],
"description": "Optional. Defines the serde configuration.\n\nIf provided, the checkpointer will apply serde settings according to the configuration.\nIf omitted, no serde behavior is configured.\n\nThis configuration requires server version 0.5 or later to take effect.\n"
},
"ttl": {
"anyOf": [
{
@@ -486,6 +546,38 @@
},
"required": []
},
"SerdeConfig": {
"title": "SerdeConfig",
"description": "Configuration for the built-in serde, which handles checkpointing of state.\n\nIf omitted, no serde is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"allowed_json_modules": {
"anyOf": [
{
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Optional. List of allowed python modules to de-serialize custom objects from.\n\nIf provided, only the specified modules will be allowed to be deserialized.\nIf omitted, no modules are allowed, and the object returned will simply be a json object OR\na deserialized langchain object.\n"
},
"pickle_fallback": {
"type": "boolean",
"description": "Optional. Whether to allow pickling as a fallback for deserialization.\n\nIf True, pickling will be allowed as a fallback for deserialization.\nIf False, pickling will not be allowed as a fallback for deserialization.\nDefaults to True if not configured."
}
},
"required": []
},
"ThreadTTLConfig": {
"title": "ThreadTTLConfig",
"description": "Configure a default TTL for checkpointed data within threads.",
@@ -522,6 +614,17 @@
},
"required": []
},
"EncryptionConfig": {
"title": "EncryptionConfig",
"description": "Configuration for custom at-rest encryption logic.\n\n Allows you to implement custom encryption for sensitive data stored in the database,\n including metadata fields and checkpoint blobs.",
"type": "object",
"properties": {
"path": {
"type": "string"
}
},
"required": []
},
"HttpConfig": {
"title": "HttpConfig",
"description": "Configuration for the built-in HTTP server that powers your deployment's routes and endpoints.",
@@ -553,13 +656,17 @@
],
"description": "Optional. Defines CORS restrictions. If omitted, no special rules are set and\ncross-origin behavior depends on default server settings.\n"
},
"disable_a2a": {
"type": "boolean",
"description": "Optional. If `True`, /a2a routes are removed, disabling default support to expose the deployment as an agent-to-agent (A2A) server.\n\nDefault is False.\n"
},
"disable_assistants": {
"type": "boolean",
"description": "Optional. If `True`, /assistants routes are removed from the server.\n\nDefault is False (meaning /assistants is enabled).\n"
},
"disable_mcp": {
"type": "boolean",
"description": "Optional. If `True`, /mcp routes are removed, disabling the MCP server.\n\nDefault is False.\n"
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.\n\nDefault is False.\n"
},
"disable_meta": {
"type": "boolean",
@@ -577,6 +684,14 @@
"type": "boolean",
"description": "Optional. If `True`, /threads routes are removed.\n\nDefault is False.\n"
},
"disable_ui": {
"type": "boolean",
"description": "Optional. If `True`, /ui routes are removed, disabling the UI server.\n\nDefault is False.\n"
},
"disable_webhooks": {
"type": "boolean",
"description": "Optional. If `True`, webhooks are disabled. Runs created with an associated webhook will\nstill be executed, but the webhook event will not be sent.\n\nDefault is False.\n"
},
"enable_custom_route_auth": {
"type": "boolean",
"description": "Optional. If `True`, authentication is enabled for custom routes,\nnot just the routes that are protected by default.\n(Routes protected by default include /assistants, /threads, and /runs).\n\nDefault is False. This flag only affects authentication behavior\nif `app` is provided and contains custom routes.\n"
@@ -605,6 +720,10 @@
}
],
"description": "Optional. Defines the order in which to apply server customizations.\n"
},
"mount_prefix": {
"type": "string",
"description": "Optional. URL prefix to prepend to all the routes.\n"
}
},
"required": []
@@ -141,6 +141,7 @@ services:
additional_contexts:
- cli_1: {str(pathlib.Path(__file__).parent.parent.parent.parent.absolute())}
dockerfile_inline: |
# syntax=docker/dockerfile:1.4
FROM langchain/langgraph-api:3.11
# -- Adding local package . --
ADD . /deps/cli
+55
View File
@@ -48,6 +48,7 @@ def test_validate_config():
"env": {},
"store": None,
"auth": None,
"encryption": None,
"checkpointer": None,
"http": None,
"ui": None,
@@ -74,6 +75,7 @@ def test_validate_config():
"env": env,
"store": None,
"auth": None,
"encryption": None,
"checkpointer": None,
"http": None,
"ui": None,
@@ -419,6 +421,7 @@ def test_config_to_docker_simple():
"langchain/langgraph-api",
)
expected_docker_stdin = f"""\
# syntax=docker/dockerfile:1.4
FROM langchain/langgraph-api:3.11
# -- Installing local requirements --
COPY --from=outer-requirements.txt requirements.txt /deps/outer-graphs_reqs_a/graphs_reqs_a/requirements.txt
@@ -482,6 +485,7 @@ def test_config_to_docker_outside_path():
)
expected_docker_stdin = (
"""\
# syntax=docker/dockerfile:1.4
FROM langchain/langgraph-api:3.11
# -- Adding non-package dependency unit_tests --
ADD . /deps/outer-unit_tests/unit_tests
@@ -746,6 +750,57 @@ RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not foun
assert additional_contexts == {}
def test_config_to_docker_python_encryption():
# Test that encryption config is included in validation
graphs = {"agent": "./agent.py:graph"}
validated = validate_config(
{
"python_version": "3.11",
"graphs": graphs,
"dependencies": ["."],
"encryption": {"path": "./encryption.py:encryption"},
}
)
# Verify that encryption config is preserved after validation
assert validated.get("encryption") is not None
assert validated["encryption"]["path"] == "./encryption.py:encryption"
def test_config_to_docker_python_encryption_bad_path():
# Test that invalid encryption path format raises ValueError
graphs = {"agent": "./agent.py:graph"}
with pytest.raises(ValueError, match="Invalid encryption.path format"):
validate_config(
{
"python_version": "3.11",
"graphs": graphs,
"dependencies": ["."],
"encryption": {"path": "./encryption.py"}, # Missing :attribute
}
)
def test_config_to_docker_python_encryption_formatted():
# Test that encryption config is properly formatted in Docker output
graphs = {"agent": "./graphs/agent.py:graph"}
actual_docker_stdin, additional_contexts = config_to_docker(
PATH_TO_CONFIG,
validate_config(
{
"python_version": "3.11",
"dependencies": ["."],
"graphs": graphs,
"encryption": {"path": "./agent.py:my_encryption"},
}
),
"langchain/langgraph-api",
)
# Verify that LANGGRAPH_ENCRYPTION is in the docker output with the correct path
assert "LANGGRAPH_ENCRYPTION=" in actual_docker_stdin
assert "/deps/outer-unit_tests/unit_tests/agent.py:my_encryption" in actual_docker_stdin
def test_config_to_docker_nodejs_internal_docker_tag():
graphs = {"agent": "./graphs/agent.js:graph"}
actual_docker_stdin, additional_contexts = config_to_docker(
+657 -451
View File
File diff suppressed because it is too large Load Diff
@@ -1,16 +0,0 @@
{
"permissions": {
"allow": [
"Bash(rg:*)",
"Bash(python:*)",
"Bash(grep:*)",
"Bash(sed:*)",
"Bash(awk:*)",
"Bash(uv run mypy:*)",
"Bash(uv run:*)",
"Bash(make test:*)",
"Bash(make test_parallel:*)"
],
"deny": []
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
- [LangChain](https://docs.langchain.com/oss/python/langchain/overview) Provides integrations and composable components to streamline LLM application development.
> [!NOTE]
> Looking for the JS version of LangGraph? See the [JS repo](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://langchain-ai.github.io/langgraphjs/).
> Looking for the JS version of LangGraph? See the [JS repo](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://docs.langchain.com/oss/javascript/langgraph/overview).
## Additional resources
+23 -7
View File
@@ -39,14 +39,19 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
def copy(self) -> Self:
"""Return a copy of the channel.
By default, delegates to `checkpoint()` and `from_checkpoint()`.
Subclasses can override this method with a more efficient implementation."""
Subclasses can override this method with a more efficient implementation.
"""
return self.from_checkpoint(self.checkpoint())
def checkpoint(self) -> Checkpoint | Any:
"""Return a serializable representation of the channel's current state.
Raises `EmptyChannelError` if the channel is empty (never updated yet),
or doesn't support checkpoints."""
or doesn't support checkpoints.
"""
try:
return self.get()
except EmptyChannelError:
@@ -55,7 +60,9 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
@abstractmethod
def from_checkpoint(self, checkpoint: Checkpoint | Any) -> Self:
"""Return a new identical channel, optionally initialized from a checkpoint.
If the checkpoint contains complex data structures, they should be copied."""
If the checkpoint contains complex data structures, they should be copied.
"""
# read methods
@@ -67,6 +74,7 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
def is_available(self) -> bool:
"""Return `True` if the channel is available (not empty), `False` otherwise.
Subclasses should override this method to provide a more efficient
implementation than calling `get()` and catching `EmptyChannelError`.
"""
@@ -83,21 +91,29 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
"""Update the channel's value with the given sequence of updates.
The order of the updates in the sequence is arbitrary.
This method is called by Pregel for all channels at the end of each step.
If there are no updates, it is called with an empty sequence.
Raises `InvalidUpdateError` if the sequence of updates is invalid.
Returns `True` if the channel was updated, `False` otherwise."""
def consume(self) -> bool:
"""Notify the channel that a subscribed task ran. By default, no-op.
A channel can use this method to modify its state, preventing the value
from being consumed again.
"""Notify the channel that a subscribed task ran.
By default, no-op.
A channel can use this method to modify its state, preventing the value from being consumed again.
Returns `True` if the channel was updated, `False` otherwise.
"""
return False
def finish(self) -> bool:
"""Notify the channel that the Pregel run is finishing. By default, no-op.
"""Notify the channel that the Pregel run is finishing.
By default, no-op.
A channel can use this method to modify its state, preventing finish.
Returns `True` if the channel was updated, `False` otherwise.
+9 -9
View File
@@ -32,8 +32,8 @@ def get_config() -> RunnableConfig:
def get_store() -> BaseStore:
"""Access LangGraph store from inside a graph node or entrypoint task at runtime.
Can be called from inside any [StateGraph][langgraph.graph.StateGraph] node or
functional API [task][langgraph.func.task], as long as the StateGraph or the [entrypoint][langgraph.func.entrypoint]
Can be called from inside any [`StateGraph`][langgraph.graph.StateGraph] node or
functional API [`task`][langgraph.func.task], as long as the `StateGraph` or the [`entrypoint`][langgraph.func.entrypoint]
was initialized with a store, e.g.:
```python
@@ -53,10 +53,10 @@ def get_store() -> BaseStore:
!!! warning "Async with Python < 3.11"
If you are using Python < 3.11 and are running LangGraph asynchronously,
`get_store()` won't work since it uses [contextvar](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
`get_store()` won't work since it uses [`contextvar`](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
Example: Using with StateGraph
Example: Using with `StateGraph`
```python
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START
@@ -124,17 +124,17 @@ def get_store() -> BaseStore:
def get_stream_writer() -> StreamWriter:
"""Access LangGraph [StreamWriter][langgraph.types.StreamWriter] from inside a graph node or entrypoint task at runtime.
"""Access LangGraph [`StreamWriter`][langgraph.types.StreamWriter] from inside a graph node or entrypoint task at runtime.
Can be called from inside any [StateGraph][langgraph.graph.StateGraph] node or
functional API [task][langgraph.func.task].
Can be called from inside any [`StateGraph`][langgraph.graph.StateGraph] node or
functional API [`task`][langgraph.func.task].
!!! warning "Async with Python < 3.11"
If you are using Python < 3.11 and are running LangGraph asynchronously,
`get_stream_writer()` won't work since it uses [contextvar](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
`get_stream_writer()` won't work since it uses [`contextvar`](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
Example: Using with StateGraph
Example: Using with `StateGraph`
```python
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START
+5 -5
View File
@@ -151,13 +151,13 @@ def task(
@task
def add_one(a: int) -> int:
def add_one_task(a: int) -> int:
return a + 1
@entrypoint()
def add_one(numbers: list[int]) -> list[int]:
futures = [add_one(n) for n in numbers]
futures = [add_one_task(n) for n in numbers]
results = [f.result() for f in futures]
return results
@@ -173,13 +173,13 @@ def task(
@task
async def add_one(a: int) -> int:
async def add_one_task(a: int) -> int:
return a + 1
@entrypoint()
async def add_one(numbers: list[int]) -> list[int]:
futures = [add_one(n) for n in numbers]
futures = [add_one_task(n) for n in numbers]
return asyncio.gather(*futures)
@@ -357,7 +357,7 @@ class entrypoint(Generic[ContextT]):
my_workflow.invoke("hello", config)
```
Example: Using entrypoint.final to save a value
Example: Using `entrypoint.final` to save a value
The `entrypoint.final` object allows you to return a value while saving
a different value to the checkpoint. This value will be accessible
in the next invocation of the entrypoint via the `previous` parameter, as
+9 -5
View File
@@ -30,6 +30,7 @@ __all__ = (
"add_messages",
"MessagesState",
"MessageGraph",
"REMOVE_ALL_MESSAGES",
)
Messages = list[MessageLikeRepresentation] | MessageLikeRepresentation
@@ -87,8 +88,8 @@ def add_messages(
If a message in `right` has the same ID as a message in `left`, the
message from `right` will replace the message from `left`.
Example:
```python title="Basic usage"
Example: Basic usage
```python
from langchain_core.messages import AIMessage, HumanMessage
msgs1 = [HumanMessage(content="Hello", id="1")]
@@ -97,14 +98,16 @@ def add_messages(
# [HumanMessage(content='Hello', id='1'), AIMessage(content='Hi there!', id='2')]
```
```python title="Overwrite existing message"
Example: Overwrite existing message
```python
msgs1 = [HumanMessage(content="Hello", id="1")]
msgs2 = [HumanMessage(content="Hello again", id="1")]
add_messages(msgs1, msgs2)
# [HumanMessage(content='Hello again', id='1')]
```
```python title="Use in a StateGraph"
Example: Use in a StateGraph
```python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph
@@ -123,7 +126,8 @@ def add_messages(
# {'messages': [AIMessage(content='Hello', id=...)]}
```
```python title="Use OpenAI message format"
Example: Use OpenAI message format
```python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, add_messages
+48 -23
View File
@@ -110,15 +110,24 @@ def _get_node_name(node: StateNode[Any, ContextT]) -> str:
class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
"""A graph whose nodes communicate by reading and writing to a shared state.
The signature of each node is State -> Partial<State>.
The signature of each node is `State -> Partial<State>`.
Each state key can optionally be annotated with a reducer function that
will be used to aggregate the values of that key received from multiple nodes.
The signature of a reducer function is `(Value, Value) -> Value`.
!!! warning
`StateGraph` is a builder class and cannot be used directly for execution.
You must first call `.compile()` to create an executable graph that supports
methods like `invoke()`, `stream()`, `astream()`, and `ainvoke()`. See the
`CompiledStateGraph` documentation for more details.
Args:
state_schema: The schema class that defines the state.
context_schema: The schema class that defines the runtime context.
Use this to expose immutable context data to your nodes, like `user_id`, `db_conn`, etc.
input_schema: The schema class that defines the input to the graph.
output_schema: The schema class that defines the output from the graph.
@@ -289,7 +298,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
destinations: dict[str, str] | tuple[str, ...] | None = None,
**kwargs: Unpack[DeprecatedKwargs],
) -> Self:
"""Add a new node to the state graph, input schema is inferred as the state schema.
"""Add a new node to the `StateGraph`, input schema is inferred as the state schema.
Will take the name of the function/runnable as the node name.
"""
...
@@ -307,7 +316,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
destinations: dict[str, str] | tuple[str, ...] | None = None,
**kwargs: Unpack[DeprecatedKwargs],
) -> Self:
"""Add a new node to the state graph, input schema is specified.
"""Add a new node to the `StateGraph`, input schema is specified.
Will take the name of the function/runnable as the node name.
"""
...
@@ -326,7 +335,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
destinations: dict[str, str] | tuple[str, ...] | None = None,
**kwargs: Unpack[DeprecatedKwargs],
) -> Self:
"""Add a new node to the state graph, input schema is inferred as the state schema."""
"""Add a new node to the `StateGraph`, input schema is inferred as the state schema."""
...
@overload
@@ -343,7 +352,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
destinations: dict[str, str] | tuple[str, ...] | None = None,
**kwargs: Unpack[DeprecatedKwargs],
) -> Self:
"""Add a new node to the state graph, input schema is specified."""
"""Add a new node to the `StateGraph`, input schema is specified."""
...
def add_node(
@@ -359,22 +368,27 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
destinations: dict[str, str] | tuple[str, ...] | None = None,
**kwargs: Unpack[DeprecatedKwargs],
) -> Self:
"""Add a new node to the state graph.
"""Add a new node to the `StateGraph`.
Args:
node: The function or runnable this node will run.
If a string is provided, it will be used as the node name, and action will be used as the function or runnable.
action: The action associated with the node.
Will be used as the node function or runnable if `node` is a string (node name).
defer: Whether to defer the execution of the node until the run is about to end.
metadata: The metadata associated with the node.
input_schema: The input schema for the node. (default: the graph's state schema)
input_schema: The input schema for the node. (Default: the graph's state schema)
retry_policy: The retry policy for the node.
If a sequence is provided, the first matching policy will be applied.
cache_policy: The cache policy for the node.
destinations: Destinations that indicate where a node can route to.
This is useful for edgeless graphs with nodes that return `Command` objects.
Useful for edgeless graphs with nodes that return `Command` objects.
If a `dict` is provided, the keys will be used as the target node names and the values will be used as the labels for the edges.
If a `tuple` is provided, the values will be used as the target node names.
!!! note
@@ -416,7 +430,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
```
Returns:
Self: The instance of the state graph, allowing for method chaining.
Self: The instance of the `StateGraph`, allowing for method chaining.
"""
if (retry := kwargs.get("retry", MISSING)) is not MISSING:
warnings.warn(
@@ -571,7 +585,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
ValueError: If the start key is `'END'` or if the start key or end key is not present in the graph.
Returns:
Self: The instance of the state graph, allowing for method chaining.
Self: The instance of the `StateGraph`, allowing for method chaining.
"""
if self.compiled:
logger.warning(
@@ -623,11 +637,14 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
Args:
source: The starting node. This conditional edge will run when
exiting this node.
path: The callable that determines the next
node or nodes. If not specifying `path_map` it should return one or
more nodes. If it returns `'END'`, the graph will stop execution.
path_map: Optional mapping of paths to node
names. If omitted the paths returned by `path` should be node names.
path: The callable that determines the next node or nodes.
If not specifying `path_map` it should return one or more nodes.
If it returns `'END'`, the graph will stop execution.
path_map: Optional mapping of paths to node names.
If omitted the paths returned by `path` should be node names.
Returns:
Self: The instance of the graph, allowing for method chaining.
@@ -668,7 +685,9 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
Args:
nodes: A sequence of `StateNode` (callables that accept a `state` arg) or `(name, StateNode)` tuples.
If no names are provided, the name will be inferred from the node object (e.g. a `Runnable` or a `Callable` name).
Each node will be executed in the order provided.
Raises:
@@ -676,7 +695,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
ValueError: If the sequence contains duplicate node names.
Returns:
Self: The instance of the state graph, allowing for method chaining.
Self: The instance of the `StateGraph`, allowing for method chaining.
"""
if len(nodes) < 1:
raise ValueError("Sequence requires at least one node.")
@@ -725,11 +744,14 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
"""Sets a conditional entry point in the graph.
Args:
path: The callable that determines the next
node or nodes. If not specifying `path_map` it should return one or
more nodes. If it returns END, the graph will stop execution.
path_map: Optional mapping of paths to node
names. If omitted the paths returned by `path` should be node names.
path: The callable that determines the next node or nodes.
If not specifying `path_map` it should return one or more nodes.
If it returns END, the graph will stop execution.
path_map: Optional mapping of paths to node names.
If omitted the paths returned by `path` should be node names.
Returns:
Self: The instance of the graph, allowing for method chaining.
@@ -809,16 +831,19 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
debug: bool = False,
name: str | None = None,
) -> CompiledStateGraph[StateT, ContextT, InputT, OutputT]:
"""Compiles the state graph into a `CompiledStateGraph` object.
"""Compiles the `StateGraph` into a `CompiledStateGraph` object.
The compiled graph implements the `Runnable` interface and can be invoked,
streamed, batched, and run asynchronously.
Args:
checkpointer: A checkpoint saver object or flag.
If provided, this `Checkpointer` serves as a fully versioned "short-term memory" for the graph,
allowing it to be paused, resumed, and replayed from any point.
If `None`, it may inherit the parent graph's checkpointer when used as a subgraph.
If `False`, it will not use or inherit any checkpointer.
interrupt_before: An optional list of node names to interrupt before.
interrupt_after: An optional list of node names to interrupt after.
@@ -826,7 +851,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
name: The name to use for the compiled graph.
Returns:
CompiledStateGraph: The compiled state graph.
CompiledStateGraph: The compiled `StateGraph`.
"""
# assign default values
interrupt_before = interrupt_before or []
+1 -2
View File
@@ -81,9 +81,8 @@ def push_ui_message(
metadata: Optional additional metadata about the UI message.
message: Optional message object to associate with the UI message.
state_key: Key in the graph state where the UI messages are stored.
Defaults to "ui".
merge: Whether to merge props with existing UI message (True) or replace
them (False). Defaults to False.
them (False).
Returns:
The created UI message.
+352 -250
View File
@@ -258,9 +258,11 @@ def apply_writes(
next_version = None
else:
next_version = get_next_version(
max(checkpoint["channel_versions"].values())
if checkpoint["channel_versions"]
else None,
(
max(checkpoint["channel_versions"].values())
if checkpoint["channel_versions"]
else None
),
None,
)
@@ -491,6 +493,11 @@ def prepare_next_tasks(
PUSH_TRIGGER = (PUSH,)
class _TaskIDFn(Protocol):
def __call__(self, namespace: bytes, *parts: str | bytes) -> str:
pass
def prepare_single_task(
task_path: tuple[Any, ...],
task_id_checksum: str | None,
@@ -520,250 +527,50 @@ def prepare_single_task(
task_id_func = _xxhash_str if checkpoint["v"] > 1 else _uuid5_str
if task_path[0] == PUSH and isinstance(task_path[-1], Call):
# (PUSH, parent task path, idx of PUSH write, id of parent task, Call)
task_path_t = cast(tuple[str, tuple, int, str, Call], task_path)
call = task_path_t[-1]
proc_ = get_runnable_for_task(call.func)
name = proc_.name
if name is None:
raise ValueError("`call` functions must have a `__name__` attribute")
# create task id
triggers: Sequence[str] = PUSH_TRIGGER
checkpoint_ns = f"{parent_ns}{NS_SEP}{name}" if parent_ns else name
task_id = task_id_func(
checkpoint_id_bytes,
checkpoint_ns,
str(step),
name,
PUSH,
task_path_str(task_path[1]),
str(task_path[2]),
return prepare_push_task_functional(
cast(tuple[str, tuple, int, str, Call], task_path),
task_id_checksum,
checkpoint=checkpoint,
checkpoint_id_bytes=checkpoint_id_bytes,
pending_writes=pending_writes,
channels=channels,
managed=managed,
config=config,
step=step,
stop=stop,
for_execution=for_execution,
store=store,
checkpointer=checkpointer,
manager=manager,
cache_policy=cache_policy,
retry_policy=retry_policy,
parent_ns=parent_ns,
task_id_func=task_id_func,
)
elif task_path[0] == PUSH:
return prepare_push_task_send(
cast(tuple[str, tuple], task_path),
task_id_checksum,
checkpoint=checkpoint,
checkpoint_id_bytes=checkpoint_id_bytes,
pending_writes=pending_writes,
channels=channels,
managed=managed,
config=config,
step=step,
processes=processes,
stop=stop,
for_execution=for_execution,
store=store,
checkpointer=checkpointer,
manager=manager,
cache_policy=cache_policy,
retry_policy=retry_policy,
parent_ns=parent_ns,
task_id_func=task_id_func,
)
task_checkpoint_ns = f"{checkpoint_ns}:{task_id}"
# we append True to the task path to indicate that a call is being
# made, so we should not return interrupts from this task (responsibility lies with the parent)
task_path = (*task_path[:3], True)
metadata = {
"langgraph_step": step,
"langgraph_node": name,
"langgraph_triggers": triggers,
"langgraph_path": task_path,
"langgraph_checkpoint_ns": task_checkpoint_ns,
}
if task_id_checksum is not None:
assert task_id == task_id_checksum, f"{task_id} != {task_id_checksum}"
if for_execution:
writes: deque[tuple[str, Any]] = deque()
cache_policy = call.cache_policy or cache_policy
if cache_policy:
args_key = cache_policy.key_func(*call.input[0], **call.input[1])
cache_key: CacheKey | None = CacheKey(
(
CACHE_NS_WRITES,
(identifier(call.func) or "__dynamic__"),
),
xxh3_128_hexdigest(
args_key.encode() if isinstance(args_key, str) else args_key,
),
cache_policy.ttl,
)
else:
cache_key = None
scratchpad = _scratchpad(
config[CONF].get(CONFIG_KEY_SCRATCHPAD),
pending_writes,
task_id,
xxh3_128_hexdigest(task_checkpoint_ns.encode()),
config[CONF].get(CONFIG_KEY_RESUME_MAP),
step,
stop,
)
runtime = cast(
Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME)
)
runtime = runtime.override(store=store)
return PregelExecutableTask(
name,
call.input,
proc_,
writes,
patch_config(
merge_configs(config, {"metadata": metadata}),
run_name=name,
callbacks=call.callbacks
or (manager.get_child(f"graph:step:{step}") if manager else None),
configurable={
CONFIG_KEY_TASK_ID: task_id,
# deque.extend is thread-safe
CONFIG_KEY_SEND: writes.extend,
CONFIG_KEY_READ: partial(
local_read,
scratchpad,
channels,
managed,
PregelTaskWrites(task_path, name, writes, triggers),
),
CONFIG_KEY_CHECKPOINTER: (
checkpointer or configurable.get(CONFIG_KEY_CHECKPOINTER)
),
CONFIG_KEY_CHECKPOINT_MAP: {
**configurable.get(CONFIG_KEY_CHECKPOINT_MAP, {}),
parent_ns: checkpoint["id"],
},
CONFIG_KEY_CHECKPOINT_ID: None,
CONFIG_KEY_CHECKPOINT_NS: task_checkpoint_ns,
CONFIG_KEY_SCRATCHPAD: scratchpad,
CONFIG_KEY_RUNTIME: runtime,
},
),
triggers,
call.retry_policy or retry_policy,
cache_key,
task_id,
task_path,
)
else:
return PregelTask(task_id, name, task_path)
elif task_path[0] == PUSH:
if len(task_path) == 2:
# SEND tasks, executed in superstep n+1
# (PUSH, idx of pending send)
idx = cast(int, task_path[1])
if not channels[TASKS].is_available():
return
sends: Sequence[Send] = channels[TASKS].get()
if idx < 0 or idx >= len(sends):
return
packet = sends[idx]
if not isinstance(packet, Send):
logger.warning(
f"Ignoring invalid packet type {type(packet)} in pending sends"
)
return
if packet.node not in processes:
logger.warning(
f"Ignoring unknown node name {packet.node} in pending sends"
)
return
# find process
proc = processes[packet.node]
proc_node = proc.node
if proc_node is None:
return
# create task id
triggers = PUSH_TRIGGER
checkpoint_ns = (
f"{parent_ns}{NS_SEP}{packet.node}" if parent_ns else packet.node
)
task_id = task_id_func(
checkpoint_id_bytes,
checkpoint_ns,
str(step),
packet.node,
PUSH,
str(idx),
)
else:
logger.warning(f"Ignoring invalid PUSH task path {task_path}")
return
task_checkpoint_ns = f"{checkpoint_ns}:{task_id}"
# we append False to the task path to indicate that a call is not being made
# so we should return interrupts from this task
task_path = (*task_path[:3], False)
metadata = {
"langgraph_step": step,
"langgraph_node": packet.node,
"langgraph_triggers": triggers,
"langgraph_path": task_path,
"langgraph_checkpoint_ns": task_checkpoint_ns,
}
if task_id_checksum is not None:
assert task_id == task_id_checksum, f"{task_id} != {task_id_checksum}"
if for_execution:
if proc.metadata:
metadata.update(proc.metadata)
writes = deque()
cache_policy = proc.cache_policy or cache_policy
if cache_policy:
args_key = cache_policy.key_func(packet.arg)
cache_key = CacheKey(
(
CACHE_NS_WRITES,
(identifier(proc) or "__dynamic__"),
packet.node,
),
xxh3_128_hexdigest(
args_key.encode() if isinstance(args_key, str) else args_key,
),
cache_policy.ttl,
)
else:
cache_key = None
scratchpad = _scratchpad(
config[CONF].get(CONFIG_KEY_SCRATCHPAD),
pending_writes,
task_id,
xxh3_128_hexdigest(task_checkpoint_ns.encode()),
config[CONF].get(CONFIG_KEY_RESUME_MAP),
step,
stop,
)
runtime = cast(
Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME)
)
runtime = runtime.override(
store=store, previous=checkpoint["channel_values"].get(PREVIOUS, None)
)
additional_config: RunnableConfig = {
"metadata": metadata,
"tags": proc.tags,
}
return PregelExecutableTask(
packet.node,
packet.arg,
proc_node,
writes,
patch_config(
merge_configs(config, additional_config),
run_name=packet.node,
callbacks=(
manager.get_child(f"graph:step:{step}") if manager else None
),
configurable={
CONFIG_KEY_TASK_ID: task_id,
# deque.extend is thread-safe
CONFIG_KEY_SEND: writes.extend,
CONFIG_KEY_READ: partial(
local_read,
scratchpad,
channels,
managed,
PregelTaskWrites(task_path, packet.node, writes, triggers),
),
CONFIG_KEY_CHECKPOINTER: (
checkpointer or configurable.get(CONFIG_KEY_CHECKPOINTER)
),
CONFIG_KEY_CHECKPOINT_MAP: {
**configurable.get(CONFIG_KEY_CHECKPOINT_MAP, {}),
parent_ns: checkpoint["id"],
},
CONFIG_KEY_CHECKPOINT_ID: None,
CONFIG_KEY_CHECKPOINT_NS: task_checkpoint_ns,
CONFIG_KEY_SCRATCHPAD: scratchpad,
CONFIG_KEY_RUNTIME: runtime,
},
),
triggers,
proc.retry_policy or retry_policy,
cache_key,
task_id,
task_path,
writers=proc.flat_writers,
subgraphs=proc.subgraphs,
)
else:
return PregelTask(task_id, packet.node, task_path)
elif task_path[0] == PULL:
# (PULL, node name)
name = cast(str, task_path[1])
@@ -834,7 +641,7 @@ def prepare_single_task(
if node := proc.node:
if proc.metadata:
metadata.update(proc.metadata)
writes = deque()
writes: deque[tuple[str, Any]] = deque()
cache_policy = proc.cache_policy or cache_policy
if cache_policy:
args_key = cache_policy.key_func(val)
@@ -845,9 +652,11 @@ def prepare_single_task(
name,
),
xxh3_128_hexdigest(
args_key.encode()
if isinstance(args_key, str)
else args_key,
(
args_key.encode()
if isinstance(args_key, str)
else args_key
),
),
cache_policy.ttl,
)
@@ -870,7 +679,9 @@ def prepare_single_task(
node,
writes,
patch_config(
merge_configs(config, additional_config),
merge_configs(
config, cast(RunnableConfig, additional_config)
),
run_name=name,
callbacks=(
manager.get_child(f"graph:step:{step}")
@@ -919,6 +730,297 @@ def prepare_single_task(
return PregelTask(task_id, name, task_path[:3])
def prepare_push_task_functional(
task_path: tuple[str, tuple, int, str, Call],
# (PUSH, parent task path, idx of PUSH write, id of parent task, Call)
task_id_checksum: str | None,
*,
checkpoint: Checkpoint,
checkpoint_id_bytes: bytes,
pending_writes: list[PendingWrite],
channels: Mapping[str, BaseChannel],
managed: ManagedValueMapping,
config: RunnableConfig,
step: int,
stop: int,
for_execution: bool,
store: BaseStore | None = None,
checkpointer: BaseCheckpointSaver | None = None,
manager: None | ParentRunManager | AsyncParentRunManager = None,
cache_policy: CachePolicy | None = None,
retry_policy: Sequence[RetryPolicy] = (),
parent_ns: str,
# namespace: bytes, *parts: str | bytes
task_id_func: _TaskIDFn,
) -> PregelTask | PregelExecutableTask:
"""Prepare a push task with an attached caller. Used for the functional API."""
configurable = config.get(CONF, {})
call = task_path[-1]
proc_ = get_runnable_for_task(call.func)
name = proc_.name
if name is None:
raise ValueError("`call` functions must have a `__name__` attribute")
# create task id
triggers: Sequence[str] = PUSH_TRIGGER
checkpoint_ns = f"{parent_ns}{NS_SEP}{name}" if parent_ns else name
task_id = task_id_func(
checkpoint_id_bytes,
checkpoint_ns,
str(step),
name,
PUSH,
task_path_str(task_path[1]),
str(task_path[2]),
)
task_checkpoint_ns = f"{checkpoint_ns}:{task_id}"
# we append True to the task path to indicate that a call is being
# made, so we should not return interrupts from this task (responsibility lies with the parent)
in_progress_task_path = (*task_path[:3], True)
metadata = {
"langgraph_step": step,
"langgraph_node": name,
"langgraph_triggers": triggers,
"langgraph_path": in_progress_task_path,
"langgraph_checkpoint_ns": task_checkpoint_ns,
}
if task_id_checksum is not None:
assert task_id == task_id_checksum, f"{task_id} != {task_id_checksum}"
if for_execution:
writes: deque[tuple[str, Any]] = deque()
cache_policy = call.cache_policy or cache_policy
if cache_policy:
args_key = cache_policy.key_func(*call.input[0], **call.input[1])
cache_key: CacheKey | None = CacheKey(
(
CACHE_NS_WRITES,
(identifier(call.func) or "__dynamic__"),
),
xxh3_128_hexdigest(
args_key.encode() if isinstance(args_key, str) else args_key,
),
cache_policy.ttl,
)
else:
cache_key = None
scratchpad = _scratchpad(
configurable.get(CONFIG_KEY_SCRATCHPAD),
pending_writes,
task_id,
xxh3_128_hexdigest(task_checkpoint_ns.encode()),
configurable.get(CONFIG_KEY_RESUME_MAP),
step,
stop,
)
runtime = cast(Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME))
runtime = runtime.override(store=store)
return PregelExecutableTask(
name,
call.input,
proc_,
writes,
patch_config(
merge_configs(config, {"metadata": metadata}),
run_name=name,
callbacks=call.callbacks
or (manager.get_child(f"graph:step:{step}") if manager else None),
configurable={
CONFIG_KEY_TASK_ID: task_id,
# deque.extend is thread-safe
CONFIG_KEY_SEND: writes.extend,
CONFIG_KEY_READ: partial(
local_read,
scratchpad,
channels,
managed,
PregelTaskWrites(in_progress_task_path, name, writes, triggers),
),
CONFIG_KEY_CHECKPOINTER: (
checkpointer or configurable.get(CONFIG_KEY_CHECKPOINTER)
),
CONFIG_KEY_CHECKPOINT_MAP: {
**configurable.get(CONFIG_KEY_CHECKPOINT_MAP, {}),
parent_ns: checkpoint["id"],
},
CONFIG_KEY_CHECKPOINT_ID: None,
CONFIG_KEY_CHECKPOINT_NS: task_checkpoint_ns,
CONFIG_KEY_SCRATCHPAD: scratchpad,
CONFIG_KEY_RUNTIME: runtime,
},
),
triggers,
call.retry_policy or retry_policy,
cache_key,
task_id,
in_progress_task_path,
)
else:
return PregelTask(task_id, name, in_progress_task_path)
def prepare_push_task_send(
task_path: tuple[str, tuple],
# (PUSH, parent task path)
task_id_checksum: str | None,
*,
checkpoint: Checkpoint,
checkpoint_id_bytes: bytes,
pending_writes: list[PendingWrite],
channels: Mapping[str, BaseChannel],
managed: ManagedValueMapping,
config: RunnableConfig,
step: int,
stop: int,
for_execution: bool,
store: BaseStore | None = None,
checkpointer: BaseCheckpointSaver | None = None,
manager: None | ParentRunManager | AsyncParentRunManager = None,
cache_policy: CachePolicy | None = None,
retry_policy: Sequence[RetryPolicy] = (),
parent_ns: str,
task_id_func: _TaskIDFn,
processes: Mapping[str, PregelNode],
) -> PregelTask | PregelExecutableTask | None:
if len(task_path) == 2:
# SEND tasks, executed in superstep n+1
# (PUSH, idx of pending send)
idx = cast(int, task_path[1])
if not channels[TASKS].is_available():
return
sends: Sequence[Send] = channels[TASKS].get()
if idx < 0 or idx >= len(sends):
return
packet = sends[idx]
if not isinstance(packet, Send):
logger.warning(
f"Ignoring invalid packet type {type(packet)} in pending sends"
)
return
if packet.node not in processes:
logger.warning(f"Ignoring unknown node name {packet.node} in pending sends")
return
# find process
proc = processes[packet.node]
proc_node = proc.node
if proc_node is None:
return
# create task id
triggers = PUSH_TRIGGER
checkpoint_ns = (
f"{parent_ns}{NS_SEP}{packet.node}" if parent_ns else packet.node
)
task_id = task_id_func(
checkpoint_id_bytes,
checkpoint_ns,
str(step),
packet.node,
PUSH,
str(idx),
)
else:
logger.warning(f"Ignoring invalid PUSH task path {task_path}")
return
configurable = config.get(CONF, {})
task_checkpoint_ns = f"{checkpoint_ns}:{task_id}"
# we append False to the task path to indicate that a call is not being made
# so we should return interrupts from this task
translated_task_path = (*task_path[:3], False)
metadata = {
"langgraph_step": step,
"langgraph_node": packet.node,
"langgraph_triggers": triggers,
"langgraph_path": translated_task_path,
"langgraph_checkpoint_ns": task_checkpoint_ns,
}
if task_id_checksum is not None:
assert task_id == task_id_checksum, f"{task_id} != {task_id_checksum}"
if for_execution:
if proc.metadata:
metadata.update(proc.metadata)
writes: deque[tuple[str, Any]] = deque()
cache_policy = proc.cache_policy or cache_policy
if cache_policy:
args_key = cache_policy.key_func(packet.arg)
cache_key = CacheKey(
(
CACHE_NS_WRITES,
(identifier(proc) or "__dynamic__"),
packet.node,
),
xxh3_128_hexdigest(
args_key.encode() if isinstance(args_key, str) else args_key,
),
cache_policy.ttl,
)
else:
cache_key = None
scratchpad = _scratchpad(
config[CONF].get(CONFIG_KEY_SCRATCHPAD),
pending_writes,
task_id,
xxh3_128_hexdigest(task_checkpoint_ns.encode()),
config[CONF].get(CONFIG_KEY_RESUME_MAP),
step,
stop,
)
runtime = cast(Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME))
runtime = runtime.override(
store=store, previous=checkpoint["channel_values"].get(PREVIOUS, None)
)
additional_config: RunnableConfig = {
"metadata": metadata,
"tags": proc.tags,
}
return PregelExecutableTask(
packet.node,
packet.arg,
proc_node,
writes,
patch_config(
merge_configs(config, additional_config),
run_name=packet.node,
callbacks=(
manager.get_child(f"graph:step:{step}") if manager else None
),
configurable={
CONFIG_KEY_TASK_ID: task_id,
# deque.extend is thread-safe
CONFIG_KEY_SEND: writes.extend,
CONFIG_KEY_READ: partial(
local_read,
scratchpad,
channels,
managed,
PregelTaskWrites(
translated_task_path, packet.node, writes, triggers
),
),
CONFIG_KEY_CHECKPOINTER: (
checkpointer or configurable.get(CONFIG_KEY_CHECKPOINTER)
),
CONFIG_KEY_CHECKPOINT_MAP: {
**configurable.get(CONFIG_KEY_CHECKPOINT_MAP, {}),
parent_ns: checkpoint["id"],
},
CONFIG_KEY_CHECKPOINT_ID: None,
CONFIG_KEY_CHECKPOINT_NS: task_checkpoint_ns,
CONFIG_KEY_SCRATCHPAD: scratchpad,
CONFIG_KEY_RUNTIME: runtime,
},
),
triggers,
proc.retry_policy or retry_policy,
cache_key,
task_id,
translated_task_path,
writers=proc.flat_writers,
subgraphs=proc.subgraphs,
)
else:
return PregelTask(task_id, packet.node, translated_task_path)
def checkpoint_null_version(
checkpoint: Checkpoint,
) -> V | None:
+10 -5
View File
@@ -459,9 +459,6 @@ class PregelLoop:
def tick(self) -> bool:
"""Execute a single iteration of the Pregel loop.
Args:
input_keys: The key(s) to read input from.
Returns:
True if more iterations are needed.
"""
@@ -769,6 +766,7 @@ class PregelLoop:
]
self.checkpoint["channel_values"][TASKS] = sanitized_tasks
# bail if no checkpointer
if do_checkpoint and self._checkpointer_put_after_previous is not None:
self.prev_checkpoint_config = (
self.checkpoint_config
@@ -938,8 +936,15 @@ class PregelLoop:
stream_modes = self.stream.modes if self.stream else []
if "updates" in stream_modes:
self._emit("updates", lambda: iter(interrupts))
elif "values" in stream_modes:
self._emit("values", lambda: iter(interrupts))
if "values" in stream_modes:
current_values = read_channels(self.channels, self.output_keys)
# self.output_keys is a sequence, stream chunk contains entire state and interrupts
if isinstance(current_values, dict):
current_values[INTERRUPT] = interrupts[0][INTERRUPT]
self._emit("values", lambda: iter([current_values]))
# self.output_keys is a string, stream chunk contains only interrupts
else:
self._emit("values", lambda: iter(interrupts))
elif writes[0][0] != ERROR:
self._emit(
"updates",
+67 -91
View File
@@ -210,8 +210,10 @@ class NodeBuilder:
*channels: str,
read: bool = True,
) -> Self:
"""Add channels to subscribe to. Node will be invoked when any of these
channels are updated, with a dict of the channel values as input.
"""Add channels to subscribe to.
Node will be invoked when any of these channels are updated, with a dict of the
channel values as input.
Args:
channels: Channel name(s) to subscribe to
@@ -270,8 +272,8 @@ class NodeBuilder:
"""Add channel writes.
Args:
*channels: Channel names to write to
**kwargs: Channel name and value mappings
*channels: Channel names to write to.
**kwargs: Channel name and value mappings.
Returns:
Self for chaining
@@ -375,12 +377,12 @@ class Pregel(
### Advanced channels: Context and BinaryOperatorAggregate
- `Context`: exposes the value of a context manager, managing its lifecycle.
Useful for accessing external resources that require setup and/or teardown. eg.
`client = Context(httpx.Client)`
Useful for accessing external resources that require setup and/or teardown. e.g.
`client = Context(httpx.Client)`
- `BinaryOperatorAggregate`: stores a persistent value, updated by applying
a binary operator to the current value and each update
sent to the channel, useful for computing aggregates over multiple steps. eg.
`total = BinaryOperatorAggregate(int, operator.add)`
a binary operator to the current value and each update
sent to the channel, useful for computing aggregates over multiple steps. e.g.
`total = BinaryOperatorAggregate(int, operator.add)`
## Examples
@@ -495,7 +497,7 @@ class Pregel(
{"c": ["foofoo", "foofoofoofoo"]}
```
Example: Using a BinaryOperatorAggregate channel
Example: Using a `BinaryOperatorAggregate` channel
```python
from langgraph.channels import EphemeralValue, BinaryOperatorAggregate
from langgraph.pregel import Pregel, NodeBuilder
@@ -541,8 +543,9 @@ class Pregel(
Example: Introducing a cycle
This example demonstrates how to introduce a cycle in the graph, by having
a chain write to a channel it subscribes to. Execution will continue
until a None value is written to the channel.
a chain write to a channel it subscribes to.
Execution will continue until a `None` value is written to the channel.
```python
from langgraph.channels import EphemeralValue
@@ -1426,7 +1429,8 @@ class Pregel(
Args:
config: The config to apply the updates to.
supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state.
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
Raises:
ValueError: If no checkpointer is set or no updates are provided.
@@ -1702,7 +1706,9 @@ class Pregel(
# we use the task id generated by prepare_next_tasks
node_to_task_ids: dict[str, deque[str]] = defaultdict(deque)
if saved is not None and saved.pending_writes is not None:
# tasks for this checkpoint
# we call prepare_next_tasks to discover the task IDs that
# would have been generated, so we can reuse them and
# properly populate task.result in state history
next_tasks = prepare_next_tasks(
checkpoint,
saved.pending_writes,
@@ -1721,32 +1727,6 @@ class Pregel(
for t in next_tasks.values():
node_to_task_ids[t.name].append(t.id)
# apply null writes
if null_writes := [
w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID
]:
apply_writes(
checkpoint,
channels,
[PregelTaskWrites((), INPUT, null_writes, [])],
checkpointer.get_next_version,
self.trigger_to_nodes,
)
# apply writes
for tid, k, v in saved.pending_writes:
if k in (ERROR, INTERRUPT):
continue
if tid not in next_tasks:
continue
next_tasks[tid].writes.append((k, v))
if tasks := [t for t in next_tasks.values() if t.writes]:
apply_writes(
checkpoint,
channels,
tasks,
checkpointer.get_next_version,
self.trigger_to_nodes,
)
valid_updates: list[tuple[str, dict[str, Any] | None, str | None]] = []
if len(updates) == 1:
values, as_node, task_id = updates[0]
@@ -1893,7 +1873,8 @@ class Pregel(
Args:
config: The config to apply the updates to.
supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state.
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
Raises:
ValueError: If no checkpointer is set or no updates are provided.
@@ -2167,7 +2148,9 @@ class Pregel(
# we use the task id generated by prepare_next_tasks
node_to_task_ids: dict[str, deque[str]] = defaultdict(deque)
if saved is not None and saved.pending_writes is not None:
# tasks for this checkpoint
# we call prepare_next_tasks to discover the task IDs that
# would have been generated, so we can reuse them and
# properly populate task.result in state history
next_tasks = prepare_next_tasks(
checkpoint,
saved.pending_writes,
@@ -2186,31 +2169,6 @@ class Pregel(
for t in next_tasks.values():
node_to_task_ids[t.name].append(t.id)
# apply null writes
if null_writes := [
w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID
]:
apply_writes(
checkpoint,
channels,
[PregelTaskWrites((), INPUT, null_writes, [])],
checkpointer.get_next_version,
self.trigger_to_nodes,
)
for tid, k, v in saved.pending_writes:
if k in (ERROR, INTERRUPT):
continue
if tid not in next_tasks:
continue
next_tasks[tid].writes.append((k, v))
if tasks := [t for t in next_tasks.values() if t.writes]:
apply_writes(
checkpoint,
channels,
tasks,
checkpointer.get_next_version,
self.trigger_to_nodes,
)
valid_updates: list[tuple[str, dict[str, Any] | None, str | None]] = []
if len(updates) == 1:
values, as_node, task_id = updates[0]
@@ -2401,7 +2359,7 @@ class Pregel(
validate_keys(output_keys, self.channels)
interrupt_before = interrupt_before or self.interrupt_before_nodes
interrupt_after = interrupt_after or self.interrupt_after_nodes
if not isinstance(stream_mode, list):
if isinstance(stream_mode, str):
stream_modes = {stream_mode}
else:
stream_modes = set(stream_mode)
@@ -2467,6 +2425,7 @@ class Pregel(
context: The static context to use for the run.
!!! version-added "Added in version 0.6.0"
stream_mode: The mode to stream output, defaults to `self.stream_mode`.
Options are:
- `"values"`: Emit all values in the state after each step, including interrupts.
@@ -2475,31 +2434,36 @@ class Pregel(
If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately.
- `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`.
- `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks.
Will be emitted as 2-tuples `(LLM token, metadata)`.
- Will be emitted as 2-tuples `(LLM token, metadata)`.
- `"checkpoints"`: Emit an event when a checkpoint is created, in the same format as returned by `get_state()`.
- `"tasks"`: Emit events when tasks start and finish, including their results and errors.
- `"debug"`: Emit debug events with as much information as possible for each step.
You can pass a list as the `stream_mode` parameter to stream multiple modes at once.
The streamed outputs will be tuples of `(mode, data)`.
See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
See [LangGraph streaming guide](https://docs.langchain.com/oss/python/langgraph/streaming) for more details.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
Does not affect the output of the graph in any way.
output_keys: The keys to stream, defaults to all non-context channels.
interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph.
interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph.
durability: The durability mode for the graph execution, defaults to `"async"`.
Options are:
- `"sync"`: Changes are persisted synchronously before the next step starts.
- `"async"`: Changes are persisted asynchronously while the next step executes.
- `"exit"`: Changes are persisted only when the graph exits.
subgraphs: Whether to stream events from inside subgraphs, defaults to False.
subgraphs: Whether to stream events from inside subgraphs, defaults to `False`.
If `True`, the events will be emitted as tuples `(namespace, data)`,
or `(namespace, mode, data)` if `stream_mode` is a list,
where `namespace` is a tuple with the path to the node where a subgraph is invoked,
e.g. `("parent_node:<task_id>", "child_node:<task_id>")`.
See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details.
See [LangGraph streaming guide](https://docs.langchain.com/oss/python/langgraph/streaming) for more details.
Yields:
The output of each step in the graph. The output shape depends on the `stream_mode`.
@@ -2735,6 +2699,7 @@ class Pregel(
context: The static context to use for the run.
!!! version-added "Added in version 0.6.0"
stream_mode: The mode to stream output, defaults to `self.stream_mode`.
Options are:
- `"values"`: Emit all values in the state after each step, including interrupts.
@@ -2743,30 +2708,36 @@ class Pregel(
If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately.
- `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`.
- `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks.
Will be emitted as 2-tuples `(LLM token, metadata)`.
- Will be emitted as 2-tuples `(LLM token, metadata)`.
- `"checkpoints"`: Emit an event when a checkpoint is created, in the same format as returned by `get_state()`.
- `"tasks"`: Emit events when tasks start and finish, including their results and errors.
- `"debug"`: Emit debug events with as much information as possible for each step.
You can pass a list as the `stream_mode` parameter to stream multiple modes at once.
The streamed outputs will be tuples of `(mode, data)`.
See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
See [LangGraph streaming guide](https://docs.langchain.com/oss/python/langgraph/streaming) for more details.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
Does not affect the output of the graph in any way.
output_keys: The keys to stream, defaults to all non-context channels.
interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph.
interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph.
durability: The durability mode for the graph execution, defaults to `"async"`.
Options are:
- `"sync"`: Changes are persisted synchronously before the next step starts.
- `"async"`: Changes are persisted asynchronously while the next step executes.
- `"exit"`: Changes are persisted only when the graph exits.
subgraphs: Whether to stream events from inside subgraphs, defaults to False.
subgraphs: Whether to stream events from inside subgraphs, defaults to `False`.
If `True`, the events will be emitted as tuples `(namespace, data)`,
or `(namespace, mode, data)` if `stream_mode` is a list,
where `namespace` is a tuple with the path to the node where a subgraph is invoked,
e.g. `("parent_node:<task_id>", "child_node:<task_id>")`.
See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details.
See [LangGraph streaming guide](https://docs.langchain.com/oss/python/langgraph/streaming) for more details.
Yields:
The output of each step in the graph. The output shape depends on the `stream_mode`.
@@ -3069,11 +3040,14 @@ class Pregel(
context: The static context to use for the run.
!!! version-added "Added in version 0.6.0"
stream_mode: The stream mode for the graph run.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
Does not affect the output of the graph in any way.
output_keys: The output keys to retrieve from the graph run.
interrupt_before: The nodes to interrupt the graph run before.
interrupt_after: The nodes to interrupt the graph run after.
durability: The durability mode for the graph execution, defaults to `"async"`.
Options are:
- `"sync"`: Changes are persisted synchronously before the next step starts.
@@ -3148,31 +3122,33 @@ class Pregel(
durability: Durability | None = None,
**kwargs: Any,
) -> dict[str, Any] | Any:
"""Asynchronously invoke the graph on a single input.
"""Asynchronously run the graph with a single input and config.
Args:
input: The input data for the computation. It can be a dictionary or any other type.
config: The configuration for the computation.
input: The input data for the graph. It can be a dictionary or any other type.
config: The configuration for the graph run.
context: The static context to use for the run.
!!! version-added "Added in version 0.6.0"
stream_mode: The stream mode for the computation.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
output_keys: The output keys to include in the result.
interrupt_before: The nodes to interrupt before.
interrupt_after: The nodes to interrupt after.
stream_mode: The stream mode for the graph run.
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
Does not affect the output of the graph in any way.
output_keys: The output keys to retrieve from the graph run.
interrupt_before: The nodes to interrupt the graph run before.
interrupt_after: The nodes to interrupt the graph run after.
durability: The durability mode for the graph execution, defaults to `"async"`.
Options are:
- `"sync"`: Changes are persisted synchronously before the next step starts.
- `"async"`: Changes are persisted asynchronously while the next step executes.
- `"exit"`: Changes are persisted only when the graph exits.
**kwargs: Additional keyword arguments.
**kwargs: Additional keyword arguments to pass to the graph run.
Returns:
The result of the computation. If `stream_mode` is `"values"`, it returns the latest value.
If `stream_mode` is `"chunks"`, it returns a list of chunks.
The output of the graph run. If `stream_mode` is `"values"`, it returns the latest output.
If `stream_mode` is not `"values"`, it returns a list of output chunks.
"""
output_keys = output_keys if output_keys is not None else self.output_channels
latest: dict[str, Any] | Any = None
+4 -2
View File
@@ -725,9 +725,10 @@ class RemoteGraph(PregelProtocol):
input = None
else:
command = None
thread_id = sanitized_config.get("configurable", {}).pop("thread_id", None)
for chunk in sync_client.runs.stream(
thread_id=sanitized_config["configurable"].get("thread_id"),
thread_id=thread_id,
assistant_id=self.assistant_id,
input=input,
command=command,
@@ -834,9 +835,10 @@ class RemoteGraph(PregelProtocol):
input = None
else:
command = None
thread_id = sanitized_config.get("configurable", {}).pop("thread_id", None)
async for chunk in client.runs.stream(
thread_id=sanitized_config["configurable"].get("thread_id"),
thread_id=thread_id,
assistant_id=self.assistant_id,
input=input,
command=command,
+1 -1
View File
@@ -111,7 +111,7 @@ class Runtime(Generic[ContextT]):
stream_writer=other.stream_writer
if other.stream_writer is not _no_op_stream_writer
else self.stream_writer,
previous=other.previous or self.previous,
previous=self.previous if other.previous is None else other.previous,
)
def override(
+64 -54
View File
@@ -55,6 +55,7 @@ __all__ = (
"Command",
"Durability",
"interrupt",
"Overwrite",
)
Durability = Literal["sync", "async", "exit"]
@@ -283,26 +284,32 @@ class Send:
node (str): The name of the target node to send the message to.
arg (Any): The state or message to send to the target node.
Examples:
>>> from typing import Annotated
>>> import operator
>>> class OverallState(TypedDict):
... subjects: list[str]
... jokes: Annotated[list[str], operator.add]
>>> from langgraph.types import Send
>>> from langgraph.graph import END, START
>>> def continue_to_jokes(state: OverallState):
... return [Send("generate_joke", {"subject": s}) for s in state["subjects"]]
>>> from langgraph.graph import StateGraph
>>> builder = StateGraph(OverallState)
>>> builder.add_node("generate_joke", lambda state: {"jokes": [f"Joke about {state['subject']}"]})
>>> builder.add_conditional_edges(START, continue_to_jokes)
>>> builder.add_edge("generate_joke", END)
>>> graph = builder.compile()
>>>
>>> # Invoking with two subjects results in a generated joke for each
>>> graph.invoke({"subjects": ["cats", "dogs"]})
{'subjects': ['cats', 'dogs'], 'jokes': ['Joke about cats', 'Joke about dogs']}
!!! example
```python
from typing import Annotated
from langgraph.types import Send
from langgraph.graph import END, START
from langgraph.graph import StateGraph
import operator
class OverallState(TypedDict):
subjects: list[str]
jokes: Annotated[list[str], operator.add]
def continue_to_jokes(state: OverallState):
return [Send("generate_joke", {"subject": s}) for s in state["subjects"]]
builder = StateGraph(OverallState)
builder.add_node("generate_joke", lambda state: {"jokes": [f"Joke about {state['subject']}"]})
builder.add_conditional_edges(START, continue_to_jokes)
builder.add_edge("generate_joke", END)
graph = builder.compile()
# Invoking with two subjects results in a generated joke for each
graph.invoke({"subjects": ["cats", "dogs"]})
# {'subjects': ['cats', 'dogs'], 'jokes': ['Joke about cats', 'Joke about dogs']}
```
"""
__slots__ = ("node", "arg")
@@ -342,10 +349,8 @@ N = TypeVar("N", bound=Hashable)
class Command(Generic[N], ToolOutputMixin):
"""One or more commands to update the graph's state and send messages to nodes.
!!! version-added "Added in version 0.2.24"
Args:
graph: graph to send the command to. Supported values are:
graph: Graph to send the command to. Supported values are:
- `None`: the current graph
- `Command.PARENT`: closest parent graph
@@ -415,7 +420,8 @@ def interrupt(value: Any) -> Any:
To use an `interrupt`, you must enable a checkpointer, as the feature relies
on persisting the graph state.
Example:
!!! example
```python
import uuid
from typing import Optional
@@ -520,38 +526,42 @@ def interrupt(value: Any) -> Any:
@dataclass(slots=True)
class Overwrite:
"""Bypass a reducer and write the wrapped value directly to a BinaryOperatorAggregate channel.
"""Bypass a reducer and write the wrapped value directly to a `BinaryOperatorAggregate` channel.
Receiving multiple Overwrite values for the same channel in a single super-step will raise an InvalidUpdateError.
Receiving multiple `Overwrite` values for the same channel in a single super-step
will raise an `InvalidUpdateError`.
Example:
>>> from typing import Annotated
>>> import operator
>>> from langgraph.graph import StateGraph
>>> from langgraph.types import Overwrite
>>>
>>> class State(TypedDict):
... messages: Annotated[list, operator.add]
>>>
>>> def node_a(state: TypedDict):
... # Normal update: uses the reducer (operator.add)
... return {"messages": ["a"]}
>>>
>>> def node_b(state: State):
... # Overwrite: bypasses the reducer and replaces the entire value
... return {"messages": Overwrite(value=["b"])}
>>>
>>> builder = StateGraph(State)
>>> builder.add_node("node_a", node_a)
>>> builder.add_node("node_b", node_b)
>>> builder.set_entry_point("node_a")
>>> builder.add_edge("node_a", "node_b")
>>> graph = builder.compile()
>>>
>>> # Without Overwrite in node_b, messages would be ["START", "a", "b"]
>>> # With Overwrite, messages is just ["b"]
>>> result = graph.invoke({"messages": ["START"]})
>>> assert result == {"messages": ["b"]}
!!! example
```python
from typing import Annotated
import operator
from langgraph.graph import StateGraph
from langgraph.types import Overwrite
class State(TypedDict):
messages: Annotated[list, operator.add]
def node_a(state: TypedDict):
# Normal update: uses the reducer (operator.add)
return {"messages": ["a"]}
def node_b(state: State):
# Overwrite: bypasses the reducer and replaces the entire value
return {"messages": Overwrite(value=["b"])}
builder = StateGraph(State)
builder.add_node("node_a", node_a)
builder.add_node("node_b", node_b)
builder.set_entry_point("node_a")
builder.add_edge("node_a", "node_b")
graph = builder.compile()
# Without Overwrite in node_b, messages would be ["START", "a", "b"]
# With Overwrite, messages is just ["b"]
result = graph.invoke({"messages": ["START"]})
assert result == {"messages": ["b"]}
```
"""
value: Any
+2 -2
View File
@@ -31,13 +31,13 @@ ContextT_contra = TypeVar(
)
InputT = TypeVar("InputT", bound=StateLike, default=StateT)
"""Type variable used to represent the input to a state graph.
"""Type variable used to represent the input to a `StateGraph`.
Defaults to `StateT`.
"""
OutputT = TypeVar("OutputT", bound=StateLike, default=StateT)
"""Type variable used to represent the output of a state graph.
"""Type variable used to represent the output of a `StateGraph`.
Defaults to `StateT`.
"""
+9 -2
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph"
version = "1.0.2"
version = "1.0.4"
description = "Building stateful, multi-actor applications with LLMs"
authors = []
requires-python = ">=3.10"
@@ -32,8 +32,15 @@ dependencies = [
"pydantic>=2.7.4",
]
[project.urls]
Repository = "https://www.github.com/langchain-ai/langgraph"
Homepage = "https://docs.langchain.com/oss/python/langgraph/overview"
Documentation = "https://reference.langchain.com/python/langgraph/"
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/langgraph"
Changelog = "https://github.com/langchain-ai/langgraph/releases"
Twitter = "https://x.com/LangChainAI"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
[dependency-groups]
test = [
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -3271,7 +3271,7 @@ def test_root_graph(
content="result for query",
name="search_api",
tool_call_id="tool_call123",
id="00000000-0000-4000-8000-000000000024",
id="00000000-0000-4000-8000-000000000004",
)
]
},
@@ -3294,7 +3294,7 @@ def test_root_graph(
content="result for another",
name="search_api",
tool_call_id="tool_call456",
id="00000000-0000-4000-8000-000000000030",
id="00000000-0000-4000-8000-000000000005",
)
]
},
@@ -3898,7 +3898,7 @@ def test_root_graph(
"__root__": [
HumanMessage(
content="what is weather in sf",
id="00000000-0000-4000-8000-000000000051",
id="00000000-0000-4000-8000-000000000008",
),
AIMessage(
content="",
@@ -3918,7 +3918,7 @@ def test_root_graph(
),
AIMessage(content="answer", id="ai2"),
AIMessage(
content="an extra message", id="00000000-0000-4000-8000-000000000066"
content="an extra message", id="00000000-0000-4000-8000-000000000010"
),
HumanMessage(content="what is weather in la"),
],
+78 -6
View File
@@ -8422,23 +8422,55 @@ def test_null_resume_disallowed_with_multiple_interrupts(
}
def test_interrupt_stream_mode_values():
"""Test that interrupts are surfaced when steam_mode='values'"""
def test_interrupt_stream_mode_values(sync_checkpointer: BaseCheckpointSaver):
"""Test that interrupts are surfaced on 'values' stream mode"""
class State(TypedDict):
robot_input: str
human_input: str
def robot_input_node(state: State) -> State:
return {"robot_input": "beep boop i am a robot"}
def human_input_node(state: State) -> Command:
human_input = interrupt("interrupt")
return Command(update={"human_input": human_input})
builder = StateGraph(State)
builder.add_node(robot_input_node)
builder.add_node(human_input_node)
builder.add_edge(START, "human_input_node")
app = builder.compile()
builder.add_edge(START, "robot_input_node")
builder.add_edge("robot_input_node", "human_input_node")
app = builder.compile(checkpointer=sync_checkpointer)
config = {"configurable": {"thread_id": str(uuid.uuid4())}}
result = [*app.stream(State(), stream_mode="values")]
assert "__interrupt__" in result[-1]
result = [*app.stream(State(), config, stream_mode=["updates", "values"])]
assert len(result) == 4
assert result == [
("updates", {"robot_input_node": {"robot_input": "beep boop i am a robot"}}),
("values", {"robot_input": "beep boop i am a robot"}),
("updates", {"__interrupt__": (Interrupt(value="interrupt", id=AnyStr()),)}),
(
"values",
{
"robot_input": "beep boop i am a robot",
"__interrupt__": (Interrupt(value="interrupt", id=AnyStr()),),
},
),
]
resume_result = [
*app.stream(
Command(resume="i am a human"), config, stream_mode=["updates", "values"]
)
]
assert resume_result == [
("values", {"robot_input": "beep boop i am a robot"}),
("updates", {"human_input_node": {"human_input": "i am a human"}}),
(
"values",
{"robot_input": "beep boop i am a robot", "human_input": "i am a human"},
),
]
def test_supersteps_populate_task_results(
@@ -8805,3 +8837,43 @@ def test_overwrite_parallel_error(
InvalidUpdateError, match="Can receive only one Overwrite value per super-step."
):
graph.invoke({"messages": ["START"]}, config)
def test_fork_does_not_apply_pending_writes(
sync_checkpointer: BaseCheckpointSaver,
) -> None:
"""Test that forking with update_state does not apply pending writes from original execution."""
class State(TypedDict):
value: Annotated[int, operator.add]
def node_a(state: State) -> State:
return {"value": 10}
def node_b(state: State) -> State:
return {"value": 100}
graph = (
StateGraph(State)
.add_node("node_a", node_a)
.add_node("node_b", node_b)
.add_edge(START, "node_a")
.add_edge("node_a", "node_b")
.compile(checkpointer=sync_checkpointer)
)
thread1 = {"configurable": {"thread_id": "1"}}
graph.invoke({"value": 1}, thread1)
history = list(graph.get_state_history(thread1))
checkpoint_before_a = next(s for s in history if s.next == ("node_a",))
fork_config = graph.update_state(
checkpoint_before_a.config, {"value": 20}, as_node="node_a"
)
# Continue from fork (should run node_b)
result = graph.invoke(None, fork_config)
# Should be: 1 (input) + 20 (forked node_a) + 100 (node_b) = 121
assert result == {"value": 121}
+96
View File
@@ -9196,6 +9196,64 @@ async def test_astream_waiter_cleanup_on_cancel(
assert all(t.done() for t in recorded_tasks)
@NEEDS_CONTEXTVARS
async def test_interrupt_stream_mode_values(async_checkpointer: BaseCheckpointSaver):
"""Test that interrupts are surfaced on 'values' stream mode"""
class State(TypedDict):
robot_input: str
human_input: str
def robot_input_node(state: State) -> State:
return {"robot_input": "beep boop i am a robot"}
def human_input_node(state: State) -> Command:
human_input = interrupt("interrupt")
return Command(update={"human_input": human_input})
builder = StateGraph(State)
builder.add_node(robot_input_node)
builder.add_node(human_input_node)
builder.add_edge(START, "robot_input_node")
builder.add_edge("robot_input_node", "human_input_node")
app = builder.compile(checkpointer=async_checkpointer)
config = {"configurable": {"thread_id": str(uuid.uuid4())}}
result = [
(mode, e)
async for mode, e in app.astream(
State(), config, stream_mode=["updates", "values"]
)
]
assert len(result) == 4
assert result == [
("updates", {"robot_input_node": {"robot_input": "beep boop i am a robot"}}),
("values", {"robot_input": "beep boop i am a robot"}),
("updates", {"__interrupt__": (Interrupt(value="interrupt", id=AnyStr()),)}),
(
"values",
{
"robot_input": "beep boop i am a robot",
"__interrupt__": (Interrupt(value="interrupt", id=AnyStr()),),
},
),
]
resume_result = [
(mode, e)
async for mode, e in app.astream(
Command(resume="i am a human"), config, stream_mode=["updates", "values"]
)
]
assert resume_result == [
("values", {"robot_input": "beep boop i am a robot"}),
("updates", {"human_input_node": {"human_input": "i am a human"}}),
(
"values",
{"robot_input": "beep boop i am a robot", "human_input": "i am a human"},
),
]
async def test_supersteps_populate_task_results(
async_checkpointer: BaseCheckpointSaver,
) -> None:
@@ -9249,3 +9307,41 @@ async def test_supersteps_populate_task_results(
assert bulk_start_result == ref_start_result == {"num": 1, "text": "one"}
assert bulk_double_result == ref_double_result == {"num": 2, "text": "oneone"}
async def test_fork_does_not_apply_pending_writes(
async_checkpointer: BaseCheckpointSaver,
) -> None:
"""Test that forking with aupdate_state does not apply pending writes from original execution."""
class State(TypedDict):
value: Annotated[int, operator.add]
def node_a(state: State) -> State:
return {"value": 10}
def node_b(state: State) -> State:
return {"value": 100}
graph = (
StateGraph(State)
.add_node("node_a", node_a)
.add_node("node_b", node_b)
.add_edge(START, "node_a")
.add_edge("node_a", "node_b")
.compile(checkpointer=async_checkpointer)
)
thread1 = {"configurable": {"thread_id": "1"}}
await graph.ainvoke({"value": 1}, thread1)
history = [c async for c in graph.aget_state_history(thread1)]
checkpoint_before_a = next(s for s in history if s.next == ("node_a",))
fork_config = await graph.aupdate_state(
checkpoint_before_a.config, {"value": 20}, as_node="node_a"
)
result = await graph.ainvoke(None, fork_config)
# 1 (input) + 20 (forked node_a) + 100 (node_b) = 121
assert result == {"value": 121}
+42 -2
View File
@@ -840,6 +840,46 @@ def test_invoke():
assert result == {"messages": [{"type": "human", "content": "world"}]}
def test_invoke_sanitizes_thread_id():
# Ensure that invoking with thread_id passes thread_id as a top-level arg
# and removes it from the config body.
mock_sync_client = MagicMock()
mock_sync_client.runs.stream.return_value = []
remote_pregel = RemoteGraph("test_graph_id", sync_client=mock_sync_client)
config = {"configurable": {"thread_id": "thread_1"}}
remote_pregel.invoke(
{"input": {"messages": [{"type": "human", "content": "hello"}]}}, config
)
assert mock_sync_client.runs.stream.called
_, kwargs = mock_sync_client.runs.stream.call_args
assert kwargs.get("thread_id") == "thread_1"
passed_config = kwargs.get("config") or {}
assert "configurable" in passed_config
assert "thread_id" not in passed_config["configurable"]
assert not passed_config["configurable"]
def test_stream_sanitizes_thread_id():
# Ensure that streaming with thread_id passes thread_id as a top-level arg
# and removes it from the config body.
mock_sync_client = MagicMock()
mock_sync_client.runs.stream.return_value = []
remote_pregel = RemoteGraph("test_graph_id", sync_client=mock_sync_client)
config = {"configurable": {"thread_id": "thread_2"}}
list(remote_pregel.stream({"input": {"messages": []}}, config))
assert mock_sync_client.runs.stream.called
_, kwargs = mock_sync_client.runs.stream.call_args
assert kwargs.get("thread_id") == "thread_2"
passed_config = kwargs.get("config") or {}
assert "configurable" in passed_config
assert "thread_id" not in passed_config["configurable"]
assert not passed_config["configurable"]
@pytest.mark.anyio
async def test_ainvoke():
# set up test
@@ -879,8 +919,8 @@ async def test_langgraph_cloud_integration():
from langgraph.graph import END, START, MessagesState, StateGraph
# create RemotePregel instance
client = get_client()
sync_client = get_sync_client()
client = get_client(url="http://localhost:8123")
sync_client = get_sync_client(url="http://localhost:8123")
remote_pregel = RemoteGraph(
"agent",
client=client,
+951 -835
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
{
"permissions": {
"allow": [
"Bash(make test:*)",
"Bash(uv run pytest:*)",
"Bash(LANGGRAPH_TEST_FAST=0 make start-services:*)",
"Bash(LANGGRAPH_TEST_FAST=0 uv run:*)",
"Bash(EXIT_CODE=$?)",
"Bash(make stop-services:*)",
"Bash(exit $EXIT_CODE)",
"Read(//Users/sydney_runkle/oss/langgraph/**)",
"Bash(python3:*)",
"Bash(find:*)",
"Bash(python -m pytest:*)",
"Bash(python:*)",
"Read(//tmp/**)"
],
"deny": [],
"ask": []
}
}
@@ -63,7 +63,7 @@ class AgentState(TypedDict):
@deprecated(
"AgentStatePydantic has been moved to `langchain.agents`. Please update your import to `from langchain.agents import AgentStatePydantic`.",
"AgentStatePydantic has been deprecated in favor of AgentState in `langchain.agents`.",
category=LangGraphDeprecatedSinceV10,
)
class AgentStatePydantic(BaseModel):
@@ -78,11 +78,11 @@ with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=LangGraphDeprecatedSinceV10,
message="AgentState has been moved to langchain.agents.*",
message="AgentState has been moved to `langchain.agents`.*",
)
@deprecated(
"AgentStateWithStructuredResponse has been moved to `langchain.agents`. Please update your import to `from langchain.agents import AgentStateWithStructuredResponse`.",
"AgentStateWithStructuredResponse has been deprecated in favor of AgentState in `langchain.agents`.",
category=LangGraphDeprecatedSinceV10,
)
class AgentStateWithStructuredResponse(AgentState):
@@ -95,11 +95,11 @@ with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=LangGraphDeprecatedSinceV10,
message="AgentStatePydantic has been moved to langchain.agents.*",
message="AgentStatePydantic has been deprecated in favor of AgentState in `langchain.agents`.",
)
@deprecated(
"AgentStateWithStructuredResponsePydantic has been moved to `langchain.agents`. Please update your import to `from langchain.agents import AgentStateWithStructuredResponsePydantic`.",
"AgentStateWithStructuredResponsePydantic has been deprecated in favor of AgentState in `langchain.agents`.",
category=LangGraphDeprecatedSinceV10,
)
class AgentStateWithStructuredResponsePydantic(AgentStatePydantic):
@@ -309,37 +309,40 @@ def create_react_agent(
model: The language model for the agent. Supports static and dynamic
model selection.
- **Static model**: A chat model instance (e.g., `ChatOpenAI()`) or
string identifier (e.g., `"openai:gpt-4"`)
- **Static model**: A chat model instance (e.g.,
[`ChatOpenAI`][langchain_openai.ChatOpenAI]) or string identifier (e.g.,
`"openai:gpt-4"`)
- **Dynamic model**: A callable with signature
`(state, runtime) -> BaseChatModel` that returns different models
based on runtime context
If the model has tools bound via `.bind_tools()` or other configurations,
the return type should be a Runnable[LanguageModelInput, BaseMessage]
Coroutines are also supported, allowing for asynchronous model selection.
`(state, runtime) -> BaseChatModel` that returns different models
based on runtime context
If the model has tools bound via `bind_tools` or other configurations,
the return type should be a `Runnable[LanguageModelInput, BaseMessage]`
Coroutines are also supported, allowing for asynchronous model selection.
Dynamic functions receive graph state and runtime, enabling
context-dependent model selection. Must return a `BaseChatModel`
instance. For tool calling, bind tools using `.bind_tools()`.
Bound tools must be a subset of the `tools` parameter.
Dynamic model example:
```python
from dataclasses import dataclass
!!! example "Dynamic model"
@dataclass
class ModelContext:
model_name: str = "gpt-3.5-turbo"
```python
from dataclasses import dataclass
# Instantiate models globally
gpt4_model = ChatOpenAI(model="gpt-4")
gpt35_model = ChatOpenAI(model="gpt-3.5-turbo")
@dataclass
class ModelContext:
model_name: str = "gpt-3.5-turbo"
def select_model(state: AgentState, runtime: Runtime[ModelContext]) -> ChatOpenAI:
model_name = runtime.context.model_name
model = gpt4_model if model_name == "gpt-4" else gpt35_model
return model.bind_tools(tools)
```
# Instantiate models globally
gpt4_model = ChatOpenAI(model="gpt-4")
gpt35_model = ChatOpenAI(model="gpt-3.5-turbo")
def select_model(state: AgentState, runtime: Runtime[ModelContext]) -> ChatOpenAI:
model_name = runtime.context.model_name
model = gpt4_model if model_name == "gpt-4" else gpt35_model
return model.bind_tools(tools)
```
!!! note "Dynamic Model Requirements"
@@ -351,23 +354,26 @@ def create_react_agent(
If an empty list is provided, the agent will consist of a single LLM node without tool calling.
prompt: An optional prompt for the LLM. Can take a few different forms:
- str: This is converted to a SystemMessage and added to the beginning of the list of messages in state["messages"].
- SystemMessage: this is added to the beginning of the list of messages in state["messages"].
- Callable: This function should take in full graph state and the output is then passed to the language model.
- Runnable: This runnable should take in full graph state and the output is then passed to the language model.
- `str`: This is converted to a `SystemMessage` and added to the beginning of the list of messages in `state["messages"]`.
- `SystemMessage`: this is added to the beginning of the list of messages in `state["messages"]`.
- `Callable`: This function should take in full graph state and the output is then passed to the language model.
- `Runnable`: This runnable should take in full graph state and the output is then passed to the language model.
response_format: An optional schema for the final agent output.
If provided, output will be formatted to match the given schema and returned in the 'structured_response' state key.
If not provided, `structured_response` will not be present in the output state.
Can be passed in as:
- an OpenAI function/tool schema,
- a JSON Schema,
- a TypedDict class,
- or a Pydantic class.
- a tuple (prompt, schema), where schema is one of the above.
The prompt will be used together with the model that is being used to generate the structured response.
- An OpenAI function/tool schema,
- A JSON Schema,
- A TypedDict class,
- A Pydantic class.
- A tuple `(prompt, schema)`, where schema is one of the above.
The prompt will be used together with the model that is being used to
generate the structured response.
!!! Important
`response_format` requires the model to support `.with_structured_output`
@@ -428,13 +434,16 @@ def create_react_agent(
store: An optional store object. This is used for persisting data
across multiple threads (e.g., multiple conversations / users).
interrupt_before: An optional list of node names to interrupt before.
Should be one of the following: "agent", "tools".
Should be one of the following: `"agent"`, `"tools"`.
This is useful if you want to add a user confirmation or other interrupt before taking an action.
interrupt_after: An optional list of node names to interrupt after.
Should be one of the following: "agent", "tools".
Should be one of the following: `"agent"`, `"tools"`.
This is useful if you want to return directly or run additional processing on an output.
debug: A flag indicating whether to enable debug mode.
version: Determines the version of the graph to create.
Can be one of:
- `"v1"`: The tool node processes a single message. All tool
@@ -443,7 +452,7 @@ def create_react_agent(
Tool calls are distributed across multiple instances of the tool
node using the [Send](https://langchain-ai.github.io/langgraph/concepts/low_level/#send)
API.
name: An optional name for the CompiledStateGraph.
name: An optional name for the `CompiledStateGraph`.
This name will be automatically used when adding ReAct agent graph to another graph as a subgraph node -
particularly useful for building multi-agent systems.
@@ -453,14 +462,14 @@ def create_react_agent(
Returns:
A compiled LangChain runnable that can be used for chat interactions.
A compiled LangChain `Runnable` that can be used for chat interactions.
The "agent" node calls the language model with the messages list (after applying the prompt).
If the resulting AIMessage contains `tool_calls`, the graph will then call the ["tools"][langgraph.prebuilt.tool_node.ToolNode].
The "tools" node executes the tools (1 tool per `tool_call`) and adds the responses to the messages list
as `ToolMessage` objects. The agent node then calls the language model again.
The process repeats until no more `tool_calls` are present in the response.
The agent then returns the full list of messages as a dictionary containing the key "messages".
The agent then returns the full list of messages as a dictionary containing the key `'messages'`.
``` mermaid
sequenceDiagram
+10 -8
View File
@@ -36,7 +36,7 @@ class ActionRequest(TypedDict):
Contains the action type and any associated arguments needed for the action.
Attributes:
action: The type or name of action being requested (e.g., "Approve XYZ action")
action: The type or name of action being requested (e.g., `"Approve XYZ action"`)
args: Key-value pairs of arguments needed for the action
"""
@@ -89,14 +89,16 @@ class HumanResponse(TypedDict):
Attributes:
type: The type of response:
- "accept": Approves the current state without changes
- "ignore": Skips/ignores the current step
- "response": Provides text feedback or instructions
- "edit": Modifies the current state/content
- `'accept'`: Approves the current state without changes
- `'ignore'`: Skips/ignores the current step
- `'response'`: Provides text feedback or instructions
- `'edit'`: Modifies the current state/content
args: The response payload:
- None: For ignore/accept actions
- str: For text responses
- ActionRequest: For edit actions with updated content
- `None`: For ignore/accept actions
- `str`: For text responses
- `ActionRequest`: For edit actions with updated content
"""
type: Literal["accept", "ignore", "response", "edit"]
+253 -241
View File
@@ -6,6 +6,7 @@ Tools are functions that models can call to interact with external systems,
APIs, databases, or perform computations.
The module implements design patterns for:
- Parallel execution of multiple tool calls for efficiency
- Robust error handling with customizable error messages
- State injection for tools that need access to graph state
@@ -13,11 +14,13 @@ The module implements design patterns for:
- Command-based state updates for advanced control flow
Key Components:
`ToolNode`: Main class for executing tools in LangGraph workflows
`InjectedState`: Annotation for injecting graph state into tools
`InjectedStore`: Annotation for injecting persistent store into tools
`ToolRuntime`: Runtime information for tools, bundling together state, context, config, stream_writer, tool_call_id, and store
`tools_condition`: Utility function for conditional routing based on tool calls
- `ToolNode`: Main class for executing tools in LangGraph workflows
- `InjectedState`: Annotation for injecting graph state into tools
- `InjectedStore`: Annotation for injecting persistent store into tools
- `ToolRuntime`: Runtime information for tools, bundling together `state`, `context`,
`config`, `stream_writer`, `tool_call_id`, and `store`
- `tools_condition`: Utility function for conditional routing based on tool calls
Typical Usage:
```python
@@ -139,6 +142,25 @@ class ToolCallRequest:
state: Any
runtime: ToolRuntime
def __setattr__(self, name: str, value: Any) -> None:
"""Raise deprecation warning when setting attributes directly.
Direct attribute assignment is deprecated. Use the `override()` method instead.
"""
import warnings
# Allow setting attributes during initialization
if not hasattr(self, "__dataclass_fields__") or not hasattr(self, name):
object.__setattr__(self, name, value)
else:
warnings.warn(
f"Setting attribute '{name}' on ToolCallRequest is deprecated. "
"Use the override() method instead to create a new instance with modified values.",
DeprecationWarning,
stacklevel=2,
)
object.__setattr__(self, name, value)
def override(
self, **overrides: Unpack[_ToolCallRequestOverrides]
) -> ToolCallRequest:
@@ -199,8 +221,9 @@ Examples:
```python
def handler(request, execute):
request.tool_call["args"]["value"] *= 2
return execute(request)
modified_call = {**request.tool_call, "args": {**request.tool_call["args"], "value": request.tool_call["args"]["value"] * 2}}
modified_request = request.override(tool_call=modified_call)
return execute(modified_request)
```
Retry on error (execute multiple times):
@@ -476,9 +499,7 @@ def _infer_handled_types(handler: Callable[..., str]) -> tuple[type[Exception],
def _filter_validation_errors(
validation_error: ValidationError,
tool_to_state_args: dict[str, str | None],
tool_to_store_arg: str | None,
tool_to_runtime_arg: str | None,
injected_args: _InjectedArgs | None,
) -> list[ErrorDetails]:
"""Filter validation errors to only include LLM-controlled arguments.
@@ -493,25 +514,28 @@ def _filter_validation_errors(
Args:
validation_error: The Pydantic ValidationError raised during tool invocation.
tool_to_state_args: Mapping of state argument names to state field names.
tool_to_store_arg: Name of the store argument, if any.
tool_to_runtime_arg: Name of the runtime argument, if any.
injected_args: The _InjectedArgs structure containing all injected arguments,
or None if there are no injected arguments.
Returns:
List of ErrorDetails containing only errors for LLM-controlled arguments,
with system-injected argument values removed from the input field.
"""
injected_args = set(tool_to_state_args.keys())
if tool_to_store_arg:
injected_args.add(tool_to_store_arg)
if tool_to_runtime_arg:
injected_args.add(tool_to_runtime_arg)
# Collect all injected argument names
injected_arg_names: set[str] = set()
if injected_args:
if injected_args.state:
injected_arg_names.update(injected_args.state.keys())
if injected_args.store:
injected_arg_names.add(injected_args.store)
if injected_args.runtime:
injected_arg_names.add(injected_args.runtime)
filtered_errors: list[ErrorDetails] = []
for error in validation_error.errors():
# Check if error location contains any injected argument
# error['loc'] is a tuple like ('field_name',) or ('field_name', 'nested_field')
if error["loc"] and error["loc"][0] not in injected_args:
if error["loc"] and error["loc"][0] not in injected_arg_names:
# Create a copy of the error dict to avoid mutating the original
error_copy: dict[str, Any] = {**error}
@@ -519,7 +543,7 @@ def _filter_validation_errors(
if isinstance(error_copy.get("input"), dict):
input_dict = error_copy["input"]
input_copy = {
k: v for k, v in input_dict.items() if k not in injected_args
k: v for k, v in input_dict.items() if k not in injected_arg_names
}
error_copy["input"] = input_copy
@@ -529,6 +553,60 @@ def _filter_validation_errors(
return filtered_errors
@dataclass
class _InjectedArgs:
"""Internal structure for tracking injected arguments for a tool.
This data structure is built once during ToolNode initialization by analyzing
the tool's signature and args schema, then reused during execution for efficient
injection without repeated reflection.
The structure maps from tool parameter names to their injection sources, enabling
the ToolNode to know exactly which arguments need to be injected and where to
get their values from.
Attributes:
state: Mapping from tool parameter names to state field names for injection.
Keys are tool parameter names, values are either:
- str: Name of the state field to extract and inject
- None: Inject the entire state object
Empty dict if no state injection is needed.
store: Name of the tool parameter where the store should be injected,
or None if no store injection is needed.
runtime: Name of the tool parameter where the runtime should be injected,
or None if no runtime injection is needed.
Example:
For a tool with signature:
```python
def my_tool(
x: int,
messages: Annotated[list, InjectedState("messages")],
full_state: Annotated[dict, InjectedState()],
store: Annotated[BaseStore, InjectedStore()],
runtime: ToolRuntime,
) -> str:
...
```
The resulting `_InjectedArgs` would be:
```python
_InjectedArgs(
state={
"messages": "messages", # Extract state["messages"]
"full_state": None, # Inject entire state
},
store="store", # Inject into "store" parameter
runtime="runtime", # Inject into "runtime" parameter
)
```
"""
state: dict[str, str | None]
store: str | None
runtime: str | None
class ToolNode(RunnableCallable):
"""A node for executing tools in LangGraph workflows.
@@ -552,44 +630,52 @@ class ToolNode(RunnableCallable):
Output format depends on input type and tool behavior:
**For Regular tools**:
- Dict input `{"messages": [ToolMessage(...)]}`
- List input `[ToolMessage(...)]`
**For Command tools**:
- Returns `[Command(...)]` or mixed list with regular tool outputs
- Commands can update state, trigger navigation, or send messages
- `Command` can update state, trigger navigation, or send messages
Args:
tools: A sequence of tools that can be invoked by this node. Supports:
tools: A sequence of tools that can be invoked by this node.
Supports:
- **BaseTool instances**: Tools with schemas and metadata
- **Plain functions**: Automatically converted to tools with inferred schemas
name: The name identifier for this node in the graph. Used for debugging
and visualization. Defaults to "tools".
and visualization.
tags: Optional metadata tags to associate with the node for filtering
and organization. Defaults to `None`.
and organization.
handle_tool_errors: Configuration for error handling during tool execution.
Supports multiple strategies:
- **True**: Catch all errors and return a ToolMessage with the default
- `True`: Catch all errors and return a `ToolMessage` with the default
error template containing the exception details.
- **str**: Catch all errors and return a ToolMessage with this custom
- `str`: Catch all errors and return a `ToolMessage` with this custom
error message string.
- **type[Exception]**: Only catch exceptions with the specified type and
- `type[Exception]`: Only catch exceptions with the specified type and
return the default error message for it.
- **tuple[type[Exception], ...]**: Only catch exceptions with the specified
- `tuple[type[Exception], ...]`: Only catch exceptions with the specified
types and return default error messages for them.
- **Callable[..., str]**: Catch exceptions matching the callable's signature
- `Callable[..., str]`: Catch exceptions matching the callable's signature
and return the string result of calling it with the exception.
- **False**: Disable error handling entirely, allowing exceptions to
- `False`: Disable error handling entirely, allowing exceptions to
propagate.
Defaults to a callable that:
- catches tool invocation errors (due to invalid arguments provided by the model) and returns a descriptive error message
- ignores tool execution errors (they will be re-raised)
- Catches tool invocation errors (due to invalid arguments provided by the
model) and returns a descriptive error message
- Ignores tool execution errors (they will be re-raised)
messages_key: The key in the state dictionary that contains the message list.
This same key will be used for the output `ToolMessage` objects.
Defaults to "messages".
Allows custom state schemas with different message field names.
Examples:
@@ -665,9 +751,7 @@ class ToolNode(RunnableCallable):
"""
super().__init__(self._func, self._afunc, name=name, tags=tags, trace=False)
self._tools_by_name: dict[str, BaseTool] = {}
self._tool_to_state_args: dict[str, dict[str, str | None]] = {}
self._tool_to_store_arg: dict[str, str | None] = {}
self._tool_to_runtime_arg: dict[str, str | None] = {}
self._injected_args: dict[str, _InjectedArgs] = {}
self._handle_tool_errors = handle_tool_errors
self._messages_key = messages_key
self._wrap_tool_call = wrap_tool_call
@@ -678,9 +762,8 @@ class ToolNode(RunnableCallable):
else:
tool_ = tool
self._tools_by_name[tool_.name] = tool_
self._tool_to_state_args[tool_.name] = _get_state_args(tool_)
self._tool_to_store_arg[tool_.name] = _get_store_arg(tool_)
self._tool_to_runtime_arg[tool_.name] = _get_runtime_arg(tool_)
# Build injected args mapping once during initialization in a single pass
self._injected_args[tool_.name] = _get_all_injected_args(tool_)
@property
def tools_by_name(self) -> dict[str, BaseTool]:
@@ -833,12 +916,8 @@ class ToolNode(RunnableCallable):
response = tool.invoke(call_args, config)
except ValidationError as exc:
# Filter out errors for injected arguments
filtered_errors = _filter_validation_errors(
exc,
self._tool_to_state_args.get(call["name"], {}),
self._tool_to_store_arg.get(call["name"]),
self._tool_to_runtime_arg.get(call["name"]),
)
injected = self._injected_args.get(call["name"])
filtered_errors = _filter_validation_errors(exc, injected)
# Use original call["args"] without injected values for error reporting
raise ToolInvocationError(
call["name"], exc, call["args"], filtered_errors
@@ -990,12 +1069,8 @@ class ToolNode(RunnableCallable):
response = await tool.ainvoke(call_args, config)
except ValidationError as exc:
# Filter out errors for injected arguments
filtered_errors = _filter_validation_errors(
exc,
self._tool_to_state_args.get(call["name"], {}),
self._tool_to_store_arg.get(call["name"]),
self._tool_to_runtime_arg.get(call["name"]),
)
injected = self._injected_args.get(call["name"])
filtered_errors = _filter_validation_errors(exc, injected)
# Use original call["args"] without injected values for error reporting
raise ToolInvocationError(
call["name"], exc, call["args"], filtered_errors
@@ -1188,86 +1263,6 @@ class ToolNode(RunnableCallable):
return input["state"]
return input
def _inject_state(
self,
tool_call: ToolCall,
state: list[AnyMessage] | dict[str, Any] | BaseModel,
) -> ToolCall:
state_args = self._tool_to_state_args[tool_call["name"]]
if state_args and isinstance(state, list):
required_fields = list(state_args.values())
if (
len(required_fields) == 1 and required_fields[0] == self._messages_key
) or required_fields[0] is None:
state = {self._messages_key: state}
else:
err_msg = (
f"Invalid input to ToolNode. Tool {tool_call['name']} requires "
f"graph state dict as input."
)
if any(state_field for state_field in state_args.values()):
required_fields_str = ", ".join(f for f in required_fields if f)
err_msg += f" State should contain fields {required_fields_str}."
raise ValueError(err_msg)
if isinstance(state, dict):
tool_state_args = {
tool_arg: state[state_field] if state_field else state
for tool_arg, state_field in state_args.items()
}
else:
tool_state_args = {
tool_arg: getattr(state, state_field) if state_field else state
for tool_arg, state_field in state_args.items()
}
tool_call["args"] = {
**tool_call["args"],
**tool_state_args,
}
return tool_call
def _inject_store(self, tool_call: ToolCall, store: BaseStore | None) -> ToolCall:
store_arg = self._tool_to_store_arg[tool_call["name"]]
if not store_arg:
return tool_call
if store is None:
msg = (
"Cannot inject store into tools with InjectedStore annotations - "
"please compile your graph with a store."
)
raise ValueError(msg)
tool_call["args"] = {
**tool_call["args"],
store_arg: store,
}
return tool_call
def _inject_runtime(
self, tool_call: ToolCall, tool_runtime: ToolRuntime
) -> ToolCall:
"""Inject ToolRuntime into tool call arguments.
Args:
tool_call: The tool call to inject runtime into.
tool_runtime: The ToolRuntime instance to inject.
Returns:
The tool call with runtime injected if needed.
"""
runtime_arg = self._tool_to_runtime_arg.get(tool_call["name"])
if not runtime_arg:
return tool_call
tool_call["args"] = {
**tool_call["args"],
runtime_arg: tool_runtime,
}
return tool_call
def _inject_tool_args(
self,
tool_call: ToolCall,
@@ -1306,12 +1301,64 @@ class ToolNode(RunnableCallable):
if tool_call["name"] not in self.tools_by_name:
return tool_call
injected = self._injected_args.get(tool_call["name"])
if not injected:
return tool_call
tool_call_copy: ToolCall = copy(tool_call)
tool_call_with_state = self._inject_state(tool_call_copy, tool_runtime.state)
tool_call_with_store = self._inject_store(
tool_call_with_state, tool_runtime.store
)
return self._inject_runtime(tool_call_with_store, tool_runtime)
injected_args = {}
# Inject state
if injected.state:
state = tool_runtime.state
# Handle list state by converting to dict
if isinstance(state, list):
required_fields = list(injected.state.values())
if (
len(required_fields) == 1
and required_fields[0] == self._messages_key
) or required_fields[0] is None:
state = {self._messages_key: state}
else:
err_msg = (
f"Invalid input to ToolNode. Tool {tool_call['name']} requires "
f"graph state dict as input."
)
if any(state_field for state_field in injected.state.values()):
required_fields_str = ", ".join(f for f in required_fields if f)
err_msg += (
f" State should contain fields {required_fields_str}."
)
raise ValueError(err_msg)
# Extract state values
if isinstance(state, dict):
for tool_arg, state_field in injected.state.items():
injected_args[tool_arg] = (
state[state_field] if state_field else state
)
else:
for tool_arg, state_field in injected.state.items():
injected_args[tool_arg] = (
getattr(state, state_field) if state_field else state
)
# Inject store
if injected.store:
if tool_runtime.store is None:
msg = (
"Cannot inject store into tools with InjectedStore annotations - "
"please compile your graph with a store."
)
raise ValueError(msg)
injected_args[injected.store] = tool_runtime.store
# Inject runtime
if injected.runtime:
injected_args[injected.runtime] = tool_runtime
tool_call_copy["args"] = {**tool_call_copy["args"], **injected_args}
return tool_call_copy
def _validate_tool_command(
self,
@@ -1393,7 +1440,7 @@ def tools_condition(
"""Conditional routing function for tool-calling workflows.
This utility function implements the standard conditional logic for ReAct-style
agents: if the last AI message contains tool calls, route to the tool execution
agents: if the last `AIMessage` contains tool calls, route to the tool execution
node; otherwise, end the workflow. This pattern is fundamental to most tool-calling
agent architectures.
@@ -1402,16 +1449,15 @@ def tools_condition(
Args:
state: The current graph state to examine for tool calls. Supported formats:
- Dictionary containing a messages key (for StateGraph)
- BaseModel instance with a messages attribute
- Dictionary containing a messages key (for `StateGraph`)
- `BaseModel` instance with a messages attribute
messages_key: The key or attribute name containing the message list in the state.
This allows customization for graphs using different state schemas.
Defaults to "messages".
Returns:
Either "tools" if tool calls are present in the last AI message, or "__end__"
to terminate the workflow. These are the standard routing destinations for
tool-calling conditional edges.
Either `'tools'` if tool calls are present in the last `AIMessage`, or `'__end__'`
to terminate the workflow. These are the standard routing destinations for
tool-calling conditional edges.
Raises:
ValueError: If no messages can be found in the provided state format.
@@ -1608,7 +1654,7 @@ class InjectedStore(InjectedToolArg):
This annotation enables tools to access LangGraph's persistent storage system
without exposing storage details to the language model. Tools annotated with
InjectedStore receive the store instance automatically during execution while
`InjectedStore` receive the store instance automatically during execution while
remaining invisible to the model's tool-calling interface.
The store provides persistent, cross-session data storage that tools can use
@@ -1705,120 +1751,86 @@ def _is_injection(
return False
def _get_state_args(tool: BaseTool) -> dict[str, str | None]:
"""Extract state injection mappings from tool annotations.
This function analyzes a tool's input schema to identify arguments that should
be injected with graph state. It processes InjectedState annotations to build
a mapping of tool argument names to state field names.
def _get_injection_from_type(
type_: Any, injection_type: type[InjectedState | InjectedStore | ToolRuntime]
) -> Any | None:
"""Extract injection instance from a type annotation.
Args:
tool: The tool to analyze for state injection requirements.
type_: The type annotation to check.
injection_type: The injection type to look for.
Returns:
A dictionary mapping tool argument names to state field names. If a field
name is None, the entire state should be injected for that argument.
The injection instance if found, True if injection marker found without instance, None otherwise.
"""
full_schema = tool.get_input_schema()
tool_args_to_state_fields: dict = {}
type_args = get_args(type_)
matches = [arg for arg in type_args if _is_injection(arg, injection_type)]
for name, type_ in get_all_basemodel_annotations(full_schema).items():
injections = [
type_arg
for type_arg in get_args(type_)
if _is_injection(type_arg, InjectedState)
]
if len(injections) > 1:
msg = (
"A tool argument should not be annotated with InjectedState more than "
f"once. Received arg {name} with annotations {injections}."
)
raise ValueError(msg)
if len(injections) == 1:
injection = injections[0]
if isinstance(injection, InjectedState) and injection.field:
tool_args_to_state_fields[name] = injection.field
else:
tool_args_to_state_fields[name] = None
else:
pass
return tool_args_to_state_fields
if len(matches) > 1:
msg = (
f"A tool argument should not be annotated with {injection_type.__name__} "
f"more than once. Found: {matches}"
)
raise ValueError(msg)
def _get_store_arg(tool: BaseTool) -> str | None:
"""Extract store injection argument from tool annotations.
This function analyzes a tool's input schema to identify the argument that
should be injected with the graph store. Only one store argument is supported
per tool.
Args:
tool: The tool to analyze for store injection requirements.
Returns:
The name of the argument that should receive the store injection, or None
if no store injection is required.
Raises:
ValueError: If a tool argument has multiple InjectedStore annotations.
"""
full_schema = tool.get_input_schema()
for name, type_ in get_all_basemodel_annotations(full_schema).items():
injections = [
type_arg
for type_arg in get_args(type_)
if _is_injection(type_arg, InjectedStore)
]
if len(injections) > 1:
msg = (
"A tool argument should not be annotated with InjectedStore more than "
f"once. Received arg {name} with annotations {injections}."
)
raise ValueError(msg)
if len(injections) == 1:
return name
if len(matches) == 1:
return matches[0]
elif _is_injection(type_, injection_type):
return True
return None
def _get_runtime_arg(tool: BaseTool) -> str | None:
"""Extract runtime injection argument from tool annotations.
def _get_all_injected_args(tool: BaseTool) -> _InjectedArgs:
"""Extract all injected arguments from tool in a single pass.
This function analyzes a tool's input schema to identify the argument that
should be injected with the ToolRuntime instance. Only one runtime argument
is supported per tool.
This function analyzes both the tool's input schema and function signature
to identify all arguments that should be injected (state, store, runtime).
Args:
tool: The tool to analyze for runtime injection requirements.
tool: The tool to analyze for injection requirements.
Returns:
The name of the argument that should receive the runtime injection, or None
if no runtime injection is required.
Raises:
ValueError: If a tool argument has multiple ToolRuntime annotations.
_InjectedArgs structure containing all detected injections.
"""
# Get annotations from both schema and function signature
full_schema = tool.get_input_schema()
for name, type_ in get_all_basemodel_annotations(full_schema).items():
# Check if the parameter name is "runtime" (regardless of type)
schema_annotations = get_all_basemodel_annotations(full_schema)
func = getattr(tool, "func", None) or getattr(tool, "coroutine", None)
func_annotations = get_type_hints(func, include_extras=True) if func else {}
# Combine both annotation sources, preferring schema annotations
# In the future, we might want to add more restrictions here...
all_annotations = {**func_annotations, **schema_annotations}
# Track injected args
state_args: dict[str, str | None] = {}
store_arg: str | None = None
runtime_arg: str | None = None
for name, type_ in all_annotations.items():
# Check for runtime (special case: parameter named "runtime")
if name == "runtime":
return name
# Check if the type itself is ToolRuntime (direct usage)
if _is_injection(type_, ToolRuntime):
return name
# Check if ToolRuntime is in Annotated args
injections = [
type_arg
for type_arg in get_args(type_)
if _is_injection(type_arg, ToolRuntime)
]
if len(injections) > 1:
msg = (
"A tool argument should not be annotated with ToolRuntime more than "
f"once. Received arg {name} with annotations {injections}."
)
raise ValueError(msg)
if len(injections) == 1:
return name
runtime_arg = name
return None
# Check for InjectedState
if state_inj := _get_injection_from_type(type_, InjectedState):
if isinstance(state_inj, InjectedState) and state_inj.field:
state_args[name] = state_inj.field
else:
state_args[name] = None
# Check for InjectedStore
if _get_injection_from_type(type_, InjectedStore):
store_arg = name
# Check for ToolRuntime
if _get_injection_from_type(type_, ToolRuntime):
runtime_arg = name
return _InjectedArgs(
state=state_args,
store=store_arg,
runtime=runtime_arg,
)
@@ -45,9 +45,9 @@ def _default_format_error(
category=LangGraphDeprecatedSinceV10,
)
class ValidationNode(RunnableCallable):
"""A node that validates all tools requests from the last AIMessage.
"""A node that validates all tools requests from the last `AIMessage`.
It can be used either in StateGraph with a "messages" key.
It can be used either in `StateGraph` with a `'messages'` key.
!!! note
@@ -57,7 +57,8 @@ class ValidationNode(RunnableCallable):
messages and tool IDs (for use in multi-turn conversations).
Returns:
(Union[Dict[str, List[ToolMessage]], Sequence[ToolMessage]]): A list of ToolMessages with the validated content or error messages.
(Union[Dict[str, List[ToolMessage]], Sequence[ToolMessage]]): A list of
`ToolMessage` objects with the validated content or error messages.
Example:
```python title="Example usage for re-prompting the model to generate a valid response:"
+5 -2
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph-prebuilt"
version = "1.0.2"
version = "1.0.5"
description = "Library with high-level APIs for creating and executing LangGraph agents and tools."
authors = []
requires-python = ">=3.10"
@@ -29,7 +29,10 @@ dependencies = [
]
[project.urls]
Repository = "https://www.github.com/langchain-ai/langgraph"
Source = "https://github.com/langchain-ai/langgraph/tree/main/libs/prebuilt"
Twitter = "https://x.com/LangChainAI"
Slack = "https://www.langchain.com/join-community"
Reddit = "https://www.reddit.com/r/LangChain/"
[dependency-groups]
test = [
+83 -9
View File
@@ -130,11 +130,17 @@ def test_modify_arguments() -> None:
execute: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
"""Handler that doubles the input arguments."""
# Modify the arguments
request.tool_call["args"]["a"] *= 2
request.tool_call["args"]["b"] *= 2
return execute(request)
# Modify the arguments using override method
modified_call = {
**request.tool_call,
"args": {
**request.tool_call["args"],
"a": request.tool_call["args"]["a"] * 2,
"b": request.tool_call["args"]["b"] * 2,
},
}
modified_request = request.override(tool_call=modified_call)
return execute(modified_request)
tool_node = ToolNode([add], wrap_tool_call=modify_args_handler)
@@ -362,10 +368,17 @@ async def test_handler_with_async_execution() -> None:
execute: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
"""Handler that modifies arguments."""
# Add 10 to both arguments
request.tool_call["args"]["a"] += 10
request.tool_call["args"]["b"] += 10
return execute(request)
# Add 10 to both arguments using override method
modified_call = {
**request.tool_call,
"args": {
**request.tool_call["args"],
"a": request.tool_call["args"]["a"] + 10,
"b": request.tool_call["args"]["b"] + 10,
},
}
modified_request = request.override(tool_call=modified_call)
return execute(modified_request)
tool_node = ToolNode([async_add], wrap_tool_call=modifying_handler)
@@ -1305,3 +1318,64 @@ async def test_state_extraction_with_tool_call_with_context_async() -> None:
assert state_seen[0] == actual_state
assert "__type" not in state_seen[0]
assert "tool_call" not in state_seen[0]
def test_tool_call_request_is_frozen() -> None:
"""Test that ToolCallRequest raises deprecation warnings on direct attribute reassignment."""
tool_call: ToolCall = {"name": "add", "args": {"a": 1, "b": 2}, "id": "call_1"}
state: dict = {"messages": []}
runtime = None
request = ToolCallRequest(
tool_call=tool_call, tool=add, state=state, runtime=runtime
) # type: ignore[arg-type]
# Test that direct attribute reassignment raises DeprecationWarning
with pytest.warns(
DeprecationWarning,
match="Setting attribute 'tool_call' on ToolCallRequest is deprecated",
):
request.tool_call = {"name": "other", "args": {}, "id": "call_2"} # type: ignore[misc]
with pytest.warns(
DeprecationWarning,
match="Setting attribute 'tool' on ToolCallRequest is deprecated",
):
request.tool = None # type: ignore[misc]
with pytest.warns(
DeprecationWarning,
match="Setting attribute 'state' on ToolCallRequest is deprecated",
):
request.state = {} # type: ignore[misc]
with pytest.warns(
DeprecationWarning,
match="Setting attribute 'runtime' on ToolCallRequest is deprecated",
):
request.runtime = None # type: ignore[misc]
# Test that override method works correctly
new_tool_call: ToolCall = {
"name": "multiply",
"args": {"x": 5, "y": 10},
"id": "call_3",
}
# Original request should be unchanged (note: it was modified by the warnings tests above)
# So we create a fresh request to test override properly
fresh_request = ToolCallRequest(
tool_call=tool_call, tool=add, state=state, runtime=runtime
) # type: ignore[arg-type]
fresh_new_request = fresh_request.override(tool_call=new_tool_call)
# Original request should be unchanged
assert fresh_request.tool_call == tool_call
assert fresh_request.tool_call["name"] == "add"
# New request should have the updated tool_call
assert fresh_new_request.tool_call == new_tool_call
assert fresh_new_request.tool_call["name"] == "multiply"
assert fresh_new_request.tool == add # Other fields should remain the same
assert fresh_new_request.state == state
assert fresh_new_request.runtime is None
+3 -18
View File
@@ -53,7 +53,6 @@ from langgraph.prebuilt.chat_agent_executor import (
from langgraph.prebuilt.tool_node import (
InjectedState,
InjectedStore,
_get_state_args,
_infer_handled_types,
)
from tests.any_str import AnyStr
@@ -639,8 +638,9 @@ def test_react_agent_parallel_tool_calls(
for event in agent.stream(
{"messages": [("user", query)]}, config, stream_mode="values"
):
if messages := event.get("messages"):
message_types.append([m.type for m in messages])
if "__interrupt__" not in event:
if messages := event.get("messages"):
message_types.append([m.type for m in messages])
if version == "v1":
assert message_types == [
@@ -1084,21 +1084,6 @@ async def test_return_direct(version: str) -> None:
]
def test__get_state_args() -> None:
class Schema1(BaseModel):
a: Annotated[str, InjectedState]
class Schema2(Schema1):
b: Annotated[int, InjectedState("bar")]
@dec_tool(args_schema=Schema2)
def foo(a: str, b: int) -> float:
"""return"""
return 0.0
assert _get_state_args(foo) == {"a": None, "b": "bar"}
def test_inspect_react() -> None:
model = FakeToolCallingModel(tool_calls=[])
agent = create_react_agent(model, [])
+250
View File
@@ -42,6 +42,7 @@ from langgraph.prebuilt import (
from langgraph.prebuilt.tool_node import (
TOOL_CALL_ERROR_TEMPLATE,
ToolInvocationError,
ToolRuntime,
tools_condition,
)
@@ -1610,3 +1611,252 @@ def test_tool_node_stream_writer() -> None:
},
),
]
def test_tool_call_request_setattr_deprecation_warning():
"""Test that ToolCallRequest raises a deprecation warning on direct attribute modification."""
import warnings
from langgraph.prebuilt.tool_node import ToolCallRequest
# Create a mock ToolCall
tool_call = {"name": "test", "args": {"a": 1}, "id": "call_1", "type": "tool_call"}
# Create a ToolCallRequest
request = ToolCallRequest(
tool_call=tool_call,
tool=None,
state={"messages": []},
runtime=None,
)
# Test 1: Direct attribute assignment should raise deprecation warning but still work
with pytest.warns(DeprecationWarning, match="deprecated.*override"):
request.tool_call = {"name": "other", "args": {}, "id": "call_2"}
# Verify the attribute was actually modified
assert request.tool_call == {"name": "other", "args": {}, "id": "call_2"}
# Reset for further tests
with warnings.catch_warnings():
warnings.simplefilter("ignore")
request.tool_call = tool_call
# Test 2: override method should work without warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
new_tool_call = {
"name": "new_tool",
"args": {"b": 2},
"id": "call_3",
"type": "tool_call",
}
new_request = request.override(tool_call=new_tool_call)
# Verify no warning was raised
assert len(w) == 0
# Verify original is unchanged
assert request.tool_call == tool_call
# Verify new request has updated values
assert new_request.tool_call == new_tool_call
# Test 3: Initialization should not trigger warning
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
ToolCallRequest(
tool_call=tool_call,
tool=None,
state={"messages": []},
runtime=None,
)
# Verify no warning was raised during initialization
assert len(w) == 0
async def test_tool_node_inject_async_all_types_signature_only() -> None:
"""Test all injection types without @tool decorator."""
store = InMemoryStore()
namespace = ("test",)
store.put(namespace, "test_key", {"store_data": "from_store"})
class TestState(TypedDict):
messages: list
foo: str
bar: int
async def comprehensive_async_tool(
x: int,
whole_state: Annotated[TestState, InjectedState],
foo_field: Annotated[str, InjectedState("foo")],
store: Annotated[BaseStore, InjectedStore()],
runtime: ToolRuntime,
) -> str:
"""Async tool that uses all injection types."""
bar_from_whole = whole_state["bar"]
foo_value = foo_field
store_val = store.get(namespace, "test_key").value["store_data"]
foo_from_runtime = runtime.state["foo"]
tool_call_id = runtime.tool_call_id
return (
f"x={x}, "
f"bar_from_whole={bar_from_whole}, "
f"foo_field={foo_value}, "
f"store={store_val}, "
f"foo_from_runtime={foo_from_runtime}, "
f"tool_call_id={tool_call_id}"
)
node = ToolNode([comprehensive_async_tool], handle_tool_errors=True)
tool_call = {
"name": "comprehensive_async_tool",
"args": {"x": 42},
"id": "test_call_123",
"type": "tool_call",
}
msg = AIMessage("hi?", tool_calls=[tool_call])
config = _create_config_with_runtime(store=store)
result = await node.ainvoke(
{"messages": [msg], "foo": "foo_value", "bar": 99}, config=config
)
tool_message = result["messages"][-1]
assert tool_message.content == (
"x=42, "
"bar_from_whole=99, "
"foo_field=foo_value, "
"store=from_store, "
"foo_from_runtime=foo_value, "
"tool_call_id=test_call_123"
)
async def test_tool_node_inject_async_all_types_with_decorator() -> None:
"""Test all injection types with @tool decorator."""
store = InMemoryStore()
namespace = ("test",)
store.put(namespace, "test_key", {"store_data": "from_store"})
class TestState(TypedDict):
messages: list
foo: str
bar: int
@dec_tool
async def comprehensive_async_tool(
x: int,
whole_state: Annotated[TestState, InjectedState],
foo_field: Annotated[str, InjectedState("foo")],
store: Annotated[BaseStore, InjectedStore()],
runtime: ToolRuntime,
) -> str:
"""Async tool that uses all injection types."""
bar_from_whole = whole_state["bar"]
foo_value = foo_field
store_val = store.get(namespace, "test_key").value["store_data"]
foo_from_runtime = runtime.state["foo"]
tool_call_id = runtime.tool_call_id
return (
f"x={x}, "
f"bar_from_whole={bar_from_whole}, "
f"foo_field={foo_value}, "
f"store={store_val}, "
f"foo_from_runtime={foo_from_runtime}, "
f"tool_call_id={tool_call_id}"
)
node = ToolNode([comprehensive_async_tool], handle_tool_errors=True)
tool_call = {
"name": "comprehensive_async_tool",
"args": {"x": 42},
"id": "test_call_456",
"type": "tool_call",
}
msg = AIMessage("hi?", tool_calls=[tool_call])
config = _create_config_with_runtime(store=store)
result = await node.ainvoke(
{"messages": [msg], "foo": "foo_value", "bar": 99}, config=config
)
tool_message = result["messages"][-1]
assert tool_message.content == (
"x=42, "
"bar_from_whole=99, "
"foo_field=foo_value, "
"store=from_store, "
"foo_from_runtime=foo_value, "
"tool_call_id=test_call_456"
)
async def test_tool_node_inject_async_all_types_with_schema() -> None:
"""Test all injection types with explicit schema."""
store = InMemoryStore()
namespace = ("test",)
store.put(namespace, "test_key", {"store_data": "from_store"})
class TestState(TypedDict):
messages: list
foo: str
bar: int
class ComprehensiveToolSchema(BaseModel):
model_config = {"arbitrary_types_allowed": True}
x: int
whole_state: Annotated[TestState, InjectedState]
foo_field: Annotated[str, InjectedState("foo")]
store: Annotated[BaseStore, InjectedStore()]
runtime: ToolRuntime
@dec_tool(args_schema=ComprehensiveToolSchema)
async def comprehensive_async_tool(
x: int,
whole_state: Annotated[TestState, InjectedState],
foo_field: Annotated[str, InjectedState("foo")],
store: Annotated[BaseStore, InjectedStore()],
runtime: ToolRuntime,
) -> str:
"""Async tool that uses all injection types."""
bar_from_whole = whole_state["bar"]
foo_value = foo_field
store_val = store.get(namespace, "test_key").value["store_data"]
foo_from_runtime = runtime.state["foo"]
tool_call_id = runtime.tool_call_id
return (
f"x={x}, "
f"bar_from_whole={bar_from_whole}, "
f"foo_field={foo_value}, "
f"store={store_val}, "
f"foo_from_runtime={foo_from_runtime}, "
f"tool_call_id={tool_call_id}"
)
node = ToolNode([comprehensive_async_tool], handle_tool_errors=True)
tool_call = {
"name": "comprehensive_async_tool",
"args": {"x": 42},
"id": "test_call_789",
"type": "tool_call",
}
msg = AIMessage("hi?", tool_calls=[tool_call])
config = _create_config_with_runtime(store=store)
result = await node.ainvoke(
{"messages": [msg], "foo": "foo_value", "bar": 99}, config=config
)
tool_message = result["messages"][-1]
assert tool_message.content == (
"x=42, "
"bar_from_whole=99, "
"foo_field=foo_value, "
"store=from_store, "
"foo_from_runtime=foo_value, "
"tool_call_id=test_call_789"
)
+739 -541
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -17,7 +17,7 @@ lint lint_diff:
uv run ruff check .
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
uvx ty check .
uv run ty check .
format format_diff:
uv run ruff check --select I --fix $(PYTHON_FILES)
+4 -2
View File
@@ -1,6 +1,8 @@
from langgraph_sdk.auth import Auth
from langgraph_sdk.client import get_client, get_sync_client
from langgraph_sdk.encryption import Encryption
from langgraph_sdk.encryption.types import EncryptionContext
__version__ = "0.2.9"
__version__ = "0.2.14"
__all__ = ["Auth", "get_client", "get_sync_client"]
__all__ = ["Auth", "Encryption", "EncryptionContext", "get_client", "get_sync_client"]
+10 -11
View File
@@ -90,11 +90,11 @@ class Auth:
"""
__slots__ = (
"on",
"_handlers",
"_global_handlers",
"_authenticate_handler",
"_global_handlers",
"_handler_cache",
"_handlers",
"on",
)
types = types
"""Reference to auth type definitions.
@@ -259,7 +259,7 @@ class Auth:
"""
if self._authenticate_handler is not None:
raise ValueError(
"Authentication handler already set as {self._authenticate_handler}."
f"Authentication handler already set as {self._authenticate_handler}."
)
self._authenticate_handler = fn
return fn
@@ -383,7 +383,7 @@ class _ResourceOn(typing.Generic[VCreate, VRead, VUpdate, VDelete, VSearch]):
if fn is not None:
_validate_handler(fn)
return typing.cast(
_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch],
"_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch]",
_register_handler(self.auth, self.resource, "*", fn),
)
@@ -392,7 +392,7 @@ class _ResourceOn(typing.Generic[VCreate, VRead, VUpdate, VDelete, VSearch]):
) -> _ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch]:
_validate_handler(handler)
return typing.cast(
_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch],
"_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch]",
_register_handler(self.auth, self.resource, "*", handler),
)
@@ -612,10 +612,10 @@ class _On:
__slots__ = (
"_auth",
"assistants",
"threads",
"runs",
"crons",
"runs",
"store",
"threads",
"value",
)
@@ -740,9 +740,8 @@ def is_studio_user(
) -> bool:
return (
isinstance(user, types.StudioUser)
or isinstance(user, dict)
and user.get("kind") == "StudioUser" # ty: ignore[invalid-argument-type]
or (isinstance(user, dict) and user.get("kind") == "StudioUser") # ty: ignore[invalid-argument-type]
)
__all__ = ["Auth", "types", "exceptions"]
__all__ = ["Auth", "exceptions", "types"]
+14 -14
View File
@@ -243,7 +243,7 @@ class StudioUser:
```
"""
__slots__ = ("username", "_is_authenticated", "_permissions")
__slots__ = ("_is_authenticated", "_permissions", "username")
def __init__(self, username: str, is_authenticated: bool = False) -> None:
self.username = username
@@ -1093,22 +1093,22 @@ class on:
__all__ = [
"on",
"AssistantsCreate",
"AssistantsDelete",
"AssistantsRead",
"AssistantsSearch",
"AssistantsUpdate",
"MetadataInput",
"RunsCreate",
"ThreadsCreate",
"ThreadsRead",
"ThreadsUpdate",
"ThreadsDelete",
"ThreadsSearch",
"AssistantsCreate",
"AssistantsRead",
"AssistantsUpdate",
"AssistantsDelete",
"AssistantsSearch",
"StoreDelete",
"StoreGet",
"StoreSearch",
"StoreListNamespaces",
"StorePut",
"StoreDelete",
"StoreSearch",
"ThreadsCreate",
"ThreadsDelete",
"ThreadsRead",
"ThreadsSearch",
"ThreadsUpdate",
"on",
]
File diff suppressed because it is too large Load Diff

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