From e9bde462f2d8c6923817eacfde0155b9500c926c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:51:29 -0700 Subject: [PATCH 01/17] chore(deps-dev): bump types-requests from 2.33.0.20260712 to 2.33.0.20260906 in /libs/langgraph (#9156) Bumps [types-requests](https://github.com/python/typeshed) from 2.33.0.20260712 to 2.33.0.20260906.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=types-requests&package-manager=uv&previous-version=2.33.0.20260712&new-version=2.33.0.20260906)](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) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show 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)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/langgraph/uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index 452d26676..ce616e7ca 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -3636,14 +3636,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.33.0.20260712" +version = "2.33.0.20260906" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/db/51/703318f7b7be8bee126ec13bf615050f932d0179b8784420f3a0199cc769/types_requests-2.33.0.20260712.tar.gz", hash = "sha256:2141b67ab534a5c5cd2dac5034f2a35f42e699c5bf185eee608c5246a069d7fb", size = 25084, upload-time = "2026-07-12T05:14:20.455Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/18/4c2c0290953f8b3b9612adfcb07b57f144ade3ad32a76764fca42b77c5f3/types_requests-2.33.0.20260906.tar.gz", hash = "sha256:76ab8a0fb736744a0c3deee7aa57b2927e301f078d9e61f5391b3e92002416b9", size = 25263, upload-time = "2026-09-06T06:35:47.707Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/e7/010c87f559e216d83f9dc51e939633fd0d0ead3377340181ab0e223cd3b5/types_requests-2.33.0.20260712-py3-none-any.whl", hash = "sha256:de027e28c171d3da529689cbfa023b0b4eab188c8dfa22fd834eebd2cee6e7bb", size = 21392, upload-time = "2026-07-12T05:14:19.616Z" }, + { url = "https://files.pythonhosted.org/packages/60/4c/51ec821d22a45b4162fa3f3e9e94ea4c0f8c49e82363b10955baa5438391/types_requests-2.33.0.20260906-py3-none-any.whl", hash = "sha256:9f53622652bd921ead7a54d665be1b8d518165c8b51cc9d68d944cd6b6bfa8fb", size = 21461, upload-time = "2026-09-06T06:35:45.856Z" }, ] [[package]] From 7c8be00fd676e5d28d1d9680ab8890bbad0d4015 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:51:40 -0700 Subject: [PATCH 02/17] chore(deps): bump the minor-and-patch group in /libs/cli with 5 updates (#9153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group in /libs/cli with 5 updates: | Package | From | To | | --- | --- | --- | | [langgraph-sdk](https://github.com/langchain-ai/langgraph) | `0.4.4` | `0.4.5` | | [pytest-mock](https://github.com/pytest-dev/pytest-mock) | `3.15.1` | `3.16.0` | | [ruff](https://github.com/astral-sh/ruff) | `0.16.5` | `0.16.9` | | [ty](https://github.com/astral-sh/ty) | `0.0.75` | `0.0.84` | | [hatch](https://github.com/pypa/hatch) | `1.18.0` | `1.18.1` | Updates `langgraph-sdk` from 0.4.4 to 0.4.5
Release notes

Sourced from langgraph-sdk's releases.

langgraph-sdk==0.4.5

Changes since sdk==0.4.4

  • release(sdk-py): 0.4.5 (#8988)
  • release(langgraph): 1.2.12 (#8987)
  • chore(deps): bump anyio from 4.14.2 to 4.15.1 in /libs/sdk-py (#8997)
  • chore(deps): bump anyio from 4.13.0 to 4.14.2 in /libs/cli (#8998)
  • chore(deps): bump anyio from 4.12.1 to 4.14.2 in /libs/sdk-py (#8994)
  • feat(langgraph): add response_schema to interrupt() (#8886)
  • chore(deps): bump the minor-and-patch group across 1 directory with 7 updates (#8779)
  • chore(deps): bump websockets from 16.0 to 16.1.1 in /libs/sdk-py in the major group (#8781)
Commits

Updates `pytest-mock` from 3.15.1 to 3.16.0
Release notes

Sourced from pytest-mock's releases.

v3.16.0

2026-09-27

  • #604: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
  • #611: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
  • #606: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
  • #547: Added SpyType for annotating mocker.spy results.
  • Dropped support for EOL Python 3.9.
  • #147: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
  • Added support for Python 3.15.
Changelog

Sourced from pytest-mock's changelog.

3.16.0

2026-09-27

  • [#604](https://github.com/pytest-dev/pytest-mock/issues/604) <https://github.com/pytest-dev/pytest-mock/pull/604>_: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
  • [#611](https://github.com/pytest-dev/pytest-mock/issues/611) <https://github.com/pytest-dev/pytest-mock/pull/611>_: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
  • [#606](https://github.com/pytest-dev/pytest-mock/issues/606) <https://github.com/pytest-dev/pytest-mock/pull/606>_: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
  • [#547](https://github.com/pytest-dev/pytest-mock/issues/547) <https://github.com/pytest-dev/pytest-mock/issues/547>_: Added SpyType for annotating mocker.spy results.
  • Dropped support for EOL Python 3.9.
  • [#147](https://github.com/pytest-dev/pytest-mock/issues/147) <https://github.com/pytest-dev/pytest-mock/issues/147>_: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
  • Added support for Python 3.15.
Commits

Updates `ruff` from 0.16.5 to 0.16.9
Release notes

Sourced from ruff's releases.

0.16.9

Release Notes

Released on 2026-09-24.

Preview features

  • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

Bug fixes

  • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
  • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

Rule changes

  • Update LibCST-based fixes for Python 3.15 (#28616)
  • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

Documentation

  • Fix horizontal overflow on the rules documentation page (#28699)
  • Update rules table with category information (#28651)
  • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
  • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
  • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
  • [ruff] Mention related isort settings (RUF022) (#28719)

Contributors

Install ruff 0.16.9

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf
https://releases.astral.sh/github/ruff/releases/download/0.16.9/ruff-installer.sh
| sh

... (truncated)

Changelog

Sourced from ruff's changelog.

0.16.9

Released on 2026-09-24.

Preview features

  • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

Bug fixes

  • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
  • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

Rule changes

  • Update LibCST-based fixes for Python 3.15 (#28616)
  • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

Documentation

  • Fix horizontal overflow on the rules documentation page (#28699)
  • Update rules table with category information (#28651)
  • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
  • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
  • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
  • [ruff] Mention related isort settings (RUF022) (#28719)

Contributors

0.16.8

Released on 2026-09-16.

Bug fixes

  • Visit functional TypedDict keyword arguments correctly (#28584)
  • [flake8-simplify] Detect nested async with under sync parent (SIM117) (#27821)
  • [flake8-simplify] Preserve operand order in SIM109 fix (#27824)

... (truncated)

Commits

Updates `ty` from 0.0.75 to 0.0.84
Release notes

Sourced from ty's releases.

0.0.84

Release Notes

Released on 2026-09-24.

This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

Bug fixes

  • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

LSP server

  • Complete string keys from dictionary initializers (#28820)
  • Support LSP requests against closed documents (#28595)
  • Select projects for external files using import search paths (#28594)
  • Use workspace editor settings for external files (#28639)

Performance

  • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
  • Skip reading notebooks when discovering scripts (#28781)

Core type checking

  • Avoid incorrect simplification of TypeIs materializations (#28817)
  • Fix disjointness of generic class types (#28787)
  • Fix staticmethod shadowing through generic receivers and unions (#28766)
  • Infer callable signatures from bounded type variables (#28599)
  • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
  • Infer through optional generic containers in the legacy solver (#28791)
  • Preserve call narrowing during cyclic inference (#28708)
  • Preserve intersections of type guard return types (#28796)
  • Use subtyping for constraint-set implication (#28657)

Configuration

  • Disable invalid-legacy-positional-parameter by default (#28834)

Other changes

  • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

Contributors

... (truncated)

Changelog

Sourced from ty's changelog.

0.0.84

Released on 2026-09-24.

This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

Bug fixes

  • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

LSP server

  • Complete string keys from dictionary initializers (#28820)
  • Support LSP requests against closed documents (#28595)
  • Select projects for external files using import search paths (#28594)
  • Use workspace editor settings for external files (#28639)

Performance

  • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
  • Skip reading notebooks when discovering scripts (#28781)

Core type checking

  • Avoid incorrect simplification of TypeIs materializations (#28817)
  • Fix disjointness of generic class types (#28787)
  • Fix staticmethod shadowing through generic receivers and unions (#28766)
  • Infer callable signatures from bounded type variables (#28599)
  • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
  • Infer through optional generic containers in the legacy solver (#28791)
  • Preserve call narrowing during cyclic inference (#28708)
  • Preserve intersections of type guard return types (#28796)
  • Use subtyping for constraint-set implication (#28657)

Configuration

  • Disable invalid-legacy-positional-parameter by default (#28834)

Other changes

  • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

Contributors

... (truncated)

Commits

Updates `hatch` from 1.18.0 to 1.18.1
Release notes

Sourced from hatch's releases.

Hatch v1.18.1

Added:

  • Apply context formatting to the lock-filename environment option so fields such as {env_name} and {matrix:...} are resolved when computing the lock file path.

Fixed:

  • Consolidate extras and feature resolution into a single code path, fixing regressions where environment and project extras could be dropped or resolved inconsistently, and always validate undefined features.

  • Normalize hyphens in the plugin name when building environment option environment variable names in get_env_var(), so options for hyphenated plugins resolve to the correct variable.

Commits

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) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/cli/uv.lock | 94 ++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/libs/cli/uv.lock b/libs/cli/uv.lock index f02813d33..358690d42 100644 --- a/libs/cli/uv.lock +++ b/libs/cli/uv.lock @@ -678,7 +678,7 @@ wheels = [ [[package]] name = "hatch" -version = "1.18.0" +version = "1.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backports-zstd", marker = "python_full_version < '3.14'" }, @@ -701,9 +701,9 @@ dependencies = [ { name = "uv" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/fd/634c575b5becad2dfa7135f3ad66d57102cd77d834cf473756d868a808ff/hatch-1.18.0.tar.gz", hash = "sha256:463d214cb068391454be198e58d3cc0846f71e39d68c5568ba650af7781e8ae3", size = 5275084, upload-time = "2026-08-11T05:06:41.513Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/81/a5a77aaa23df824562d50ad25eeb52ae6446924d0dc68c7c19ed6d8b171f/hatch-1.18.1.tar.gz", hash = "sha256:222fff78e197cf12a39db7efe5318ca403f2614db213416fd106a6c3260ef221", size = 5277846, upload-time = "2026-09-16T16:56:20.886Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/b1/d14265cb26c57af1485c01fd95037a35f13748b718617405cdefc6150429/hatch-1.18.0-py3-none-any.whl", hash = "sha256:75874c0905fe76b44fd39585c49bc9bd2b9fee638abb11f1f3d087042e7f9122", size = 171310, upload-time = "2026-08-11T05:06:39.807Z" }, + { url = "https://files.pythonhosted.org/packages/04/a0/dc90fbe25bca29154d5926e1c96385d1c7a6feb120fa2c1b3e6098ad4d23/hatch-1.18.1-py3-none-any.whl", hash = "sha256:f005959995df45e5f5dd27c862dd77ce07e24e7f06aa8e51cbba2a40bac33a40", size = 172336, upload-time = "2026-09-16T16:56:19.332Z" }, ] [[package]] @@ -1207,7 +1207,7 @@ wheels = [ [[package]] name = "langgraph-sdk" -version = "0.4.4" +version = "0.4.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1216,9 +1216,9 @@ dependencies = [ { name = "orjson" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/ae/91446c1fffa04a2dc1f81afbfb5bfff3590452891a72bd9098a656ab2657/langgraph_sdk-0.4.4.tar.gz", hash = "sha256:4e651ffa09de695681579396375377bdde23bedbc8e35b070e615cbd5af7da8b", size = 345789, upload-time = "2026-08-27T21:25:22.593Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9c/7ff305b366ce986052a27dc5946839bef2823b3567664cbad22e294dff5a/langgraph_sdk-0.4.5.tar.gz", hash = "sha256:d49a98a2ee8e0c494b101a7caf8061c8b4b94d3ee5ada0ea2dbe50903a5487b1", size = 349783, upload-time = "2026-09-21T14:42:55.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/d5/2ad7f3a835c6fcebf58b6669552536ecd52d2dfe4cf49c6c36648fd83572/langgraph_sdk-0.4.4-py3-none-any.whl", hash = "sha256:39afe416c91742925e6f8a93715f566d499b36e1b636b804a4ffe3190e4f4e64", size = 162270, upload-time = "2026-08-27T21:25:21.072Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/71a6499f3481bc00bf4f42a17cb81caf19a02fce762db356626d7214e2c2/langgraph_sdk-0.4.5-py3-none-any.whl", hash = "sha256:e03ef033a20d21288af496e9b6c08ae3afcc1b58dc5abcd37575bfd1a93045de", size = 162304, upload-time = "2026-09-21T14:42:54.623Z" }, ] [[package]] @@ -1827,14 +1827,14 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.15.1" +version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/7f/6ed29931d5c8cd396e7c0a55412e6cc88020373365c8685985dea53d26d7/pytest_mock-3.16.0.tar.gz", hash = "sha256:5a8395528b8f498205f3718f575228d0edaed7425fff638f87d1a6c3e0383636", size = 35362, upload-time = "2026-09-27T14:57:55.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/5b/b83a9bf1a3b4ec222f9fa083147ff6816245223da0ab92370e7e056f113f/pytest_mock-3.16.0-py3-none-any.whl", hash = "sha256:007cfeb257801d88d9c0b2a7b5a15a15e73b71968dfd72e7bf8c4a2f8393aec8", size = 10016, upload-time = "2026-09-27T14:57:54.283Z" }, ] [[package]] @@ -1998,27 +1998,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.5" +version = "0.16.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, - { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, - { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, - { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, - { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, - { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, - { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, - { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" }, + { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" }, + { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" }, + { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" }, + { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" }, ] [[package]] @@ -2197,27 +2197,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.75" +version = "0.0.84" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, - { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, - { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, - { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, - { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, - { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, - { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" }, ] [[package]] From 3474bbff296f1fd58c14726f64284696e02eb2c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:51:50 -0700 Subject: [PATCH 03/17] chore(deps): bump the minor-and-patch group in /libs/cli/js-monorepo-example with 8 updates (#9151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group in /libs/cli/js-monorepo-example with 8 updates: | Package | From | To | | --- | --- | --- | | [turbo](https://github.com/vercel/turborepo) | `2.10.12` | `2.11.5` | | [@eslint/eslintrc](https://github.com/eslint/eslintrc) | `3.3.6` | `3.3.7` | | [eslint](https://github.com/eslint/eslint) | `10.9.1` | `10.11.0` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.68.0` | `8.70.1` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.68.0` | `8.70.1` | | [prettier](https://github.com/prettier/prettier) | `3.9.6` | `3.9.9` | | [@langchain/core](https://github.com/langchain-ai/langchainjs) | `1.2.9` | `1.2.13` | | [@langchain/langgraph](https://github.com/langchain-ai/langgraphjs/tree/HEAD/libs/langgraph-core) | `1.4.13` | `1.4.18` | Updates `turbo` from 2.10.12 to 2.11.5
Release notes

Sourced from turbo's releases.

Turborepo v2.11.5

What's Changed

Changelog

... (truncated)

Commits

Updates `@eslint/eslintrc` from 3.3.6 to 3.3.7
Release notes

Sourced from @​eslint/eslintrc's releases.

eslintrc: v3.3.7

3.3.7 (2026-09-01)

Bug Fixes

  • Bump js-yaml to 4.3.1 (#239) (f27e7c9)
  • update js-yaml to 4.3.2 to address security vulnerability (#243) (bb0d97a)
Changelog

Sourced from @​eslint/eslintrc's changelog.

3.3.7 (2026-09-01)

Bug Fixes

  • Bump js-yaml to 4.3.1 (#239) (f27e7c9)
  • update js-yaml to 4.3.2 to address security vulnerability (#243) (bb0d97a)
Commits

Updates `eslint` from 10.9.1 to 10.11.0
Release notes

Sourced from eslint's releases.

v10.11.0

Features

  • d136fa4 feat: object-shorthand handle quoted properties for ignoreConstructors (#21271) (Pavel)
  • 397b3b8 feat: report unsafe labeled continue in no-unsafe-finally rule (#21316) (electrohyun)
  • d3dd47f feat: only exempt new-cap built-ins that reference the global (#21290) (sethamus)

Bug Fixes

  • 22b09f5 fix: ignore __proto__ properties in prefer-object-spread (#21311) (xbinaryx)
  • b684bb1 fix: make TimePass.parse optional in types and docs (#21313) (ntnyq)
  • 26d11bc fix: don't report __proto__ properties in object-shorthand (#21310) (xbinaryx)

Documentation

  • 9ecfdc5 docs: note that --cache can serve stale results for cross-file rules (#21312) (bytedoe)
  • 6c789ff docs: Update README (GitHub Actions Bot)
  • 5997825 docs: clarify preserve-caught-error known limitation (#21294) (Akinyemi Toluwalase)

Chores

  • 520dd77 perf: Implement fast paths in critical areas (#21210) (Nicholas C. Zakas)
  • 92086c8 test: update EMFILE error generation for Node.js 26.9.0 compatibility (#21330) (Francesco Trotta)
  • 9ac7eb6 chore: update github/codeql-action action to v4.38.0 (#21331) (renovate[bot])
  • 24310e3 chore: update ecosystem plugins (#21324) (ESLint Bot)
  • 45ad79e ci: bump pnpm/action-setup from 6.0.10 to 6.1.0 (#21318) (dependabot[bot])
  • ac74e37 chore: Add AGENTS.md with AI disclosure requirements (#21221) (Nicholas C. Zakas)
  • c832660 chore: Upgrade Stylelint to the latest version in docs (#21245) (Jung Hyeon Jun)
  • f9f88fc chore: update ecosystem plugins (#21308) (ESLint Bot)
  • fc81076 ci: add more types integration tests (#20395) (Nitin Kumar)

v10.10.0

Features

  • 264b434 feat: add d and v flags to no-unexpected-multiline (#21305) (Gihyeon Jeong / 정기현)
  • c6cc6c5 feat: check Object.prototype property names in new-cap (#21269) (crimsonjay0)
  • 5661fa6 feat: no-extra-bind false negatives with class fields and static blocks (#21260) (synthex-byte)

Bug Fixes

  • bb47dc6 fix: update dependency file-entry-cache to v11 (#20801) (Milos Djermanovic)
  • 427ac0a fix: use format strings in debug calls (#21247) (Francesco Trotta)
  • 9d81532 fix: support __proto__ in /* exported */ comments (#21261) (sethamus)
  • 87e0a08 fix: prefer-object-has-own autofix breaks when Object is shadowed (#21282) (김채영)
  • 8e2cb14 fix: new-cap false positive for UTC calls with properties: false (#21275) (Pixel)
  • 9f4a364 fix: Ignore static imports in no-unreachable (#21276) (Taha Kotil)

Documentation

  • 2417cad docs: Update README (GitHub Actions Bot)
  • 9cecb8a docs: document \c control letter escapes in no-control-regex (#21286) (한국)
  • 8724829 docs: update compat table links (#21263) (fnx)
  • 5634542 docs: Clarify eqeqeq suggestion behavior (#21256) (Müslüm Yılmaz)

Chores

  • b3d876b chore: disable npm audit in ecosystem tests (#21306) (Francesco Trotta)
  • 1696682 ci: restore EMFILE test on Node.js 26 (#21297) (Marry (Subin Yang))

... (truncated)

Commits
  • 3c0b7c6 10.11.0
  • 321f0a7 Build: changelog update for 10.11.0
  • 520dd77 perf: Implement fast paths in critical areas (#21210)
  • 9ecfdc5 docs: note that --cache can serve stale results for cross-file rules (#21312)
  • 92086c8 test: update EMFILE error generation for Node.js 26.9.0 compatibility (#21330)
  • 9ac7eb6 chore: update github/codeql-action action to v4.38.0 (#21331)
  • 22b09f5 fix: ignore __proto__ properties in prefer-object-spread (#21311)
  • 24310e3 chore: update ecosystem plugins (#21324)
  • d136fa4 feat: object-shorthand handle quoted properties for ignoreConstructors (#21...
  • 45ad79e ci: bump pnpm/action-setup from 6.0.10 to 6.1.0 (#21318)
  • Additional commits viewable in compare view

Updates `@typescript-eslint/eslint-plugin` from 8.68.0 to 8.70.1
Release notes

Sourced from @​typescript-eslint/eslint-plugin's releases.

v8.70.1

8.70.1 (2026-09-21)

🩹 Fixes

  • ast-spec: narrow import attribute keys to identifiers and strings (#12879)
  • eslint-plugin: [no-useless-default-assignment] avoid false positives on tuples with a rest element (#12768)
  • eslint-plugin: [no-unnecessary-type-parameters] handle type precedence in the suggestion fixer (#12637)
  • eslint-plugin: [no-explicit-any] use unknown[] for bare any rest parameters (#12818)
  • eslint-plugin: [no-generated-empty-object-type] don't report a mapped type whose keys are not resolved yet (#12854)
  • eslint-plugin: [no-misused-spread] omit WeakMap spread suggestions (#12850)
  • eslint-plugin: [no-unnecessary-type-assertion] false positive for empty object asserted to a type alias of Record (#12869)
  • eslint-plugin: [no-meaningless-void-operator] allow void on assignment expressions (#12873)
  • eslint-plugin: [await-thenable] prevent autofix from breaking code when removing await (#12716)
  • eslint-plugin: [no-unnecessary-parameter-property-assignment] account for parameter reassignment (#12880)
  • eslint-plugin: [unbound-method] treat Intl.Collator.prototype.compare as spec-bound (#12845)
  • eslint-plugin: [no-unnecessary-condition] handle union-keyed index access on the left-hand side of nullish assignment (#12747)
  • eslint-plugin: [no-useless-default-assignment] convert the fixer to a suggestion fixer (#12826)
  • eslint-plugin: [no-misused-promises] handle multiple Promise constituents (#12904)
  • rule-tester: test the final autofix output instead of the first pass (#12867)
  • scope-manager: merge implicit global definitions (#12809)
  • type-utils: match package specifiers on whole path components (#12838)
  • typescript-estree: resolve symlinked paths when matching files to projects (#12725)
  • typescript-estree: add missing < token opening type arguments (#12821)
  • typescript-estree: require string literal import attribute values (#12894)
  • website: prevent playground from breaking down after opening link with the .js file type (#12777)

❤️ Thank You

See GitHub Releases for more information.

... (truncated)

Changelog

Sourced from @​typescript-eslint/eslint-plugin's changelog.

8.70.1 (2026-09-21)

🩹 Fixes

  • eslint-plugin: [no-misused-promises] handle multiple Promise constituents (#12904)
  • eslint-plugin: [no-useless-default-assignment] convert the fixer to a suggestion fixer (#12826)
  • eslint-plugin: [no-unnecessary-condition] handle union-keyed index access on the left-hand side of nullish assignment (#12747)
  • eslint-plugin: [unbound-method] treat Intl.Collator.prototype.compare as spec-bound (#12845)
  • eslint-plugin: [no-unnecessary-parameter-property-assignment] account for parameter reassignment (#12880)
  • eslint-plugin: [await-thenable] prevent autofix from breaking code when removing await (#12716)
  • eslint-plugin: [no-meaningless-void-operator] allow void on assignment expressions (#12873)
  • eslint-plugin: [no-unnecessary-type-assertion] false positive for empty object asserted to a type alias of Record (#12869)
  • eslint-plugin: [no-misused-spread] omit WeakMap spread suggestions (#12850)
  • eslint-plugin: [no-generated-empty-object-type] don't report a mapped type whose keys are not resolved yet (#12854)
  • eslint-plugin: [no-explicit-any] use unknown[] for bare any rest parameters (#12818)
  • eslint-plugin: [no-unnecessary-type-parameters] handle type precedence in the suggestion fixer (#12637)
  • eslint-plugin: [no-useless-default-assignment] avoid false positives on tuples with a rest element (#12768)

❤️ Thank You

See GitHub Releases for more information.

You can read about our versioning strategy and releases on our website.

8.70.0 (2026-09-07)

🚀 Features

  • eslint-plugin: [no-generated-empty-object-type] add rule (#12730)

🩹 Fixes

  • eslint-plugin: [no-deprecated] report deprecated imported values used in object shorthand properties (#12780)
  • eslint-plugin: [no-unnecessary-condition] no false positive on RHS of a nested logical expression (#12728)
  • eslint-plugin: [member-ordering] don't report fields that read fields declared before them (#12729)

❤️ Thank You

... (truncated)

Commits
  • 23d38ce chore(release): publish 8.70.1
  • 9d82e4b chore: expand eslint-plugin rules test files glob (#12878)
  • f7482f6 fix(eslint-plugin): [no-misused-promises] handle multiple Promise constituent...
  • 2673044 docs(eslint-plugin): [prefer-promise-reject-errors] add option sections and e...
  • 8f141a2 fix(eslint-plugin): [no-useless-default-assignment] convert the fixer to a su...
  • 479cd85 chore: enable assertion option requireData for all rule tests (#12816)
  • f3c190c fix(eslint-plugin): [no-unnecessary-condition] handle union-keyed index acces...
  • b1993df fix(eslint-plugin): [unbound-method] treat Intl.Collator.prototype.compare as...
  • da394bc fix(eslint-plugin): [no-unnecessary-parameter-property-assignment] account fo...
  • 4a742ff fix(eslint-plugin): [await-thenable] prevent autofix from breaking code when ...
  • Additional commits viewable in compare view

Updates `@typescript-eslint/parser` from 8.68.0 to 8.70.1
Release notes

Sourced from @​typescript-eslint/parser's releases.

v8.70.1

8.70.1 (2026-09-21)

🩹 Fixes

  • ast-spec: narrow import attribute keys to identifiers and strings (#12879)
  • eslint-plugin: [no-useless-default-assignment] avoid false positives on tuples with a rest element (#12768)
  • eslint-plugin: [no-unnecessary-type-parameters] handle type precedence in the suggestion fixer (#12637)
  • eslint-plugin: [no-explicit-any] use unknown[] for bare any rest parameters (#12818)
  • eslint-plugin: [no-generated-empty-object-type] don't report a mapped type whose keys are not resolved yet (#12854)
  • eslint-plugin: [no-misused-spread] omit WeakMap spread suggestions (#12850)
  • eslint-plugin: [no-unnecessary-type-assertion] false positive for empty object asserted to a type alias of Record (#12869)
  • eslint-plugin: [no-meaningless-void-operator] allow void on assignment expressions (#12873)
  • eslint-plugin: [await-thenable] prevent autofix from breaking code when removing await (#12716)
  • eslint-plugin: [no-unnecessary-parameter-property-assignment] account for parameter reassignment (#12880)
  • eslint-plugin: [unbound-method] treat Intl.Collator.prototype.compare as spec-bound (#12845)
  • eslint-plugin: [no-unnecessary-condition] handle union-keyed index access on the left-hand side of nullish assignment (#12747)
  • eslint-plugin: [no-useless-default-assignment] convert the fixer to a suggestion fixer (#12826)
  • eslint-plugin: [no-misused-promises] handle multiple Promise constituents (#12904)
  • rule-tester: test the final autofix output instead of the first pass (#12867)
  • scope-manager: merge implicit global definitions (#12809)
  • type-utils: match package specifiers on whole path components (#12838)
  • typescript-estree: resolve symlinked paths when matching files to projects (#12725)
  • typescript-estree: add missing < token opening type arguments (#12821)
  • typescript-estree: require string literal import attribute values (#12894)
  • website: prevent playground from breaking down after opening link with the .js file type (#12777)

❤️ Thank You

See GitHub Releases for more information.

... (truncated)

Changelog

Sourced from @​typescript-eslint/parser's changelog.

8.70.1 (2026-09-21)

This was a version bump only for parser to align it with other projects, there were no code changes.

See GitHub Releases for more information.

You can read about our versioning strategy and releases on our website.

8.70.0 (2026-09-07)

This was a version bump only for parser to align it with other projects, there were no code changes.

See GitHub Releases for more information.

You can read about our versioning strategy and releases on our website.

8.69.0 (2026-08-31)

This was a version bump only for parser to align it with other projects, there were no code changes.

See GitHub Releases for more information.

You can read about our versioning strategy and releases on our website.

Commits

Updates `prettier` from 3.9.6 to 3.9.9
Release notes

Sourced from prettier's releases.

3.9.9

  • Markdown: Fix text with $ been incorrectly parsed as math syntax (#20140 by @​fisker)

🔗 Changelog

3.9.8

  • Markdown: Don't let Liquid objects interrupt paragraphs (#20087 by @​seiyab)

🔗 Changelog

3.9.7

  • Support Angular 22.2
  • Fix regressions in v3.9

🔗 Changelog

Changelog

Sourced from prettier's changelog.

3.9.9

diff

Markdown: Fix text with $ been incorrectly parsed as math syntax (#20140 by @​fisker)

<!-- Input -->
**Uses $FOO** from `a.sh` and `b.sh`, plus `$BAR` from `c.sh`, before
anything else runs here.

<!-- Prettier 3.9.8 --> Uses $FOO from a.sh and b.sh, plus $BARfromc.sh, before anything else runs here.

<!-- Prettier 3.9.9 --> Uses $FOO from a.sh and b.sh, plus $BAR from c.sh, before anything else runs here.

3.9.8

diff

Markdown: Don't let Liquid objects interrupt paragraphs (#20087 by @​seiyab)

<!-- Input -->
If `module` is not a
[`WebAssembly.Module`](https://github.com/prettier/prettier/blob/main/en-US/docs/WebAssembly/Reference/JavaScript_interface/Module)
object instance, a
{{jsxref("TypeError")}} is thrown.

<!-- Prettier 3.9.7 --> If module is not a WebAssembly.Module object instance, a

{{jsxref("TypeError")}} is thrown.

<!-- Prettier 3.9.8 --> If module is not a WebAssembly.Module object instance, a {{jsxref("TypeError")}} is thrown.

3.9.7

diff

Markdown: Prevent indentation drift in list-item code blocks (#19647, #19990 by @​Austin1serb, @​giaBaoJS)

<!-- Input -->
- [x] short first line.
</tr></table>

... (truncated)

Commits

Updates `@langchain/core` from 1.2.9 to 1.2.13
Release notes

Sourced from @​langchain/core's releases.

@​langchain/core@​1.2.13

Patch Changes

  • #11714 a83dfb1 Thanks @​ccurme! - Abbreviate long tool-call IDs when formatting chat messages as strings, keeping original messages unchanged.

@​langchain/core@​1.2.12

Patch Changes

@​langchain/core@​1.2.11

Patch Changes

  • #11603 fec9cd8 Thanks @​thushanth-bengre-langchain! - fix(core): build streaming llmOutput.tokenUsage from the fully-accumulated chunk instead of whichever individual chunk's usage_metadata arrived last

    Affects both core streaming paths — .stream()/.streamEvents() (_streamIterator) and .invoke()/.generate() when a streaming-preferring callback is attached (_generateWithCache's hasStreamingHandler branch). Previously, llmOutput.tokenUsage was overwritten by each chunk in turn, so only the last chunk carrying usage_metadata won — correct for providers that emit one cumulative total on a final chunk, but wrong for providers (e.g. @langchain/google, @langchain/anthropic) that emit usage_metadata as a per-chunk delta across multiple chunks, where the values must be summed.

    Note for provider authors: this assumes each streamed chunk's usage_metadata is either a per-chunk delta or appears only on a single final chunk. A provider that instead repeats a cumulative total on every chunk will now see it summed (and inflated) in llmOutput.tokenUsage, matching the existing behavior of the correctly-working message.usage_metadata field.

    Also fixes @langchain/google's invoke({streaming: true}) path (no streaming-preferring callback attached), where llmOutput was never populated at all.

  • #11590 ffebdc2 Thanks @​thushanth-bengre-langchain! - Fix OpenAI Responses API replay under Zero Data Retention when a response contains more than one reasoning item, for both v0 and v1. In v0, the default replay path now reuses response_metadata.output directly, preserving every reasoning item's id/encrypted_content in original order. In v1, AIMessage.contentBlocks (outputVersion: "v1") is fixed the same way. additional_kwargs.reasoning is unchanged.

@​langchain/core@​1.2.10

Patch Changes

Commits
  • c8d1278 chore: version packages (#11729)
  • 5d509ad fix(openai): send in-memory prompt cache retention as in_memory (#11735)
  • 663b9a2 feat(openai): support explicit prompt caching (#11232)
  • 7735b21 chore(infra): drop held mcp-adapters 2.0 changeset to unblock main releases (...
  • 34edee1 fix(langchain): return failed returnDirect tool calls to the model (#11712)
  • 0fcb98b fix(textsplitters): use char length for loc.lines with custom lengthFunction ...
  • e4a3d1b chore: version packages (#11683)
  • 11.1.6 <12" find-up "^5.0.0" glob-parent "^6.0.2" ignore "^5.2.0" @@ -999,12 +1036,12 @@ fdir@^6.5.0: resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== -file-entry-cache@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" - integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== +"file-entry-cache@11.1.5 || >11.1.6 <12": + version "11.1.5" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-11.1.5.tgz#c8210eb055de63e68685ccfb6a017e386d4577d0" + integrity sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q== dependencies: - flat-cache "^4.0.0" + flat-cache "^6.1.23" find-up@^5.0.0: version "5.0.0" @@ -1014,18 +1051,19 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" - integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== +flat-cache@^6.1.23: + version "6.1.23" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-6.1.23.tgz#735dc888c271868d7301b3bbb8edba5028afb61d" + integrity sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA== dependencies: - flatted "^3.2.9" - keyv "^4.5.4" + cacheable "^2.5.0" + flatted "^3.4.2" + hookified "^1.15.0" -flatted@^3.2.9: - version "3.4.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" - integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== +flatted@^3.4.2: + version "3.4.4" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.4.tgz#aeeca2a506303f0cee61c59e6c9f2a88d2f29fc6" + integrity sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q== for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" @@ -1145,6 +1183,13 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" +hashery@^1.4.0, hashery@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/hashery/-/hashery-1.5.1.tgz#4ba82ad54911ac617467870845d57a9fe508a400" + integrity sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ== + dependencies: + hookified "^1.15.0" + hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -1152,6 +1197,16 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hookified@^1.15.0, hookified@^1.15.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/hookified/-/hookified-1.15.1.tgz#b1fafeaa5489cdc29cb85546a8f837ed4ffbbcb6" + integrity sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg== + +hookified@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hookified/-/hookified-2.2.0.tgz#1d024ac1668973dd5bcc4a96ab9ccdb7639ef8d4" + integrity sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA== + ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -1390,18 +1445,13 @@ js-tiktoken@^1.0.12: dependencies: base64-js "^1.5.1" -js-yaml@^4.3.0: +js-yaml@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.3.2.tgz#8e44fb14a2643c59726bb15787b5f1512cb3d3fb" integrity sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA== dependencies: argparse "^2.0.1" -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -1419,12 +1469,12 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -keyv@^4.5.4: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== +keyv@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-5.6.0.tgz#03044074c6b4d072d0a62c7b9fa649537baf0105" + integrity sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw== dependencies: - json-buffer "3.0.1" + "@keyv/serialize" "^1.1.1" "langsmith@>=0.5.0 <1.0.0": version "0.7.1" @@ -1657,16 +1707,23 @@ prettier-linter-helpers@^1.0.1: dependencies: fast-diff "^1.1.2" -prettier@^3.9.6: - version "3.9.6" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.6.tgz#b3ea5146515d40fc53f18aa63f74dfab1e10dbf6" - integrity sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g== +prettier@^3.9.9: + version "3.9.9" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.9.tgz#09b826918c91cd4cbc80e0cbd1d2a922ff04f233" + integrity sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg== punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qified@^0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/qified/-/qified-0.10.1.tgz#0640bf21bbe6ca540db290ad8f5fde4d870b8bda" + integrity sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA== + dependencies: + hookified "^2.1.1" + reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" @@ -1913,17 +1970,17 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -turbo@^2.10.12: - version "2.10.12" - resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.10.12.tgz#22f552bd88182d58365960a02e5f45e628fb965b" - integrity sha512-AswgMPnpOoaVZHrrSBejETzEbuIA69OVGwfkHwfrY0A23VjWXBANzgq9+OymWOHAIArB7D1+1z498WY8fGg1Jw== +turbo@^2.11.5: + version "2.11.5" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.11.5.tgz#e7223abd0555b978d3efe1b00ea9a98f43afb7ab" + integrity sha512-qLXheqz3TUvJpHIV6MUBwkNkvwKxMVG/QsimyiKT9DfNWPHJ294G/i5nDaH+fbHJt3UH7orubNNxAqYkbUNGAQ== optionalDependencies: - "@turbo/darwin-64" "2.10.12" - "@turbo/darwin-arm64" "2.10.12" - "@turbo/linux-64" "2.10.12" - "@turbo/linux-arm64" "2.10.12" - "@turbo/windows-64" "2.10.12" - "@turbo/windows-arm64" "2.10.12" + "@turbo/darwin-64" "2.11.5" + "@turbo/darwin-arm64" "2.11.5" + "@turbo/linux-64" "2.11.5" + "@turbo/linux-arm64" "2.11.5" + "@turbo/windows-64" "2.11.5" + "@turbo/windows-arm64" "2.11.5" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" From 3a2501e8c5924213d631089dfcd66f1c0d49ed72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:52:01 -0700 Subject: [PATCH 04/17] chore(deps): bump the minor-and-patch group in /libs/checkpoint with 4 updates (#9148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group in /libs/checkpoint with 4 updates: [langchain-core](https://github.com/langchain-ai/langchain), [pytest-mock](https://github.com/pytest-dev/pytest-mock), [ruff](https://github.com/astral-sh/ruff) and [ty](https://github.com/astral-sh/ty). Updates `langchain-core` from 1.6.1 to 1.6.5
    Release notes

    Sourced from langchain-core's releases.

    langchain-core==1.6.5

    Changes since langchain-core==1.6.4

    release(core): 1.6.5 (#40816) fix(core): abbreviate long tool IDs in XML buffer strings (#40792)

    langchain-core==1.6.4

    Changes since langchain-core==1.6.3

    release(core): 1.6.4 (#40718) chore(core): deprecate chat message history (#40711) chore(deps): bump anyio from 4.12.0 to 4.14.2 in /libs/core (#40634) chore(deps): bump soupsieve from 2.8.4 to 2.9 in /libs/core (#40574)

    langchain-core==1.6.3

    Changes since langchain-core==1.6.2

    release(core): 1.6.3 (#40407) feat(core): Allow model name and provider tracing metadata override based on gateway response (#40406) test(core): cover the deprecated .text() access path (#40243) docs(core): remove stale Args/Raises entries from FileCallbackHandler._write and ChatGeneration.set_text (#40211)

    langchain-core==1.6.2

    Changes since langchain-core==1.6.1

    release(core): 1.6.2 (#40209) feat(openai): support async tools (#40208) chore(deps): bump mistune from 3.3.0 to 3.3.3 in /libs/core (#40150) chore(deps): bump tornado from 6.5.7 to 6.5.8 in /libs/core (#40113) fix(core): avoid mutation in google-genai standard content (#40023) fix(core): avoid mutation in bedrock converse standard content (#40022)

    Commits

    Updates `pytest-mock` from 3.15.1 to 3.16.0
    Release notes

    Sourced from pytest-mock's releases.

    v3.16.0

    2026-09-27

    • #604: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • #611: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • #606: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • #547: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • #147: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Changelog

    Sourced from pytest-mock's changelog.

    3.16.0

    2026-09-27

    • [#604](https://github.com/pytest-dev/pytest-mock/issues/604) <https://github.com/pytest-dev/pytest-mock/pull/604>_: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • [#611](https://github.com/pytest-dev/pytest-mock/issues/611) <https://github.com/pytest-dev/pytest-mock/pull/611>_: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • [#606](https://github.com/pytest-dev/pytest-mock/issues/606) <https://github.com/pytest-dev/pytest-mock/pull/606>_: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • [#547](https://github.com/pytest-dev/pytest-mock/issues/547) <https://github.com/pytest-dev/pytest-mock/issues/547>_: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • [#147](https://github.com/pytest-dev/pytest-mock/issues/147) <https://github.com/pytest-dev/pytest-mock/issues/147>_: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Commits

    Updates `ruff` from 0.16.5 to 0.16.9
    Release notes

    Sourced from ruff's releases.

    0.16.9

    Release Notes

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    Install ruff 0.16.9

    Install prebuilt binaries via shell script

    curl --proto '=https' --tlsv1.2 -LsSf
    https://releases.astral.sh/github/ruff/releases/download/0.16.9/ruff-installer.sh
    | sh
    

    ... (truncated)

    Changelog

    Sourced from ruff's changelog.

    0.16.9

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    0.16.8

    Released on 2026-09-16.

    Bug fixes

    • Visit functional TypedDict keyword arguments correctly (#28584)
    • [flake8-simplify] Detect nested async with under sync parent (SIM117) (#27821)
    • [flake8-simplify] Preserve operand order in SIM109 fix (#27824)

    ... (truncated)

    Commits
    • 0be08a2 Bump version to 0.16.9 (#28882)
    • b4920b7 Rename ruff_cli to ruff_command_line (#28881)
    • 47c751b Update dependency astral-sh/uv to v0.12.18 (#28880)
    • 8c244e5 [flake8-comprehensions] Document map/generator exception behavior (C417...
    • 5edf5a1 Use target form in rooster.version_files (#28876)
    • 915bb2b [ty] Prefer existing @ paths over response files in Ruff and ty (#28877)
    • 4710e1a ci(github): update version number in placeholder of issue template (#28871)
    • eedfc62 [ty] Propagate outer type context through cast calls (#28855)
    • ceaa6a0 [ty] Contain rendered code within Markdown fences (#28869)
    • dba0f30 authorize ruff-pre-commit dispatch via OIDC (#28867)
    • Additional commits viewable in compare view

    Updates `ty` from 0.0.75 to 0.0.84
    Release notes

    Sourced from ty's releases.

    0.0.84

    Release Notes

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Changelog

    Sourced from ty's changelog.

    0.0.84

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Commits

    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) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/checkpoint/uv.lock | 88 ++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/libs/checkpoint/uv.lock b/libs/checkpoint/uv.lock index a7d96a383..db2022cbf 100644 --- a/libs/checkpoint/uv.lock +++ b/libs/checkpoint/uv.lock @@ -268,7 +268,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.6.1" +version = "1.6.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -282,9 +282,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/12/aff76ca89c219ebe6f9dd3c5dbc4e3b1cf5450e9fc7037dccad23d45cd7a/langchain_core-1.6.1.tar.gz", hash = "sha256:1b156cb395aac4f009a8a1b38a574c7d948fe2d5f74c96e0d8a5017b4149e04f", size = 1003359, upload-time = "2026-08-27T19:31:14.956Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/ed/ceecd3bfcfc77a94cf30a9f1506e907fdea447458825e01914a72d2d1a6f/langchain_core-1.6.5.tar.gz", hash = "sha256:bdb059fef65473fd444558dbb18f50901657402c43ceed9dc0fdc7b76a393c6b", size = 1007041, upload-time = "2026-09-24T18:11:06.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/25/f50dd65673c819aa33d3c34df58c115dbb6ec627d19f93e6e401dd0fc8d7/langchain_core-1.6.1-py3-none-any.whl", hash = "sha256:954a84132a5cb0435d27b910e336347b6744ecc18fbeef1e2de7029a0959841a", size = 571478, upload-time = "2026-08-27T19:31:13.34Z" }, + { url = "https://files.pythonhosted.org/packages/59/68/447ac7e734990b7ed9dcfc3b6dee9a4cd4d4169e296429fa71a9a47c1c7b/langchain_core-1.6.5-py3-none-any.whl", hash = "sha256:54c7b0e9314b9084fb04405bb33dc5d32986d512d92b7b8863899cd5f6267556", size = 572136, upload-time = "2026-09-24T18:11:04.837Z" }, ] [[package]] @@ -1015,14 +1015,14 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.15.1" +version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/7f/6ed29931d5c8cd396e7c0a55412e6cc88020373365c8685985dea53d26d7/pytest_mock-3.16.0.tar.gz", hash = "sha256:5a8395528b8f498205f3718f575228d0edaed7425fff638f87d1a6c3e0383636", size = 35362, upload-time = "2026-09-27T14:57:55.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/5b/b83a9bf1a3b4ec222f9fa083147ff6816245223da0ab92370e7e056f113f/pytest_mock-3.16.0-py3-none-any.whl", hash = "sha256:007cfeb257801d88d9c0b2a7b5a15a15e73b71968dfd72e7bf8c4a2f8393aec8", size = 10016, upload-time = "2026-09-27T14:57:54.283Z" }, ] [[package]] @@ -1164,27 +1164,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.5" +version = "0.16.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, - { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, - { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, - { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, - { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, - { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, - { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, - { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" }, + { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" }, + { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" }, + { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" }, + { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" }, ] [[package]] @@ -1261,27 +1261,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.75" +version = "0.0.84" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, - { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, - { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, - { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, - { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, - { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, - { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" }, ] [[package]] From b1765f3d0f581bee468cfb186c7aa3e07dc85f96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:52:12 -0700 Subject: [PATCH 05/17] chore(deps): bump the minor-and-patch group in /libs/checkpoint-postgres with 6 updates (#9147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group in /libs/checkpoint-postgres with 6 updates: | Package | From | To | | --- | --- | --- | | [psycopg](https://github.com/psycopg/psycopg) | `3.3.4` | `3.3.6` | | [psycopg-pool](https://github.com/psycopg/psycopg) | `3.3.1` | `3.3.3` | | [anyio](https://github.com/agronholm/anyio) | `4.14.2` | `4.15.1` | | [pytest-mock](https://github.com/pytest-dev/pytest-mock) | `3.15.1` | `3.16.0` | | [ruff](https://github.com/astral-sh/ruff) | `0.16.5` | `0.16.9` | | [ty](https://github.com/astral-sh/ty) | `0.0.75` | `0.0.84` | Updates `psycopg` from 3.3.4 to 3.3.6
    Changelog

    Sourced from psycopg's changelog.

    .. currentmodule:: psycopg

    .. index:: single: Release notes single: News

    psycopg release notes

    Future releases

    Psycopg 3.3.7 (unreleased) ^^^^^^^^^^^^^^^^^^^^^^^^^^

    • Fix segfault fetching arrays of strings or timestamps after closing the connection (:ticket:[#1428](https://github.com/psycopg/psycopg/issues/1428)).

    Current release

    Psycopg 3.3.6 ^^^^^^^^^^^^^

    • Add support for Python 3.15 (:ticket:[#1245](https://github.com/psycopg/psycopg/issues/1245)).
    • Improve performance of async queries by reducing the overhead of the !wait_async() function (:ticket:[#1331](https://github.com/psycopg/psycopg/issues/1331)).
    • Don't wait forever for a query to terminate after interrupting it, for instance if the server is unresponsive. The fix requires libpq 17 or newer (:ticket:[#1371](https://github.com/psycopg/psycopg/issues/1371)).
    • Cancel a running query upon receiving !SystemExit (:ticket:[#1384](https://github.com/psycopg/psycopg/issues/1384)).
    • Report !None instead of 65535 as the Column.precision of an :sql:interval column declared with a fields restriction and no explicit precision, such as e.g. :sql:interval day to second (:ticket:[#1397](https://github.com/psycopg/psycopg/issues/1397)).
    • Fix dumping of nested subclasses of lists as arrays (:ticket:[#1398](https://github.com/psycopg/psycopg/issues/1398)).
    • Discard prepared statements upon :sql:DEALLOCATE ALL (:ticket:[#1408](https://github.com/psycopg/psycopg/issues/1408)).
    • Better guards dumping large Python !int to binary numeric (:ticket:[#1414](https://github.com/psycopg/psycopg/issues/1414)).

    Psycopg 3.3.5 ^^^^^^^^^^^^^

    • Discard prepared statements upon :sql:ALTER * or DISCARD * (:ticket:[#1307](https://github.com/psycopg/psycopg/issues/1307)).
    • Fix !ProgrammingError when dumping non-!None values with no !NoneType dumper registered in python implementation (:ticket:[#1325](https://github.com/psycopg/psycopg/issues/1325)).
    • Fix !wait_selector wait function to not raise !KeyError (:ticket:[#1327](https://github.com/psycopg/psycopg/issues/1327)).
    • Fix !DataError messages leaking the literal {...} placeholder instead

    ... (truncated)

    Commits
    • a67654d chore: bump psycopg package version to 3.3.6
    • 443814b Merge pull request #1416 from dvarrazzo/wait-async-perf
    • 42966e9 test: add helpful comments to some tests
    • 5e8797f test: add reasonable connect_timeout to most tests
    • c81ba62 perf: reduce the overhead of wait_async()
    • d2bbfe4 refactor: use get_running_loop() in the async wait functions
    • 2b1484a test: add a script to measure the async wait functions
    • 573cf4a test: fix incorrect wait timing test
    • 2b68990 refactor: drop leftovers of waiting with inf interval in wait_conn_async
    • 60765dd Merge pull request #1414 from dvarrazzo/fix-decimal-overflow
    • Additional commits viewable in compare view

    Updates `psycopg-pool` from 3.3.1 to 3.3.3
    Changelog

    Sourced from psycopg-pool's changelog.

    .. currentmodule:: psycopg

    .. index:: single: Release notes single: News

    psycopg release notes

    Future releases

    Psycopg 3.3.7 (unreleased) ^^^^^^^^^^^^^^^^^^^^^^^^^^

    • Fix segfault fetching arrays of strings or timestamps after closing the connection (:ticket:[#1428](https://github.com/psycopg/psycopg/issues/1428)).

    Current release

    Psycopg 3.3.6 ^^^^^^^^^^^^^

    • Add support for Python 3.15 (:ticket:[#1245](https://github.com/psycopg/psycopg/issues/1245)).
    • Improve performance of async queries by reducing the overhead of the !wait_async() function (:ticket:[#1331](https://github.com/psycopg/psycopg/issues/1331)).
    • Don't wait forever for a query to terminate after interrupting it, for instance if the server is unresponsive. The fix requires libpq 17 or newer (:ticket:[#1371](https://github.com/psycopg/psycopg/issues/1371)).
    • Cancel a running query upon receiving !SystemExit (:ticket:[#1384](https://github.com/psycopg/psycopg/issues/1384)).
    • Report !None instead of 65535 as the Column.precision of an :sql:interval column declared with a fields restriction and no explicit precision, such as e.g. :sql:interval day to second (:ticket:[#1397](https://github.com/psycopg/psycopg/issues/1397)).
    • Fix dumping of nested subclasses of lists as arrays (:ticket:[#1398](https://github.com/psycopg/psycopg/issues/1398)).
    • Discard prepared statements upon :sql:DEALLOCATE ALL (:ticket:[#1408](https://github.com/psycopg/psycopg/issues/1408)).
    • Better guards dumping large Python !int to binary numeric (:ticket:[#1414](https://github.com/psycopg/psycopg/issues/1414)).

    Psycopg 3.3.5 ^^^^^^^^^^^^^

    • Discard prepared statements upon :sql:ALTER * or DISCARD * (:ticket:[#1307](https://github.com/psycopg/psycopg/issues/1307)).
    • Fix !ProgrammingError when dumping non-!None values with no !NoneType dumper registered in python implementation (:ticket:[#1325](https://github.com/psycopg/psycopg/issues/1325)).
    • Fix !wait_selector wait function to not raise !KeyError (:ticket:[#1327](https://github.com/psycopg/psycopg/issues/1327)).
    • Fix !DataError messages leaking the literal {...} placeholder instead

    ... (truncated)

    Commits
    • 1a8f65a chore: bump psycopg package version to 3.3.3
    • db3c435 Merge pull request #1260 from ggevay/sync-error-fix
    • 0237586 Fix ValueError when server sends ErrorResponse during Sync after Parse
    • cb97ef7 docs: fix typos
    • 09c8918 Merge pull request #1256 from veeceey/fix/tstrings-error-msg-and-docs-improve...
    • 9e74d96 fix: fix error message incorrectly generated by Claude AI
    • 0db9d8b fix: correct typo in tstrings error message and fix sql.rst docs
    • 86a0e1b chore(deps): bump pypa/cibuildwheel in the actions group
    • f5d90fa Merge pull request #1233 from lysnikolaou/pgconn-critical-section
    • d7dc6c7 Merge critical section and nogil blocks into one context manager
    • Additional commits viewable in compare view

    Updates `anyio` from 4.14.2 to 4.15.1
    Release notes

    Sourced from anyio's releases.

    4.15.1

    • Implemented a compatibility fix for supporting direct access of anyio.* submodules from the main package even when those submodules were not directly imported first (#1311 <agronholm/anyio#1311)

    4.15.0

    • Added support for the newer keyword-only arguments on anyio.Path methods to match the standard library pathlib.Path:

      • follow_symlinks on exists() (Python 3.12+)
      • follow_symlinks on is_dir() (Python 3.13+)
      • follow_symlinks on is_file() (Python 3.13+)
      • follow_symlinks on owner() (Python 3.13+)
      • follow_symlinks on group() (Python 3.13+)
      • newline on read_text() (Python 3.13+)

      (#1286, #1293; PR by @​jaideeppyne)

    • Added amap, gather, and as_completed utility functions to simplify common patterns (#1173; PR by @​Graeme22)

    • Added --anyio-mode command-line option as an alternative to the anyio_mode ini setting, and fix the pytest plugin's auto mode detection to recognize the mode when set via either mechanism(e.g: pytest_asyncio). (#1242; PR by @​EmmanuelNiyonshuti)

    • Added the anyio.Future synchronization primitive which behaves similar to asyncio.Future, allowing tasks to wait for a value (or exception) from another task (#1146; PR by @​Vizonex)

    • Added guidance for managing multiple memory object stream producers and consumers with cloned streams (#330; PR by @​nightcityblade)

    • Added StapledObjectStream.send_nowait() that delegates to the underlying ObjectSendStream, if it implements it (#1241; PR by @​davidbrochart)

    • Added the move_on_at() and fail_at() functions to complement move_on_after() and fail_after()

    • Changed the default name for a task spawned with TaskGroup.create_task(func()) to match the default task name for the analogous task spawned with TaskGroup.start_soon(func) or TaskGroup.start(func) in more situations. Previously, the default name of a TaskGroup.create_task task never included the module name. (The default name for a task spawned with TaskGroup.start_soon or TaskGroup.start typically includes the module name.) (#1234; PR by @​gschaffner)

    • Changed the anyio and anyio.abc modules to lazily (much like 810) import the necessary submodules. This is done by parsing the AST of the module and building a lookup table from the if TYPE_CHECKING: block. A fallback mode has been provided for installations where the source code is unavailable (e.g. PyInstaller). (#1169)

    • Fixed free-threading compatibility issues arising from the fact that on Python 3.14 free-threading builds, newly created threads inherit the current context by default, causing AnyIO to behave erroneously in relation to start_blocking_portal() and anyio.to_thread.run_sync() (#1224; PR by @​EmmanuelNiyonshuti)

    • Fixed SpooledTemporaryFile.readinto() and readinto1() reading twice before rollover, so the destination buffer was overwritten by the second read and the file position advanced twice, silently losing data (#1215; PR by @​c-tonneslan)

    • Added a reason parameter to fail_after (and the new fail_at) allowing for added exception context when raising TimeoutError (#1227; PR by @​Graeme22)

    • Fixed the default TaskHandle.name missing part of the task name for tasks started with TaskGroup.start on Trio (#1231; PR by @​gschaffner)

    • Fixed anyio.run leaking, or at least, delaying collection of loop and root_task due to the root task being cached in a RunVar. (#1203; PR by @​tapetersen)

    • Fixed anyio.Path.with_stem() silently producing a wrong path (e.g. Path(".txt")) instead of raising ValueError when given an empty stem on a path with a non-empty suffix, unlike pathlib.PurePath.with_stem (#1200; PR by @​Sanjays2402)

    • Fixed UNIXSocketStream.aclose() raising asyncio.InvalidStateError when a concurrent receive or send operation had just been cancelled on the asyncio backend (#1267; PR by @​alloutflo)

    • Fixed the pytest plugin importing the deprecated _pytest.python.CallSpec2 alias, which triggers PytestRemovedIn10Warning on pytest>=9.2 and crashes pytest at startup when filterwarnings = error is configured (#1271; PR by @​matthewfeickert)

    • Fixed an asyncio worker thread race that could raise RuntimeError when the event loop closed between checking its state and scheduling the worker result (#1265; PR by @​hansu650)

    • Fixed CapacityLimiter on the asyncio backend over-granting tokens when total_tokens was raised while the limiter was over-subscribed (#1223; PR by @​zelinewang)

    ... (truncated)

    Commits
    • ffcd154 Bumped up the version
    • 0ecf5ed Added a workaround for third party code accessing unimported submodules (#1309)
    • 9283662 Bumped up the version
    • d137692 Improved the instructions for AI agents
    • 033fc52 Shield TemporaryDirectory cleanup from cancellation (#1304)
    • 942e9a6 [pre-commit.ci] pre-commit autoupdate (#1305)
    • b825c3b Fixed pyproject.toml changes not triggering the test suite
    • 9727dc5 Fixed start inconsistencies between trio and asyncio (#1198)
    • b05fe6d Fixed wrong type in move_on_after (#1297)
    • 44d0c93 Fixed asyncio task group coroutine cleanup (#1275)
    • Additional commits viewable in compare view

    Updates `pytest-mock` from 3.15.1 to 3.16.0
    Release notes

    Sourced from pytest-mock's releases.

    v3.16.0

    2026-09-27

    • #604: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • #611: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • #606: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • #547: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • #147: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Changelog

    Sourced from pytest-mock's changelog.

    3.16.0

    2026-09-27

    • [#604](https://github.com/pytest-dev/pytest-mock/issues/604) <https://github.com/pytest-dev/pytest-mock/pull/604>_: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • [#611](https://github.com/pytest-dev/pytest-mock/issues/611) <https://github.com/pytest-dev/pytest-mock/pull/611>_: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • [#606](https://github.com/pytest-dev/pytest-mock/issues/606) <https://github.com/pytest-dev/pytest-mock/pull/606>_: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • [#547](https://github.com/pytest-dev/pytest-mock/issues/547) <https://github.com/pytest-dev/pytest-mock/issues/547>_: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • [#147](https://github.com/pytest-dev/pytest-mock/issues/147) <https://github.com/pytest-dev/pytest-mock/issues/147>_: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Commits

    Updates `ruff` from 0.16.5 to 0.16.9
    Release notes

    Sourced from ruff's releases.

    0.16.9

    Release Notes

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    Install ruff 0.16.9

    Install prebuilt binaries via shell script

    curl --proto '=https' --tlsv1.2 -LsSf
    https://releases.astral.sh/github/ruff/releases/download/0.16.9/ruff-installer.sh
    | sh
    

    ... (truncated)

    Changelog

    Sourced from ruff's changelog.

    0.16.9

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    0.16.8

    Released on 2026-09-16.

    Bug fixes

    • Visit functional TypedDict keyword arguments correctly (#28584)
    • [flake8-simplify] Detect nested async with under sync parent (SIM117) (#27821)
    • [flake8-simplify] Preserve operand order in SIM109 fix (#27824)

    ... (truncated)

    Commits
    • 0be08a2 Bump version to 0.16.9 (#28882)
    • b4920b7 Rename ruff_cli to ruff_command_line (#28881)
    • 47c751b Update dependency astral-sh/uv to v0.12.18 (#28880)
    • 8c244e5 [flake8-comprehensions] Document map/generator exception behavior (C417...
    • 5edf5a1 Use target form in rooster.version_files (#28876)
    • 915bb2b [ty] Prefer existing @ paths over response files in Ruff and ty (#28877)
    • 4710e1a ci(github): update version number in placeholder of issue template (#28871)
    • eedfc62 [ty] Propagate outer type context through cast calls (#28855)
    • ceaa6a0 [ty] Contain rendered code within Markdown fences (#28869)
    • dba0f30 authorize ruff-pre-commit dispatch via OIDC (#28867)
    • Additional commits viewable in compare view

    Updates `ty` from 0.0.75 to 0.0.84
    Release notes

    Sourced from ty's releases.

    0.0.84

    Release Notes

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Changelog

    Sourced from ty's changelog.

    0.0.84

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Commits

    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) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/checkpoint-postgres/uv.lock | 264 +++++++++++++++++-------------- 1 file changed, 148 insertions(+), 116 deletions(-) diff --git a/libs/checkpoint-postgres/uv.lock b/libs/checkpoint-postgres/uv.lock index 2eb051cf8..9942394d0 100644 --- a/libs/checkpoint-postgres/uv.lock +++ b/libs/checkpoint-postgres/uv.lock @@ -17,16 +17,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.14.2" +version = "4.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, + { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, ] [[package]] @@ -159,7 +159,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -254,7 +254,8 @@ dependencies = [ { name = "pydantic" }, { name = "pyyaml" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, { name = "uuid-utils" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6e/58/3ad53096eee1e07728e8219ded30ae308b0f2b8b7b26b8ebb6371917c0f5/langchain_core-1.5.2.tar.gz", hash = "sha256:2d13ab35b42eec63d4669a483776b8cdd778ee764107149369fb369d84c08c41", size = 972322, upload-time = "2026-07-28T16:38:37.977Z" } @@ -267,7 +268,8 @@ name = "langchain-protocol" version = "0.0.18" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/59/b5959aea96faa9146e2e49a7a22882b3528c62efafe9a6a95beab30c2305/langchain_protocol-0.0.18.tar.gz", hash = "sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6", size = 6150, upload-time = "2026-06-18T17:08:26.959Z" } wheels = [ @@ -597,15 +599,15 @@ wheels = [ [[package]] name = "psycopg" -version = "3.3.4" +version = "3.3.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/db/2f/cb91e5502ec9de1de6f1b76cfbf69531932725361168bb06963620c77e2e/psycopg-3.3.4.tar.gz", hash = "sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc", size = 165799, upload-time = "2026-05-01T23:31:55.179Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/26/3ea4ca5eaea1c0debcdf7ee7c1613fbe721dc27a03c461c0817ffd8a0601/psycopg-3.3.6.tar.gz", hash = "sha256:c081f2250df751a943036e42db6df4571c66cd0aabe8291a7a506512b12007d2", size = 168171, upload-time = "2026-09-18T13:22:55.152Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/e0/7b3dee031daae7743609ce3c746565d4a3ed7c2c186479eb48e34e838c64/psycopg-3.3.4-py3-none-any.whl", hash = "sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a", size = 213001, upload-time = "2026-05-01T23:20:50.816Z" }, + { url = "https://files.pythonhosted.org/packages/4e/de/748bd7609c71cae5d737f0ba9192f19329f70180ecda8fff3cac02c5abe3/psycopg-3.3.6-py3-none-any.whl", hash = "sha256:a1db9f7148b06a28606767efaca51fa6f9398c5c0a3810519be69d7000bdb631", size = 215490, upload-time = "2026-09-18T13:15:29.374Z" }, ] [package.optional-dependencies] @@ -615,76 +617,88 @@ binary = [ [[package]] name = "psycopg-binary" -version = "3.3.4" +version = "3.3.6" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/bf/70d8a60488f9955cbbcd538beae44d56bb2f1d19e673b72788f2d343ff55/psycopg_binary-3.3.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b7bfff1ca23732b488cbca3076fc11bc98d520ee122514fdb17a8e20d3338f5a", size = 4609750, upload-time = "2026-05-01T23:24:20.06Z" }, - { url = "https://files.pythonhosted.org/packages/db/b0/29e98ba210c9dbc75a6dc91e3f99b9e06ea901a62ca95804e02a1ae13e6b/psycopg_binary-3.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32a6fbf8481e3a370d0d72b860d35948a693cb01281da217f7b2f307636e591a", size = 4676700, upload-time = "2026-05-01T23:25:21.727Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ab/3df087b3c12bf74e47c08204172b2fabb5a144679110d5c7ad12d9201323/psycopg_binary-3.3.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bdef84570ebbce1d42b4e7ea952d21c414c5f118ad02fee00c5625f35e134429", size = 5496319, upload-time = "2026-05-01T23:25:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/87/9a/f088207b4cd6772f9e0d8a91807e79fa2458d4eb9eb1ae406c68415f2bec/psycopg_binary-3.3.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa1cbc10768a796c96d3243656016bf4e337c81c71097270bb7b0ad6210d9765", size = 5171906, upload-time = "2026-05-01T23:25:34.004Z" }, - { url = "https://files.pythonhosted.org/packages/48/45/4523a857f253871d75c22e1c2e79fd47e599e736bcba1bad58d83e24be02/psycopg_binary-3.3.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf7f73a4a792bc5db58a4b385d8a1467e8d468f7548702fb0ed1e9b7501b1c13", size = 6762621, upload-time = "2026-05-01T23:25:41.392Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d1/925bf776503345bef428e6c45fb017d0139ddbe0e211814b585c4253dca8/psycopg_binary-3.3.4-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7b4d40c153fa352ab3cca530f3a0baedf7621b2ebcbd7f084009522c21788fc", size = 5006319, upload-time = "2026-05-01T23:25:51.419Z" }, - { url = "https://files.pythonhosted.org/packages/6f/aa/99727337206fbba357ca084bf4ea8b29dc986f61842a2685859af61416db/psycopg_binary-3.3.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f9b1c2533af01cd7648378599f82b0b8ae32f293296e6eec5753a625bc97ef28", size = 4535388, upload-time = "2026-05-01T23:25:57.957Z" }, - { url = "https://files.pythonhosted.org/packages/0b/a4/567ba2c37d19d8c2f63d836385dfd2495aa5897bbee6cfab104d9ee58624/psycopg_binary-3.3.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ad3bc94054876155549fdaedf4a46d1ec69d39a5bcee377148afe498e84c4b8e", size = 4224544, upload-time = "2026-05-01T23:26:03.832Z" }, - { url = "https://files.pythonhosted.org/packages/b7/23/86457f5a82731685d7701de7bfaa5eb783dd1fecbf875321897d9d9ce33a/psycopg_binary-3.3.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb4eed2079c01a4850bf467deacfab56d356d4225040170af03dc9958321242d", size = 3956282, upload-time = "2026-05-01T23:26:09.983Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d8/249456df16d47de082abd9b73bce8ccdeb0293eb12e590f9150c7cbdb788/psycopg_binary-3.3.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f80e3f2b5331dbbf0901bcb658056c03eeb2c1ef31d774afb0d61598b242e744", size = 4261736, upload-time = "2026-05-01T23:26:16.798Z" }, - { url = "https://files.pythonhosted.org/packages/15/6b/c4abe228acafd8a385c1fb615d4f1e3c9b8ad7a4e4f0e84118ba3ffeed9c/psycopg_binary-3.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:574ea21a9651958f1535c5a1c649c7409e9168bcbffa29a3f2f961f58b322949", size = 3570620, upload-time = "2026-05-01T23:26:22.655Z" }, - { url = "https://files.pythonhosted.org/packages/b6/82/df3312c0ca083d5b43b352f27d4dd8b1e614bd334473074715d9e0000da4/psycopg_binary-3.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:612a627d733f695b1de1f9b4bd511c15f999a5d8b915d444bbd7dd71cf3370da", size = 4609813, upload-time = "2026-05-01T23:26:30.612Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b5/d74d542458d3e8ac0571d8a88f57ca369999b9a82f4fa528052d0d7d3e4c/psycopg_binary-3.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:13a7f380824c35896dcac7fe0f61440f7ca49d6dc73f3c13a9a4471e6a3b302e", size = 4676799, upload-time = "2026-05-01T23:26:38.475Z" }, - { url = "https://files.pythonhosted.org/packages/09/67/06bab9c60671999f4c6ceff1b334f3ac1f9fc5789eb467c714623ea21de9/psycopg_binary-3.3.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:276904e3452d6a23d474ef9a21eee19f20eed3d53ddd2576af033827e0ba0992", size = 5497050, upload-time = "2026-05-01T23:26:47.061Z" }, - { url = "https://files.pythonhosted.org/packages/72/9b/023433e2b20f970de1e22d29132a95281277646da0b2e2879dd4ee94b8c1/psycopg_binary-3.3.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab8cca8ef8fb1ccf5b048ae5bd78ba55b9e4b5d472e3ce5ca39ff4d2a9c249e4", size = 5172428, upload-time = "2026-05-01T23:26:56.708Z" }, - { url = "https://files.pythonhosted.org/packages/08/cd/ae16da8fde228a38b2fe9269bbc13cf89e0186173f2265600f02d6a71e64/psycopg_binary-3.3.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7465bfe6087d2d5b42d4c53b9b11ca9f218e477317a4a162a10e3c19e984ba8e", size = 6762746, upload-time = "2026-05-01T23:27:07.023Z" }, - { url = "https://files.pythonhosted.org/packages/4f/81/0ba09fa5f5f88779093a2541a8e02489825721f258ab88058b11d68b3eb5/psycopg_binary-3.3.4-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22cdbf5f91ef7bb91fe0c5757e1962d3127a8010256eefd9c61fcaf441802097", size = 5006033, upload-time = "2026-05-01T23:27:12.221Z" }, - { url = "https://files.pythonhosted.org/packages/73/6a/629136040cc3497adb442a305710b5913f2a754d4630fc3d3717c4c0df65/psycopg_binary-3.3.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e2631da29253a98bd496e6c4813b24e09a4fe3fb2a9e88513305d6f8747cce95", size = 4534175, upload-time = "2026-05-01T23:27:18.248Z" }, - { url = "https://files.pythonhosted.org/packages/7c/32/1027f843c6dc2d5d51960ee62cc0c2cf755a4c39455aff1371173edbef7d/psycopg_binary-3.3.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7f7668f30b9dd5163197e5cbf4e0efd54e00f0a859cc566ce56cfc31f4054839", size = 4224203, upload-time = "2026-05-01T23:27:24.3Z" }, - { url = "https://files.pythonhosted.org/packages/0b/e1/380a724d9093c74adb14d4fce920ea8327838abb61f760b1448586b14a8e/psycopg_binary-3.3.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:cffc3408d77a27973f33e5d909b624cce683db5fc25964b02fe0aae7886c1007", size = 3954509, upload-time = "2026-05-01T23:27:30.815Z" }, - { url = "https://files.pythonhosted.org/packages/db/cd/895893ae575a09c97ccfd5def070d88993d955ef34df45a881fd5ff506d6/psycopg_binary-3.3.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0579252a1202cd73e4da137a1426e2dae993ae44e757605344282af3a082848c", size = 4259551, upload-time = "2026-05-01T23:27:38.828Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c6/2330a20794e37a3ec609ef2fd8522919ec7a4395a1abf979a8e2d1775cd5/psycopg_binary-3.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:41f2ec0fea529832982bcb6c9415de3c86264ebe562b77a467c0fbcd7efbba8d", size = 3572054, upload-time = "2026-05-01T23:27:45.455Z" }, - { url = "https://files.pythonhosted.org/packages/95/7d/03818e13ba7f36de93573c93ee3482006d3dfa8b0f8d28df511bad0a1a92/psycopg_binary-3.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5ab28a2a7649df3b72e6b674b4c190e448e8e77cf496a65bd846472048de2089", size = 4591122, upload-time = "2026-05-01T23:27:56.162Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b9/11b341edf8d54e2694726b273fe9652b254d989f4f63e3ac6816ad6b55f4/psycopg_binary-3.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6402a9d8146cf4b3974ded3fd28a971e83dc6a0333eb7822524a3aa20b546578", size = 4669943, upload-time = "2026-05-01T23:28:04.522Z" }, - { url = "https://files.pythonhosted.org/packages/8b/18/4665bacd65e7865b4372fcd8abb8b9186ada4b0025f8c2ca691b364a556c/psycopg_binary-3.3.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:580ae30a5f95ccd90008ec697d3ed6a4a2047a516407ad904283fa42086936e9", size = 5469697, upload-time = "2026-05-01T23:28:11.337Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b1/b83136c6e510593d9b0c759ba5384337bc4ad82d19fda675adc4b2703c84/psycopg_binary-3.3.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7510c37550f91a187e3660a8cc50d4b760f8c3b8b2f89ebc5698cd2c7f2c85d", size = 5152995, upload-time = "2026-05-01T23:28:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/67/8d/a9821e2a648afe6091989929982a3b0f00b2631a859cb81379728f08fb75/psycopg_binary-3.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77df19583501ea288eaf15ac0fe7ad01e6d8091a91d5c41df5c718f307d8e31b", size = 6738180, upload-time = "2026-05-01T23:28:30.654Z" }, - { url = "https://files.pythonhosted.org/packages/7e/58/2e349e8d23905dc2317b80ac65f48fb6f821a4777a4e994a60da91c4850f/psycopg_binary-3.3.4-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:018fbed325936da502feb546642c982dcc4b9ffdea32dfef78dbf3b7f7ad4070", size = 4978828, upload-time = "2026-05-01T23:28:37.277Z" }, - { url = "https://files.pythonhosted.org/packages/45/48/57b00d03b4721878326122a1f1e6b0a90b85bcaec56b5b2f8ea6cfa45235/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17a21953a9e5ff3a16dab692625a3676e2f101db5e40072f39dbee2250194d68", size = 4509757, upload-time = "2026-05-01T23:28:43.078Z" }, - { url = "https://files.pythonhosted.org/packages/25/37/33b47d8c007df69aec500df5889767c4d313748e8e9e27a2fef8a6dabcee/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:eb05ee1c2b817d27c537333224c9e83c7afb86fe7296ba970990068baf819b16", size = 4190546, upload-time = "2026-05-01T23:28:50.016Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c6/32b0835dbc2122617902b649d76a91c1e75406e76bf3d595b0c3bb5ffad6/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:773d573e11f437ce0bdb95b7c18dc58390494f96d43f8b45b9760436114f7652", size = 3926197, upload-time = "2026-05-01T23:28:55.55Z" }, - { url = "https://files.pythonhosted.org/packages/cd/68/d190ef0c0c5b16ded07831dabc8ddd412f4cdab07ec6e30ed38d9bda0e1f/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e55ccbdfae79a2ed9c6369c3008a3025817ff9d7e27b32a2d84e2a4267e66e", size = 4236627, upload-time = "2026-05-01T23:29:05.336Z" }, - { url = "https://files.pythonhosted.org/packages/25/8f/81dcbc2e8454b74d14881275ea45f00791052dac531a9fa8be1730d1685b/psycopg_binary-3.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:494ca54901be8cf9eb7e02c25b731f2317c378efa44f43e8f9bd0e1184ae7be4", size = 3560782, upload-time = "2026-05-01T23:29:11.967Z" }, - { url = "https://files.pythonhosted.org/packages/09/43/13e9c406fbbf354580476e248a16b64802a376873ebe6339e30bb655572d/psycopg_binary-3.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbd1d4ed566895ad2d3bf4ddfd8bae90026930ddf29df3b9d91d32c8c47866a7", size = 4590377, upload-time = "2026-05-01T23:29:18.782Z" }, - { url = "https://files.pythonhosted.org/packages/22/be/2923cd7c3683e7afdecf4f10796a18de02f5c5ddc0969aa2ad0a8cdd3bbd/psycopg_binary-3.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:75a9067e236f9b9ae3535b66fe99bddb33d39c0de10112e49b9ab11eee53dc31", size = 4669023, upload-time = "2026-05-01T23:29:25.884Z" }, - { url = "https://files.pythonhosted.org/packages/96/a0/2c913d6fe13d6a8bd13597d36739bf47af063ad9399e402cfecab16f3c1e/psycopg_binary-3.3.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b56b603ebcea8aa10b46228b8410ba7f13e7c2ee54389d4d9be0927fd8ce2a70", size = 5467423, upload-time = "2026-05-01T23:29:33.416Z" }, - { url = "https://files.pythonhosted.org/packages/e7/38/205d10bc1ad0df4a21c5c51659126bd3ea0ef98fcad1e852f78c249bb9c3/psycopg_binary-3.3.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c677c4ad433cb7150c8cd304a0769ae3bcfbe5ea0676eb53faa7b1443b16d0d3", size = 5151137, upload-time = "2026-05-01T23:29:42.013Z" }, - { url = "https://files.pythonhosted.org/packages/36/fc/f0381ddcd45eff3bb70dbca6823a996048d7f507b2ec3fc92c6fabc0fe87/psycopg_binary-3.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26df2717e59c0473e4465a97dfb1b7afebaa479277870fd5784d1436470db47c", size = 6736671, upload-time = "2026-05-01T23:29:51.626Z" }, - { url = "https://files.pythonhosted.org/packages/95/40/fa545ae152c24327651e5624e4902121e808270be36c10b12e9939be09bc/psycopg_binary-3.3.4-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dc1f79fd16bb1f3f4421417a514607539f17804d95c7ed617265369d1981cae", size = 4979601, upload-time = "2026-05-01T23:29:56.961Z" }, - { url = "https://files.pythonhosted.org/packages/86/e4/2f8a47ee97f90cd2b933d0463081d35631ff419de2b8c984a5f369857de0/psycopg_binary-3.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:136f199a407b5348b9b857c504aff60c77622a28482e7195839ce1b51238c4cc", size = 4510513, upload-time = "2026-05-01T23:30:07.243Z" }, - { url = "https://files.pythonhosted.org/packages/0e/0e/94e842ff4a7f98ed162580ca2e8b8864b28c1e0350f2443f8ee47f821167/psycopg_binary-3.3.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b6f5a29e9c775b9f12a1a717aa7a2c80f9e1db6f27ba44a5b59c80ac61d2ffcf", size = 4187243, upload-time = "2026-05-01T23:30:15.352Z" }, - { url = "https://files.pythonhosted.org/packages/d0/83/fc6c174b672e29b7de996ea77b6cbddf46c891751c3355f6974292baa6b4/psycopg_binary-3.3.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ee17a2cf4943cde261adfad1bbc5bf38d6b3776d7afff74c7cabcbeaeb08c260", size = 3927347, upload-time = "2026-05-01T23:30:21.186Z" }, - { url = "https://files.pythonhosted.org/packages/e9/65/768364d4a97a15b1a7f47ba52688c1686f22941d8332a8398cefc468e25f/psycopg_binary-3.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c4ab71be17bdca30cb34c34c4e1496e2f5d6f20c199c12bad226070b22ef9bf", size = 4236393, upload-time = "2026-05-01T23:30:26.211Z" }, - { url = "https://files.pythonhosted.org/packages/bd/3b/218efbc9e645becd80cdf651acda05f85cfe546b7a9c0458c7cbc8fe1f74/psycopg_binary-3.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:dbfdb9b6cc79f31104a7b162a2b921b765fcc62af6c00540a167a8de47e4ed38", size = 3564592, upload-time = "2026-05-01T23:30:31.764Z" }, - { url = "https://files.pythonhosted.org/packages/48/a6/828c9185701dab71b234c2a76c38a08b098ebfec5020716b4e93807492b5/psycopg_binary-3.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:28b7398fdd19db3232c884fb24550bdfe951221f510e195e233299e4c9b78f97", size = 4607292, upload-time = "2026-05-01T23:30:38.962Z" }, - { url = "https://files.pythonhosted.org/packages/92/58/5b40dbc9d839045c9dae956960e4fb6d20bcabe6c59a2aa34fc3a371913f/psycopg_binary-3.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1fbaa292a3c8bb61b45df1ad3da1908ccee7cb889db9425e3557d9e34e2a4829", size = 4687023, upload-time = "2026-05-01T23:30:47.227Z" }, - { url = "https://files.pythonhosted.org/packages/85/a9/793f0ac107a9003b48441d0d1f9f616d96e0f37458dd8dc12528ceff55fb/psycopg_binary-3.3.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94596f9e7633ee3f6440711d43bb70aa31cc0a46a900ab8b4201a366ace5c9e7", size = 5486985, upload-time = "2026-05-01T23:30:55.517Z" }, - { url = "https://files.pythonhosted.org/packages/8f/26/42e8533497e2592334f68ec529cf5f840f7fa4e99575a4bb61aa184dbfbf/psycopg_binary-3.3.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8c0056529e68dbe9184cd4019a1f3d8f3a4ead2f6fc7a5afcf27d3314edd1277", size = 5168745, upload-time = "2026-05-01T23:31:01.904Z" }, - { url = "https://files.pythonhosted.org/packages/15/af/b7151776cc08d5935d45c833ec818a9beb417cf7c08239af1aafbdae78ee/psycopg_binary-3.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c09aad7051326e7603c14e50636db9c01f78272dc54b3accff03d46370461e6", size = 6761486, upload-time = "2026-05-01T23:31:14.511Z" }, - { url = "https://files.pythonhosted.org/packages/d0/ed/c92533b9124712d592cbf1cd6c76da933a2e0acea81dfe1fbe7e735f0cff/psycopg_binary-3.3.4-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:514404ed543efd620c85602b747df2a23cf1241b4067199e1a66f2d2757aaa41", size = 4997427, upload-time = "2026-05-01T23:31:20.901Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/ccadfd0de416aa188356daa199453af24087b042e296088706d190ae0295/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:46893c26858be12cc49ca4226ed6a60b4bfccadd946b3bebb783a60b38788228", size = 4533549, upload-time = "2026-05-01T23:31:26.204Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a0/c8f43cee36386f7bc891ab41a9d31ea07cf9826038e732da79f26b1e5f34/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:df1d567fc430f6df15c9fcf67d87685fc49bdb325adc0db5af1adfb2f44eb5c9", size = 4210256, upload-time = "2026-05-01T23:31:33.884Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2c/c1547871be3790676e8868b38655496422f94f0978dfb66b74bdba2f1676/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6b9016b1714da4dd5ecaaa75b82098aa5a0b87854ce9b092e21c27c4ae23e014", size = 3946204, upload-time = "2026-05-01T23:31:39.626Z" }, - { url = "https://files.pythonhosted.org/packages/c4/b1/f6670f00fa7ea601584623f6c11602ab92117d83eaff885e0210f6de7418/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:47c656a8a7ba6eb0cff1801a4caaa9c8bdc12d03080e273aff1c8ac39971a77e", size = 4255811, upload-time = "2026-05-01T23:31:44.986Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e6/5fff07a70d1f945ed90ae131c3bd76cab32beff7c58c6db15ad5820b6d1f/psycopg_binary-3.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8", size = 3666849, upload-time = "2026-05-01T23:31:51.165Z" }, + { url = "https://files.pythonhosted.org/packages/52/92/00350a66de0af05e41d01aa3134e3970045e816afed3f99d58ec1abe15b2/psycopg_binary-3.3.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7beb3e41c9a1e509f3ed85263386588cbe3e975aa67be21f79f44fd35ffaeefc", size = 4728682, upload-time = "2026-09-18T13:15:36.605Z" }, + { url = "https://files.pythonhosted.org/packages/91/fc/afa9c7fd316a469af7ede6ebb020eac482f5d827fae57d5310c9bc0c41ae/psycopg_binary-3.3.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa73160077345ec21b3f51e8e24b3de2e99586217e497629326eb9b2ea88c52e", size = 4777111, upload-time = "2026-09-18T13:15:46.566Z" }, + { url = "https://files.pythonhosted.org/packages/f2/44/7c1e015f1bc56b36ff1369f09e852b2d83ccefd5a669a42633a916cdedc4/psycopg_binary-3.3.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f87dbdc42e78ee0f7ea180c03f8c78e80a949e373066629bd90fefff10552dff", size = 5592755, upload-time = "2026-09-18T13:15:52.886Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ae/314a251ca918cdac380bce1b87839ade9355382ea749e6ef3ba75ba0c09f/psycopg_binary-3.3.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a9348c5b43a3bb5ef8c2e89d5237c9c87eeafb01d338c84a7aebbc5cd0313299", size = 5267949, upload-time = "2026-09-18T13:16:00.53Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9f/3bb0cfe9bb0f31ca57cf486ddc8c9ac51251aed8181bf88ff870b2623105/psycopg_binary-3.3.6-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a52991594ac4db888c7d39bccef331797e30cb31a95cae02cf2607f83a42dc2", size = 6862403, upload-time = "2026-09-18T13:16:10.385Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d6/7032c10309c3155e9b24300fdcc9a1afa539cfd20ce52fdef74a46f10161/psycopg_binary-3.3.6-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ea8beeb5541780b4b50b462eeacbc4f594ce3b911dc20c81c75f267876f71d2", size = 5107271, upload-time = "2026-09-18T13:16:16.843Z" }, + { url = "https://files.pythonhosted.org/packages/61/cc/79add2cf92684cf1a81da134b32caa662c25c72d0cc905d181ef4455f834/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:198a48e68cc99ccac03ba95ac857e73aa66f3bf6be77019fafb0832a05f7ad03", size = 4627984, upload-time = "2026-09-18T13:16:23.889Z" }, + { url = "https://files.pythonhosted.org/packages/c9/48/6dfb14f9350c14af6a2edb3c31262051b8cd94e2186e4b831e46dbbe8cd9/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fa34eb47969297471db7b7f193622c7e3ee839ec05abd05f1fe104d5b1b1dcf4", size = 4322054, upload-time = "2026-09-18T13:16:29.33Z" }, + { url = "https://files.pythonhosted.org/packages/29/35/2982338716a91cbb4dfc866be015be4457ee8106a445aabf3d1fb6a270e0/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b979a42815410432420275412633960807178b1ce26591a16ce06e78a5bd4bb2", size = 4048471, upload-time = "2026-09-18T13:16:34.119Z" }, + { url = "https://files.pythonhosted.org/packages/24/e1/171b1db1542c5f76a678b7ee0a7800bebc9735a0a03417c76cf948bfd63c/psycopg_binary-3.3.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:889e42acec10450185e0cdfb396f375e2c1a8d7737c114830a7fde4654f59e30", size = 4353931, upload-time = "2026-09-18T13:16:38.692Z" }, + { url = "https://files.pythonhosted.org/packages/08/89/4424e62a944eef40bd9326ada4ae23802b28eab6502af91e84ef7bba74fb/psycopg_binary-3.3.6-cp310-cp310-win_amd64.whl", hash = "sha256:cbd5f73073ed19c378d4c35499db1e3e703a5b1a324e521204065967bfaa7a18", size = 3674362, upload-time = "2026-09-18T13:16:44.454Z" }, + { url = "https://files.pythonhosted.org/packages/70/86/b71166048974d49c6d136b2ed1c0e5bec0b974d8c4de5cbce7e86a9e412a/psycopg_binary-3.3.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:be4f9b3c9338ac5dd217c5847e21521b396c8117f78dc420d495a5c49bbef874", size = 4728002, upload-time = "2026-09-18T13:16:53.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/1d/1e06c0de7ed5aed898acb87544eac6ef0bc7d752a67ec6e5d6b835e9b40c/psycopg_binary-3.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f0535693ce476a722b718b002d5d2c27d47e71ca945276ac194409c98e74c492", size = 4775403, upload-time = "2026-09-18T13:16:58.939Z" }, + { url = "https://files.pythonhosted.org/packages/84/02/2ffcbc43f8e4bbc38e5286a22013bcac01898d13cd38325f60dd5428a8af/psycopg_binary-3.3.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3c9e663b2e800e3218994cf948c11bcc2844e6491b34aa80d089baf6531827bf", size = 5594145, upload-time = "2026-09-18T13:17:08.515Z" }, + { url = "https://files.pythonhosted.org/packages/e1/25/031dae2c7d2e7e77dcf5b1962c1e0684fa548d7af0ff6707b6b5e6054ca7/psycopg_binary-3.3.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a2e44a342d2aee40508e28a563d8961c39d9bbd8cae36d8578f0a3c6658aab0f", size = 5267985, upload-time = "2026-09-18T13:17:16.24Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/94c89ada3c003a4d858178f3bba49a35e0297ef2aad659b80eb5e380e690/psycopg_binary-3.3.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f598f19fa9a91540b5cee17932ffd227b7b53a481605bcc4573c0eafa647300", size = 6860986, upload-time = "2026-09-18T13:17:23.348Z" }, + { url = "https://files.pythonhosted.org/packages/9d/a0/81bf499d095adee8413bd19822a6872fbfa21663ec78014a68d83a8db83c/psycopg_binary-3.3.6-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ff05561e4a067d35507dc5c90f1deb2ec1c9703ac5cccc1bc26e08a197f9c5a", size = 5106702, upload-time = "2026-09-18T13:17:28.847Z" }, + { url = "https://files.pythonhosted.org/packages/00/75/99d56da64c27bd985fd82c6ecbf7976b724ac638fdd1654ef995323a1a26/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:566dd827f17728efdf7d88a5b066f815170f6fdad13967ae952842d90e6aaa9f", size = 4627090, upload-time = "2026-09-18T13:17:36.668Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0c/0222171d11233332c6a24b1cef1578215f0ffddf3642eb8dd8c4448ad69f/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9b2f11794e017ce340934e35de46181c46ef71ec75ea3d85dd75cd836761c01e", size = 4320353, upload-time = "2026-09-18T13:17:42.526Z" }, + { url = "https://files.pythonhosted.org/packages/62/6f/e1cc2a28dd1228c67c969ba6fd37cd8726b312e2ff51380f847ddb38ccde/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:910ace140e3e7b7596898d083f37a8fe90c5c40684252ad4e682364b2cd3deba", size = 4047253, upload-time = "2026-09-18T13:17:47.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/fd/38b64790ce7a515b1dbd2bab3d119637a858aeb22c380cf4859bc4ce0e42/psycopg_binary-3.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37e517c146b185f9c0c6e8d0a0ebbdeeeb67896af28466e032bc810d0c7dc7a7", size = 4353208, upload-time = "2026-09-18T13:17:52.41Z" }, + { url = "https://files.pythonhosted.org/packages/f7/dc/45386530ceb2a8c789a226de9b9b34eca8fccf1feba2e4ef68a6aca50c56/psycopg_binary-3.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:c7f92daa0d2a1c76f07264abddf8cbabd30152a2f09c3270e50f0c7efdf5dcac", size = 3675638, upload-time = "2026-09-18T13:17:58.112Z" }, + { url = "https://files.pythonhosted.org/packages/e6/01/2cdd1824e58b4467ee0b9498664cd28c42d8794db6b1e35b6bcb834f0044/psycopg_binary-3.3.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3f84dab25e0385692ee13274c68678377e0b1a70ab9d14e56264cbf61f60c62d", size = 4707086, upload-time = "2026-09-18T13:18:05.138Z" }, + { url = "https://files.pythonhosted.org/packages/f6/76/de9948ac06895261c84d5b9fbe283d8f3c5bc9f070691b8d9eaa1b51e322/psycopg_binary-3.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:612382ac3ed13651c7fa44b5fee9fbf7baaa2ddbc6f500391672682c5f1df9e0", size = 4769607, upload-time = "2026-09-18T13:18:12.83Z" }, + { url = "https://files.pythonhosted.org/packages/76/a9/72436c9915ee4905964689e7f0e182ce7767cc0a0390b3ce703be8177625/psycopg_binary-3.3.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:366db6e97e66b37211475f20c4c1324a2dc0dd825e46d4e87f9d599304d276f9", size = 5554134, upload-time = "2026-09-18T13:18:21.175Z" }, + { url = "https://files.pythonhosted.org/packages/0a/42/948bb3d2617795093512613fd96ba380e922992c7908fbc073858147d196/psycopg_binary-3.3.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1679a1cb93fbe5a6d1fd58d82cbddcc6fcb8c61446ba7cae6eb2a7b19bc585de", size = 5235723, upload-time = "2026-09-18T13:18:27.071Z" }, + { url = "https://files.pythonhosted.org/packages/99/47/93e823ff1b0088400703410939c9bda3e63ed9c850b3ee088e8769f4c10b/psycopg_binary-3.3.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37d40450659401600e6d043ff586c89a71a69f33cbb8bcdba6cdb2569beecdbe", size = 6833587, upload-time = "2026-09-18T13:18:33.794Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2d/ecc69c847795aa704041a9f5667a6b0938a088cf1853636d762a6938e493/psycopg_binary-3.3.6-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a5165300324efd5a772c48a88ab3a928513ab3979fca76553e62ee815f7b2b9c", size = 5070013, upload-time = "2026-09-18T13:18:39.628Z" }, + { url = "https://files.pythonhosted.org/packages/92/36/6126f0dac21713dcae91404f2a76da18598a6252339a8c669c46370d43b2/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d636338c8f21b0df2f84657b00bc34f9313f826ef93f1155bc743607e4a0c5eb", size = 4597367, upload-time = "2026-09-18T13:18:45.023Z" }, + { url = "https://files.pythonhosted.org/packages/4d/29/7ecfc04243b46c89ffd49924e9c5634ea904ef96c7d0f37e4073623584c1/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a4ee3bdd5468a725f2a4d9aab8a74b6d0279f768c8b5d3aeb102c5307ff3d59c", size = 4275419, upload-time = "2026-09-18T13:18:49.299Z" }, + { url = "https://files.pythonhosted.org/packages/6e/90/2f46d2e0de79706ac170df0a3637fe63c4498fc04f131f6049520b78b806/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:289aadd6a00e151203c081f708348ec89f1e483c9b510ef4ac3981f847f01f79", size = 4007358, upload-time = "2026-09-18T13:18:53.944Z" }, + { url = "https://files.pythonhosted.org/packages/03/48/6744e91291b751a8cf12d63d719977974bb94c84ceba913e7ddb2e478e51/psycopg_binary-3.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f21d057f3e5f5491067e5b292498073b73847d48799b099803fef100775fcc52", size = 4320156, upload-time = "2026-09-18T13:18:59.258Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9b/94ff7fce53a64d5b286e2ec454e0a025cf3d6e6b4a9189bef16aa5de98b2/psycopg_binary-3.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:e23a66a763fbe83fcc210bc77c27e5a5ea380ebf091c06f34d8561b695e5a40f", size = 3658864, upload-time = "2026-09-18T13:19:06.503Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c3/c072584b69ad44a747b448cfc9766fecb8aae56e372a017e2ef668790057/psycopg_binary-3.3.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ad8f35e67cc16d1fad1fa8c88972dc9b3a3141ea67897399904edab96a301b6", size = 4712284, upload-time = "2026-09-18T13:19:13.451Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b9/4283b785339e8e2318d03048994b093d650ea6289fabaa806b765dc0d449/psycopg_binary-3.3.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:373704aea331d3f3e3402c125a1543f5875e2986ebb54f97d1647942161f803f", size = 4772031, upload-time = "2026-09-18T13:19:18.524Z" }, + { url = "https://files.pythonhosted.org/packages/6f/72/7a1321d359246769fff1affffbd0132785a28f7f63c18524c15a502398f4/psycopg_binary-3.3.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b82491019b884d62318b5f30706c3d7e6d4e5a6cb7eabcb3edc0c1b0fdaceae9", size = 5556392, upload-time = "2026-09-18T13:19:24.418Z" }, + { url = "https://files.pythonhosted.org/packages/de/b0/c6f8a0585a5dacbea74e130bcfc66629390e8f5bbc79d2a8e806e8952150/psycopg_binary-3.3.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cec5ea900390897d0b46130f60bc2883bf19c314f9044235217c8be88b0ef269", size = 5237855, upload-time = "2026-09-18T13:19:31.257Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fc/c3a7a8bbef7e945ec584ac61d460a612363ea398511cd0e220242b1d69f1/psycopg_binary-3.3.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98c02090d88f2ebc0ec1e8da538f77d225ce0fffecf372aa39262e62a1b054ef", size = 6833856, upload-time = "2026-09-18T13:19:43.622Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f2/8e80b921db728ebb68fc105bd7c4277f908210ad755bd6481d5ea7add740/psycopg_binary-3.3.6-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ee2c4728c691245e24501fcd7a97b5b381236b9985bc445bba88cdce7d1b5784", size = 5070730, upload-time = "2026-09-18T13:19:49.968Z" }, + { url = "https://files.pythonhosted.org/packages/54/6a/5b313e0c5348244f0e973aff3258bf86766656256d5ece8d541a53e35b4a/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f19cc87343eaa55255e76b31259a570072ac95d6ae82c92dd34b97691f5e49dc", size = 4598089, upload-time = "2026-09-18T13:19:56.426Z" }, + { url = "https://files.pythonhosted.org/packages/32/e9/db7f76ec24bf6699e92bf604e5c4bae10664a681a8999ef42aa0faf0f2c6/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdccb3a0e184b03e9baa673b15a809cf36c339c85dbda0ebc25a698846dfbee8", size = 4278481, upload-time = "2026-09-18T13:20:04.681Z" }, + { url = "https://files.pythonhosted.org/packages/61/83/72c67013656f4d6b547caabffb193e91d57e63f90eefdcc6d045c400e97d/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9892188bb15e5803beb51afe8a25add6b56be391a53058e8bca03b74e1e6bf22", size = 4009229, upload-time = "2026-09-18T13:20:11.905Z" }, + { url = "https://files.pythonhosted.org/packages/82/35/5e4500df2c999eb0faed8b184e6958b834172128274f06167a5deef4c19c/psycopg_binary-3.3.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3af90f92769d8cc10f94515ee7a0aef36ea85ca733a0ce22858f6e0953f41138", size = 4321467, upload-time = "2026-09-18T13:20:17.949Z" }, + { url = "https://files.pythonhosted.org/packages/55/7f/e350e1cf498ba2565c3f87b12f429d2012eb86b76c2b3845a19ee5fbb4d6/psycopg_binary-3.3.6-cp313-cp313-win_amd64.whl", hash = "sha256:0ebfad5d131de9f892ae9e70cc7616207768b6714b66a52d4612b8ceaf78b372", size = 3658179, upload-time = "2026-09-18T13:20:22.691Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b9/60711317c284a442511644ea7185b56ebe627606d6741e732cd16108c47b/psycopg_binary-3.3.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:b3f75dee0f9afafabe4edc52c4842f1e1878ed2069bd05b22d6fe961e97e4dba", size = 4720512, upload-time = "2026-09-18T13:20:29.278Z" }, + { url = "https://files.pythonhosted.org/packages/63/da/28befc84454cbc6374550de7746f591f8fe1b6165c1fce249652cc8291c4/psycopg_binary-3.3.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5927b7ba63153cd8e9862987290a2b783a5c590daf2a4ef981700cc3569166d4", size = 4782318, upload-time = "2026-09-18T13:20:35.401Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8a/0d21c2c833cdc0d4244c77e858e0ed37fa2abec2623be4fd686f617109ce/psycopg_binary-3.3.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0bf08b749cc144f33b44a91b78e3f71c60eb07963746a0df5a100b36ce3d7475", size = 5567460, upload-time = "2026-09-18T13:20:41.902Z" }, + { url = "https://files.pythonhosted.org/packages/49/6d/7692d0d4e656b6cc9868d8acc2e3b42f17a0db4a625400a6d093cb0533a1/psycopg_binary-3.3.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:31cd942c23f613276b81a6e6598cefa12960058b0f46e1e874b540c793f6aca5", size = 5246902, upload-time = "2026-09-18T13:20:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c1/b8a1f18fb1b7558a17f57f7cb3fc8bc93189feea2958925950b3acb15743/psycopg_binary-3.3.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4690cf67738f0e0e49a32aeec99bf0e4595cc2b4f1af984a4345394b1dcff91a", size = 6847192, upload-time = "2026-09-18T13:20:56.874Z" }, + { url = "https://files.pythonhosted.org/packages/a5/76/404f33519167c65cca88ec4998776f1dbebccc301ee977f0e62c47fb0826/psycopg_binary-3.3.6-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ad1c785e784cfd87e8436c6b7702f2d321fc39601bbaf29bc63a41a867091638", size = 5079573, upload-time = "2026-09-18T13:21:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d9/79e8fbc8f37262a415f3550f0bcc5f98037442bf3d12ef6cbae2056655ae/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:79a2a1c3449f6c3409427078ed1cec10de79f3023cb5f2504f0597d350ad46c7", size = 4613633, upload-time = "2026-09-18T13:21:10.664Z" }, + { url = "https://files.pythonhosted.org/packages/d4/47/96225db74be7d2ce04b3a58678b53cda610225055edf5faa775c9f501d8b/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:86147cb5d140341c3363fb5bacce31f8d5543902a46699d3c536b101bbceaf9e", size = 4293375, upload-time = "2026-09-18T13:21:16.027Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d2/18e9c779a5efd565250329adaf529ecc2b8b2ed5be5cb0f6ccee208cbfd9/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7308c93cf0b19bbaf8e6ff0a6ad50d3c442385739245fe15a8d593bf841734a6", size = 4019883, upload-time = "2026-09-18T13:21:21.587Z" }, + { url = "https://files.pythonhosted.org/packages/ef/28/0cc654afc6c2cda982767f5679d3646b30b1ec86545bdaa9402202d6776c/psycopg_binary-3.3.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:05a83ac9fd52b9bca7cb5ab04b3691163170bd16f53defa27216ea3aa07ee781", size = 4332607, upload-time = "2026-09-18T13:21:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3e/0a753a74fbd7aef120f286c016e09d3cc3f1daf7688f4a145d27281260b2/psycopg_binary-3.3.6-cp314-cp314-win_amd64.whl", hash = "sha256:1fbd30e537dab22cafdf080608f10148fe2a5f3a61294ddb5113caac8a623840", size = 3755671, upload-time = "2026-09-18T13:21:33.855Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b1/a372b9c02aea50148e71c9853e19efca8fa5ae2010a8e27243b9b8f790c0/psycopg_binary-3.3.6-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:bf8c8481d026b85dd70c5fa7dde85b2333aed0b32a2602bcd38a900cbd78a49c", size = 4719571, upload-time = "2026-09-18T13:21:41.437Z" }, + { url = "https://files.pythonhosted.org/packages/65/7c/811e3828c6b82e2f10c6c9cdd963cfc66f3e024026e5a69ac18530bad984/psycopg_binary-3.3.6-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:b599defe9190b17e9907c8b4d114c181e702c87efcd1b8a0ad40971cdcc4634a", size = 4781230, upload-time = "2026-09-18T13:21:49.516Z" }, + { url = "https://files.pythonhosted.org/packages/3e/15/9a784eed813ea9e97c294af3ead63d02b7b203502c66380336c50065e441/psycopg_binary-3.3.6-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b8ece331509f7a975b90501f41e83ad905e4141753fedf3f2711b2bc70a8efbc", size = 5566111, upload-time = "2026-09-18T13:21:58.089Z" }, + { url = "https://files.pythonhosted.org/packages/68/16/47194e002007c27337b11e49bf459c4b19727463f9aff2e1a90917bcc806/psycopg_binary-3.3.6-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c61617eaae0112ca154da87ffb99b73af2c74067acac28dfb9a4455b019dff2e", size = 5249963, upload-time = "2026-09-18T13:22:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/53/84/5dcf9f310b11f0675cd860c6b2c70f58ce61798a3ee3f6f962b53fa358ca/psycopg_binary-3.3.6-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6d19cb4999d03231e8730a5f66c8f5068bc3b532677eb39dab0f600bff3e312", size = 6847925, upload-time = "2026-09-18T13:22:13.088Z" }, + { url = "https://files.pythonhosted.org/packages/f3/06/1957a06dc22963c418c27b284929579de84f29c37ad1abe6dc6ee9e8cf25/psycopg_binary-3.3.6-cp315-cp315-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8cbb54454dbf1bbf2ff08dd7693e8d94ac94b1a20f70f4b3b813d52ecb5cbc1", size = 5087720, upload-time = "2026-09-18T13:22:17.959Z" }, + { url = "https://files.pythonhosted.org/packages/21/43/ac07d042bae99b57bf123bb473632f29af544008094da0ffd285ab8011e2/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:dc75da5a20951049f7b773145f998f69d181adad9c58a0ff36e0cf1d73c10e10", size = 4613412, upload-time = "2026-09-18T13:22:26.719Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/019156fbeafcefb4cccc9d109de4699493bceb8313c7545c8349e089dfbc/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:955e3dd94da361e052d2e49acf591017158dc8f8ed2c8a42c2e3943403c39dc2", size = 4292618, upload-time = "2026-09-18T13:22:33.042Z" }, + { url = "https://files.pythonhosted.org/packages/5d/0f/62113dc6b1df65983a1f2fc816c04b1edfa22f2ae9d4abee74ed267f4a96/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:c7753871eb57e6a5f4646f6168590c6653073dea5e9e720b201c8875332df4c8", size = 4027121, upload-time = "2026-09-18T13:22:38.334Z" }, + { url = "https://files.pythonhosted.org/packages/5d/d5/cf0cbd1ea5a7d8167fe2c6953efde19101f7b193bd61a23e6d622ad6854c/psycopg_binary-3.3.6-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:303732e798fe6729f8e12021b9c96107df8e95ecec4dd487c67b98ec2a59435e", size = 4336388, upload-time = "2026-09-18T13:22:45.576Z" }, + { url = "https://files.pythonhosted.org/packages/98/33/e2a5b36edf8aa422f6fa4b894756eb33dc93b36df5f65121280bb8b929c4/psycopg_binary-3.3.6-cp315-cp315-win_amd64.whl", hash = "sha256:2f122603f36050937982abf9668d8bc4769a79f7c93a65013b1c49f1cab7b56b", size = 3756154, upload-time = "2026-09-18T13:22:51.283Z" }, ] [[package]] name = "psycopg-pool" -version = "3.3.1" +version = "3.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/82/7a23d26039827ecd4ebe93905651029ddd307c5182ad59296dfb6f67b528/psycopg_pool-3.3.1.tar.gz", hash = "sha256:b10b10b7a175d5cc1592147dc5b7eec8a9e0834eb3ed2c4a92c858e2f51eb63c", size = 31661, upload-time = "2026-05-01T23:31:59.809Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/5e/c0664b968b102ff68b811d999c728546c48d5c1eec03e3bbaf88c0cb4472/psycopg_pool-3.3.3.tar.gz", hash = "sha256:df87b5d9d0ad7db37f6cdad4fa8ce113d250f5997f6db38e9a99192fb67f9e1d", size = 32006, upload-time = "2026-09-22T15:53:24.947Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/ed/89c2c620af0e1660354cd8aabf9f5b21f911597ce22acb37c805d6c86bc8/psycopg_pool-3.3.1-py3-none-any.whl", hash = "sha256:2af5b432941c4c9ad5c87b3fa410aec910ec8f7c122855897983a06c45f2e4b5", size = 40023, upload-time = "2026-05-01T23:31:53.136Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b4/452c6607a0f479465cd8a9b0d9956919fcb150050c1f83f9f11e6b8ee8dc/psycopg_pool-3.3.3-py3-none-any.whl", hash = "sha256:9b9cd6a4fcec47a410f7e82d408540e7f77b478509e91b44c1a5457a13e5ff37", size = 40304, upload-time = "2026-09-22T15:53:23.712Z" }, ] [[package]] @@ -694,7 +708,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } @@ -707,7 +722,8 @@ name = "pydantic-core" version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } wheels = [ @@ -854,7 +870,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } wheels = [ @@ -863,14 +879,14 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.15.1" +version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/7f/6ed29931d5c8cd396e7c0a55412e6cc88020373365c8685985dea53d26d7/pytest_mock-3.16.0.tar.gz", hash = "sha256:5a8395528b8f498205f3718f575228d0edaed7425fff638f87d1a6c3e0383636", size = 35362, upload-time = "2026-09-27T14:57:55.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/5b/b83a9bf1a3b4ec222f9fa083147ff6816245223da0ab92370e7e056f113f/pytest_mock-3.16.0-py3-none-any.whl", hash = "sha256:007cfeb257801d88d9c0b2a7b5a15a15e73b71968dfd72e7bf8c4a2f8393aec8", size = 10016, upload-time = "2026-09-27T14:57:54.283Z" }, ] [[package]] @@ -979,27 +995,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.5" +version = "0.16.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, - { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, - { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, - { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, - { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, - { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, - { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, - { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" }, + { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" }, + { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" }, + { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" }, + { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" }, ] [[package]] @@ -1067,44 +1083,60 @@ wheels = [ [[package]] name = "ty" -version = "0.0.75" +version = "0.0.84" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, - { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, - { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, - { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, - { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, - { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, - { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" }, ] [[package]] name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", +] sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + [[package]] name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ From f6e9ace6395b489778f80c65130d0d02bad4fd1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:52:22 -0700 Subject: [PATCH 06/17] chore(deps): bump actions/deploy-pages from 5.0.0 to 5.0.1 in the minor-and-patch group (#9144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group with 1 update: [actions/deploy-pages](https://github.com/actions/deploy-pages). Updates `actions/deploy-pages` from 5.0.0 to 5.0.1
    Release notes

    Sourced from actions/deploy-pages's releases.

    v5.0.1

    Changelog


    See details of all code changes since previous release.

    :warning: For use with products other than GitHub.com, such as GitHub Enterprise Server, please consult the compatibility table.

    Commits
    • 368f825 Merge pull request #444 from actions/yoannchaudet-deployment-polling-backoff
    • 7e97763 Validate deployment polling intervals
    • 0143e11 Add backoff and jitter to deployment polling
    • 5e98f10 Merge pull request #440 from actions/user/adwitiya
    • 8b0625a Improve deployment request test coverage
    • See full diff in compare view

    [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/deploy-pages&package-manager=github_actions&previous-version=5.0.0&new-version=5.0.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) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy-redirects.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-redirects.yml b/.github/workflows/deploy-redirects.yml index bed8aad74..b1d9b4575 100644 --- a/.github/workflows/deploy-redirects.yml +++ b/.github/workflows/deploy-redirects.yml @@ -50,4 +50,4 @@ jobs: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 From 9829ef9e64b11efa6fa6ac51bb84dc3c68dd40c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 08:59:43 +0000 Subject: [PATCH 07/17] chore(deps): bump virtualenv from 21.2.4 to 21.7.12 in /libs/cli (#9159) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.2.4 to 21.7.12.
    Release notes

    Sourced from virtualenv's releases.

    21.7.12

    What's Changed

    Full Changelog: https://github.com/pypa/virtualenv/compare/21.7.11...21.7.12

    21.7.11

    What's Changed

    Full Changelog: https://github.com/pypa/virtualenv/compare/21.7.10...21.7.11

    21.7.10

    What's Changed

    New Contributors

    Full Changelog: https://github.com/pypa/virtualenv/compare/21.7.9...21.7.10

    21.7.9

    What's Changed

    ... (truncated)

    Changelog

    Sourced from virtualenv's changelog.

    Bugfixes - 21.7.12

    • Fix activate.bat running arbitrary commands from a crafted --prompt, VIRTUALENV_PROMPT, or config file value. (:issue:3250)
    • Verify a downloaded seed wheel's sha256 against PyPI before seeding it into a virtual environment, skipped when a custom pip index is configured. (:issue:3251)

    v21.7.11 (2026-09-17)


    Bugfixes - 21.7.11

    • Running activate.bat again before deactivate no longer makes deactivate leave the environment's PKG_CONFIG_PATH, TCL_LIBRARY and TK_LIBRARY behind, or lose values the user had set before the first activation - by :user:darrenhuai. (:issue:3245)
    • Write pyvenv.cfg values on a single line, so a prompt carrying a line boundary can no longer inject configuration. --prompt, the VIRTUALENV_PROMPT environment variable and the config file all set the prompt, and pyvenv.cfg has no escape syntax, so a newline, a carriage return, or any other boundary str.splitlines recognizes, such as U+2028, started a new configuration line. Reading the file back picked up those lines as keys, and since the last value for a key wins, they replaced anything written earlier, including home. (:issue:3247)

    Improved Documentation - 21.7.11

    • Document the policy for AI-assisted contributions and the licensing rules for dependencies. (:issue:3239)

    Misc - 21.7.11

    • :issue:3238, :issue:3240, :issue:3241, :issue:3242, :issue:3243, :issue:3244, :issue:3246

    v21.7.10 (2026-09-15)


    Bugfixes - 21.7.10

    • Skip blank lines, # comments and lines without = in pyvenv.cfg instead of raising ValueError - by :user:r3wretrhy. (:issue:3232)
    • deactivate in bash, fish and PowerShell unsets PKG_CONFIG_PATH when activation found it unset, instead of keeping the environment's lib/pkgconfig. csh activation no longer drops a PKG_CONFIG_PATH the user had set. Activation in batch, fish, nushell and PowerShell no longer adds a trailing separator when PKG_CONFIG_PATH is unset, and PowerShell and nushell build the value with the host's path separators - by :user:darrenhuai. (:issue:3233)
    • Activation in bash, csh, fish and PowerShell keeps the user's TCL_LIBRARY and TK_LIBRARY, and deactivate restores them. csh and PowerShell removed both variables on every activation, fish did so when the interpreter has

    ... (truncated)

    Commits
    • 9666b42 release 21.7.12
    • a01ed3e 🐛 fix(seed): verify downloaded wheel digests (#3251)
    • d721ff1 🐛 fix(activation): escape batch quote() against injection (#3250)
    • 087a2ef 🔧 chore(changelog): drop dead CVE-2026-24049 fragment (#3249)
    • 73e352a release 21.7.11
    • 68ee5a3 📝 docs(readme): add OpenSSF Best Practices badge (#3248)
    • 787d1c9 🔧 chore(test): add opt-in Atheris fuzz harness (#3246)
    • a30f995 🐛 fix(create): keep pyvenv.cfg values on a single line (#3247)
    • 469dd28 🐛 fix(activation): undo a live activation before activate.bat saves values (#...
    • a045a14 👷 ci: run macOS jobs on macos-26 (#3244)
    • Additional commits viewable in compare view

    [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=virtualenv&package-manager=uv&previous-version=21.2.4&new-version=21.7.12)](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) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show 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).
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/cli/uv.lock | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libs/cli/uv.lock b/libs/cli/uv.lock index 358690d42..101c6d6d7 100644 --- a/libs/cli/uv.lock +++ b/libs/cli/uv.lock @@ -1863,15 +1863,14 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.2.2" +version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, - { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/57/250bd238b966cece44328235eb85290045d059265fdaf7527a3a958123db/python_discovery-1.6.1.tar.gz", hash = "sha256:cf87d3627dfb4412437fdd5b13eae402607722998d21567993aedbc59b23c15e", size = 84338, upload-time = "2026-09-18T01:31:53.971Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, + { url = "https://files.pythonhosted.org/packages/16/7d/e9ffbadfbf89c93848412d04594135c4ae8c1d37d9e053b9c3ed718fabc4/python_discovery-1.6.1-py3-none-any.whl", hash = "sha256:d43fcdef879fe795352bd13ccf8d185ba5a9f86f36cfcd00529f596e737442b3", size = 38664, upload-time = "2026-09-18T01:31:52.448Z" }, ] [[package]] @@ -2376,7 +2375,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.2.4" +version = "21.7.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -2385,9 +2384,9 @@ dependencies = [ { name = "python-discovery" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/98/3a7e644e19cb26133488caff231be390579860bbbb3da35913c49a1d0a46/virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada", size = 5850742, upload-time = "2026-04-14T22:15:31.438Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/db/2aae91b0237f5f6b1d1229bf4953f3b8bdd2c215312ad2d2b3f57fd831df/virtualenv-21.7.12.tar.gz", hash = "sha256:5b6576455f10637067cc20c3f9f7c677575eecaa8c95fc049db2f650652befd5", size = 5359483, upload-time = "2026-09-18T01:40:43.877Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8d/edd0bd910ff803c308ee9a6b7778621af0d10252219ad9f19ef4d4982a61/virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac", size = 5831232, upload-time = "2026-04-14T22:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/4d/ef/39ecac9ca33d0b7994e63accc86d7224a4341d2e1b42b6da1aa5c24e6a5c/virtualenv-21.7.12-py3-none-any.whl", hash = "sha256:3572a2e13deac1c2727e48ec43dc962f698c453c5c90eb2bd737c81bcd2ac0b0", size = 5328681, upload-time = "2026-09-18T01:40:41.848Z" }, ] [[package]] From b36b1d58a8b408455b512cfad3b1b26e02927282 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 09:00:10 +0000 Subject: [PATCH 08/17] chore(deps): bump tornado from 6.5.8 to 6.5.9 in /libs/langgraph (#9160) Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.5.8 to 6.5.9.
    Changelog

    Sourced from tornado's changelog.

    Release notes

    .. toctree:: :maxdepth: 2

    releases/v6.6.0 releases/v6.5.10 releases/v6.5.9 releases/v6.5.8 releases/v6.5.7 releases/v6.5.6 releases/v6.5.5 releases/v6.5.4 releases/v6.5.3 releases/v6.5.2 releases/v6.5.1 releases/v6.5.0 releases/v6.4.2 releases/v6.4.1 releases/v6.4.0 releases/v6.3.3 releases/v6.3.2 releases/v6.3.1 releases/v6.3.0 releases/v6.2.0 releases/v6.1.0 releases/v6.0.4 releases/v6.0.3 releases/v6.0.2 releases/v6.0.1 releases/v6.0.0 releases/v5.1.1 releases/v5.1.0 releases/v5.0.2 releases/v5.0.1 releases/v5.0.0 releases/v4.5.3 releases/v4.5.2 releases/v4.5.1 releases/v4.5.0 releases/v4.4.3 releases/v4.4.2 releases/v4.4.1 releases/v4.4.0 releases/v4.3.0 releases/v4.2.1 releases/v4.2.0 releases/v4.1.0 releases/v4.0.2

    ... (truncated)

    Commits
    • 75ef8b1 Merge pull request #3719 from bdarnell/fixes-659
    • 3590cb4 test: Hardcode SimpleAsyncHTTPClient in HTTP1xxLimitTestCase
    • 9fc5d6d test: Make tracemalloc optional in httpclient_test
    • 555a2ee iostream: Treat connection resets as a clean close in read_until_close
    • 3ba622f Release notes and version bump for 6.5.9
    • 41eea68 test: Fix some test issues only found by our custom tox config
    • ab1a778 Merge remote-tracking branch 'bdarnell/claude/asynchttpclient-streaming-memor...
    • 437ab5f web: Do not follow symlinks out of the static directory
    • 0394513 httputil: Apply the argument count limit to query strings
    • b798f83 http1connection: Return after reading the response that follows a 1xx
    • Additional commits viewable in compare view

    [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tornado&package-manager=uv&previous-version=6.5.8&new-version=6.5.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show 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).
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/langgraph/uv.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index ce616e7ca..9339a3145 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -3576,19 +3576,19 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.8" +version = "6.5.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/d3/343e5bb989d6515b1646cf3d40135d73f3d5e45339bded401b56cdac24dd/tornado-6.5.8.tar.gz", hash = "sha256:9452e1b208a8bd771e2cb1f2ff564985b9b214bdebbe622793e1799e0a6bd23f", size = 520493, upload-time = "2026-08-07T02:12:42.971Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/15/ca7aaebb77f850493cba71ace508aeffb896d1ad8976417b48b79823dbd2/tornado-6.5.9.tar.gz", hash = "sha256:4d868544ebdf2fc155239a74397f65ebfea9b4d3635296c96b5dfb0701c354f5", size = 536145, upload-time = "2026-09-14T18:08:37.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/d5/007086fd8df5489338e204f65adce33fd4f21a4999dbb2b9cff2f897b5f4/tornado-6.5.8-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:cc6aa787d7cfab7c3d35189dc7a56fbd2399a569624c730c6b55b3d6531d0403", size = 449487, upload-time = "2026-08-07T02:12:28.682Z" }, - { url = "https://files.pythonhosted.org/packages/70/c8/5a24a99495903f594f6a199dd7beead1cbc0a13e2cb9102727bcaaf2a997/tornado-6.5.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9715b5eb79735b2bcd454ce216a9275b7c0470e64ea1bf5742f78b2f72b26eeb", size = 447649, upload-time = "2026-08-07T02:12:30.306Z" }, - { url = "https://files.pythonhosted.org/packages/6e/de/f2e733f386b85962d1b1dc82cd63d169b5b4580062b35397eac9244a41fe/tornado-6.5.8-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:547d63f450d570c14fe0e8db2cfb14c9bbd1c2503b4a6612586267955aa47b58", size = 450707, upload-time = "2026-08-07T02:12:31.95Z" }, - { url = "https://files.pythonhosted.org/packages/0b/94/20efeee9a01c141e9ac47c397f81679dfda24b32768fc4fff24e76d36c2c/tornado-6.5.8-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e2360a0ffbe145eca8af0b19cb7203d79b1a98dd4cccdd6b368f6f49c2e3808", size = 451677, upload-time = "2026-08-07T02:12:33.512Z" }, - { url = "https://files.pythonhosted.org/packages/42/ec/a96ccb8ccf0de2b7bc2c5fa1608a4803735018242e90c4882365a9fd418f/tornado-6.5.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5d242290bdf7ab3151bc1065fdd75c0dcc21cbc7b49f22a4c56329c2d6566d22", size = 451510, upload-time = "2026-08-07T02:12:35.346Z" }, - { url = "https://files.pythonhosted.org/packages/29/b5/93185859245ad3f00e62175f29607346788b696369347f0146e0421286bb/tornado-6.5.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7b94ff0e128fe0542f3bd331fb44d06260fc4ac16881545159f34ef08aad4195", size = 450917, upload-time = "2026-08-07T02:12:36.963Z" }, - { url = "https://files.pythonhosted.org/packages/97/cf/fe33cf062834487d34d1559746a4a12521033c22645b6d74d4bca702e018/tornado-6.5.8-cp39-abi3-win32.whl", hash = "sha256:67832909c4779c64942380cb5f044a5c6163d00831472d80e25e115de9917836", size = 451952, upload-time = "2026-08-07T02:12:38.512Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e1/468ad54333e92ccb62627e62cb88e5fc14a2171daa67ed47b1b8542d5b86/tornado-6.5.8-cp39-abi3-win_amd64.whl", hash = "sha256:11881db6b7c168494be2c2d12e65931451bdf7ee718535418ae1d8855dd5a0ee", size = 452391, upload-time = "2026-08-07T02:12:39.971Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3e/cd5e4f06e34cde33b8ef66cf36aa2b5ad46354cc1af7d2136bbe365fee1d/tornado-6.5.8-cp39-abi3-win_arm64.whl", hash = "sha256:68a7468c7e289f8514d7d664101753903217eff1bb6822c6b5994a0b5f5bcb26", size = 451411, upload-time = "2026-08-07T02:12:41.469Z" }, + { url = "https://files.pythonhosted.org/packages/a0/4a/4d3459fd2f360dd99233f69f22b36012a42856ace526da2c6da3cb0e6df2/tornado-6.5.9-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:dea3bee433b73d6312d993ce89ad5f2b8d3ed9fde56e79ba5342b9edb78d342a", size = 464279, upload-time = "2026-09-14T18:08:20.909Z" }, + { url = "https://files.pythonhosted.org/packages/26/4b/5af2f7dcd674e2f1cacef763456f79823404f50954789d6e1ec7a70cd58a/tornado-6.5.9-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:22bed49cdb55292d1d58f46008ba2cc3020e5b84995ef6ae749676c8b5309514", size = 462440, upload-time = "2026-09-14T18:08:22.711Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0f/38f8d16011de3b1ce2babb4c3bcefb126d2dc47d04878ae3ad5c39a00caa/tornado-6.5.9-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0cc0876a58eee9143476f24901b1b8eb4d52443283ade2b6bca650dd1da04f84", size = 465496, upload-time = "2026-09-14T18:08:24.407Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e9/3888f265d1e287fc63cd61a6696f96a650024e32f9e89c0b1817ac5c40f4/tornado-6.5.9-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc0d703cc7ec92b47ebfe236c97cd765c1f2e45744a2e156e6677e54c948274d", size = 466468, upload-time = "2026-09-14T18:08:25.928Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8b/d2bcf417981fa6f7698ab84a52394a76cf608e6704d266e2e0e29f1fd9d0/tornado-6.5.9-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:93f9ab0226ef625b94cacb7981a5af104f1735ea02f92282abe5c389109fb31d", size = 466303, upload-time = "2026-09-14T18:08:27.566Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/c5cfab11420c3f908238a6c861b8f3864fdbd2b2b87e30c9c16f36a11512/tornado-6.5.9-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5801bbf94dfde8d21087b6aa0b17f3a5dab6110dfe44e7e4492df5de393ac15b", size = 465706, upload-time = "2026-09-14T18:08:29.182Z" }, + { url = "https://files.pythonhosted.org/packages/ab/36/5a5da0aa15b7afdd46ece09c004a75af7d00f6a96b692bf696ff94bc2a55/tornado-6.5.9-cp39-abi3-win32.whl", hash = "sha256:dca9f377e80efe5dd4d52a2776b67492a81a39b2d931de169b6b36d0c1da444c", size = 466750, upload-time = "2026-09-14T18:08:30.836Z" }, + { url = "https://files.pythonhosted.org/packages/34/d7/2446cf3125372e2b4be6483b4b93f360dc50ce2493864755484b23bb50f2/tornado-6.5.9-cp39-abi3-win_amd64.whl", hash = "sha256:f810730ac71eadf009e6e5cb6d534d096887310f5649cff19d7e0d19c0bf4ee7", size = 467191, upload-time = "2026-09-14T18:08:32.521Z" }, + { url = "https://files.pythonhosted.org/packages/39/70/f1024364bf78fa921c63ef2883d36a3b3b705750c98ae174593afaab7a65/tornado-6.5.9-cp39-abi3-win_arm64.whl", hash = "sha256:b5f4d92798337260c6d9f9f0e400c7539dd5e55cf93479a3f8508c9aa09c2b3b", size = 466211, upload-time = "2026-09-14T18:08:35.18Z" }, ] [[package]] From c4f55715ec42d5387c06ace7f8eeb6522fbed76a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:28:27 -0700 Subject: [PATCH 09/17] chore(deps): bump the minor-and-patch group in /libs/sdk-py with 5 updates (#9149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group in /libs/sdk-py with 5 updates: | Package | From | To | | --- | --- | --- | | [langchain-core](https://github.com/langchain-ai/langchain) | `1.6.1` | `1.6.5` | | [pytest-mock](https://github.com/pytest-dev/pytest-mock) | `3.15.1` | `3.16.0` | | [ruff](https://github.com/astral-sh/ruff) | `0.16.5` | `0.16.9` | | [ty](https://github.com/astral-sh/ty) | `0.0.75` | `0.0.84` | | [starlette](https://github.com/Kludex/starlette) | `1.6.0` | `1.7.0` | Updates `langchain-core` from 1.6.1 to 1.6.5
    Release notes

    Sourced from langchain-core's releases.

    langchain-core==1.6.5

    Changes since langchain-core==1.6.4

    release(core): 1.6.5 (#40816) fix(core): abbreviate long tool IDs in XML buffer strings (#40792)

    langchain-core==1.6.4

    Changes since langchain-core==1.6.3

    release(core): 1.6.4 (#40718) chore(core): deprecate chat message history (#40711) chore(deps): bump anyio from 4.12.0 to 4.14.2 in /libs/core (#40634) chore(deps): bump soupsieve from 2.8.4 to 2.9 in /libs/core (#40574)

    langchain-core==1.6.3

    Changes since langchain-core==1.6.2

    release(core): 1.6.3 (#40407) feat(core): Allow model name and provider tracing metadata override based on gateway response (#40406) test(core): cover the deprecated .text() access path (#40243) docs(core): remove stale Args/Raises entries from FileCallbackHandler._write and ChatGeneration.set_text (#40211)

    langchain-core==1.6.2

    Changes since langchain-core==1.6.1

    release(core): 1.6.2 (#40209) feat(openai): support async tools (#40208) chore(deps): bump mistune from 3.3.0 to 3.3.3 in /libs/core (#40150) chore(deps): bump tornado from 6.5.7 to 6.5.8 in /libs/core (#40113) fix(core): avoid mutation in google-genai standard content (#40023) fix(core): avoid mutation in bedrock converse standard content (#40022)

    Commits

    Updates `pytest-mock` from 3.15.1 to 3.16.0
    Release notes

    Sourced from pytest-mock's releases.

    v3.16.0

    2026-09-27

    • #604: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • #611: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • #606: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • #547: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • #147: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Changelog

    Sourced from pytest-mock's changelog.

    3.16.0

    2026-09-27

    • [#604](https://github.com/pytest-dev/pytest-mock/issues/604) <https://github.com/pytest-dev/pytest-mock/pull/604>_: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • [#611](https://github.com/pytest-dev/pytest-mock/issues/611) <https://github.com/pytest-dev/pytest-mock/pull/611>_: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • [#606](https://github.com/pytest-dev/pytest-mock/issues/606) <https://github.com/pytest-dev/pytest-mock/pull/606>_: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • [#547](https://github.com/pytest-dev/pytest-mock/issues/547) <https://github.com/pytest-dev/pytest-mock/issues/547>_: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • [#147](https://github.com/pytest-dev/pytest-mock/issues/147) <https://github.com/pytest-dev/pytest-mock/issues/147>_: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Commits

    Updates `ruff` from 0.16.5 to 0.16.9
    Release notes

    Sourced from ruff's releases.

    0.16.9

    Release Notes

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    Install ruff 0.16.9

    Install prebuilt binaries via shell script

    curl --proto '=https' --tlsv1.2 -LsSf
    https://releases.astral.sh/github/ruff/releases/download/0.16.9/ruff-installer.sh
    | sh
    

    ... (truncated)

    Changelog

    Sourced from ruff's changelog.

    0.16.9

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    0.16.8

    Released on 2026-09-16.

    Bug fixes

    • Visit functional TypedDict keyword arguments correctly (#28584)
    • [flake8-simplify] Detect nested async with under sync parent (SIM117) (#27821)
    • [flake8-simplify] Preserve operand order in SIM109 fix (#27824)

    ... (truncated)

    Commits
    • 0be08a2 Bump version to 0.16.9 (#28882)
    • b4920b7 Rename ruff_cli to ruff_command_line (#28881)
    • 47c751b Update dependency astral-sh/uv to v0.12.18 (#28880)
    • 8c244e5 [flake8-comprehensions] Document map/generator exception behavior (C417...
    • 5edf5a1 Use target form in rooster.version_files (#28876)
    • 915bb2b [ty] Prefer existing @ paths over response files in Ruff and ty (#28877)
    • 4710e1a ci(github): update version number in placeholder of issue template (#28871)
    • eedfc62 [ty] Propagate outer type context through cast calls (#28855)
    • ceaa6a0 [ty] Contain rendered code within Markdown fences (#28869)
    • dba0f30 authorize ruff-pre-commit dispatch via OIDC (#28867)
    • Additional commits viewable in compare view

    Updates `ty` from 0.0.75 to 0.0.84
    Release notes

    Sourced from ty's releases.

    0.0.84

    Release Notes

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Changelog

    Sourced from ty's changelog.

    0.0.84

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Commits

    Updates `starlette` from 1.6.0 to 1.7.0
    Release notes

    Sourced from starlette's releases.

    Version 1.7.0

    This release adds experimental OpenTelemetry tracing, HTTP QUERY support, and response trailers in TestClient. Starlette now requires AnyIO 4.

    [!WARNING] OpenTelemetryMiddleware is experimental. Its API and emitted telemetry may change in minor releases without a deprecation period.

    Added

    • Add experimental OpenTelemetryMiddleware for HTTP server spans, with URL exclusions and custom tracer providers #3438, #3463, and #3520.
    • Expose the matched route through scope["route"] #3438.
    • Support the QUERY HTTP method in HTTPEndpoint, CORS, and OpenAPI 3.2 schema generation #3489.
    • Capture HTTP response trailers in TestClient and expose them through response.extensions["http.response.trailers"] #3563.
    • Support partitioned cookies in SessionMiddleware #3510.
    • Add partitioned to Response.delete_cookie() on Python 3.14 and later #3376.
    • Support IPv6 hosts in TrustedHostMiddleware and TestClient #3471.
    • Support Python 3.15 #3508.

    Changed

    • Require anyio>=4.0.0,<5, dropping support for AnyIO 3 #3512.
    • Raise WebSocketDisconnected, a RuntimeError subclass, for disconnected WebSocket operations #2767.
    • Accept Collection[str] in CORSMiddleware configuration annotations, including sets and frozensets #3518.

    Fixed

    • Run background tasks only after the response is sent when using BaseHTTPMiddleware #3476.
    • Return 400 for invalid multipart parser input #3492.
    • Include Vary: Origin on all normal CORS responses and vary preflight responses by all request headers that affect them #3516 and #3517.
    • Handle malformed Host headers and IPv6 authorities consistently across URL construction, host routing, and redirect middleware #3472.
    • Ignore Range headers when FileResponse has a status other than 200, preserving its status and full body #3568.
    • Handle standalone If-None-Match: * in StaticFiles #3201.
    • Reject WebSocket requests to StaticFiles without raising an assertion error #3532.
    • Persist session mutations made with popitem() and |= #3436.
    • Handle empty and absent payloads in WebSocketEndpoint.decode() #3372.
    • Implement identity on SimpleUser and UnauthenticatedUser #3271.
    • Allow HTTPException to use non-standard status codes without an explicit detail #3545.
    • Avoid deprecated AnyIO imports in TestClient and add explicit imports in WSGIMiddleware for AnyIO 4.15 compatibility #3498 and #3501.
    • Offload debug traceback rendering to a worker thread in ServerErrorMiddleware #2858.

    Full changelog: 1.6.0...1.7.0

    Changelog

    Sourced from starlette's changelog.

    1.7.0 (September 23, 2026)

    This release adds experimental OpenTelemetry tracing and requires AnyIO 4.

    !!! warning "OpenTelemetryMiddleware is experimental" Its API and emitted telemetry may change in minor releases without a deprecation period #3574.

    Added

    • Add experimental OpenTelemetryMiddleware for HTTP server spans, with URL exclusions and custom tracer providers #3438, #3463, and #3520.
    • Expose the matched route through scope["route"] #3438.
    • Support the QUERY HTTP method in HTTPEndpoint, CORS, and OpenAPI 3.2 schema generation #3489.
    • Capture HTTP response trailers in TestClient and expose them through response.extensions["http.response.trailers"] #3563.
    • Support partitioned cookies in SessionMiddleware #3510.
    • Add partitioned to Response.delete_cookie() on Python 3.14 and later #3376.
    • Support IPv6 hosts in TrustedHostMiddleware and TestClient #3471.
    • Support Python 3.15 #3508.

    Changed

    • Require anyio>=4.0.0,<5, dropping support for AnyIO 3 #3512.
    • Raise WebSocketDisconnected, a RuntimeError subclass, for disconnected WebSocket operations #2767.
    • Accept Collection[str] in CORSMiddleware configuration annotations, including sets and frozensets #3518.

    Fixed

    • Run background tasks only after the response is sent when using BaseHTTPMiddleware #3476.
    • Return 400 for invalid multipart parser input #3492.
    • Include Vary: Origin on all normal CORS responses and vary preflight responses by all request headers that affect them #3516 and #3517.
    • Handle malformed Host headers and IPv6 authorities consistently across URL construction, host routing, and redirect middleware #3472.
    • Ignore Range headers when FileResponse has a status other than 200, preserving its status and full body #3568.
    • Handle standalone If-None-Match: * in StaticFiles #3201.
    • Reject WebSocket requests to StaticFiles without raising an assertion error #3532.
    • Persist session mutations made with popitem() and |= #3436.
    • Handle empty and absent payloads in WebSocketEndpoint.decode() #3372.
    • Implement identity on SimpleUser and UnauthenticatedUser #3271.
    • Allow HTTPException to use non-standard status codes without an explicit detail #3545.
    • Avoid deprecated AnyIO imports in TestClient and add explicit imports in WSGIMiddleware for AnyIO 4.15 compatibility #3498 and #3501.
    • Offload debug traceback rendering to a worker thread in ServerErrorMiddleware #2858.
    Commits
    • 2269e9a Version 1.7.0 (#3575)
    • 4fe55eb Preserve FileResponse status for range requests (#3568)
    • 1f08daf Mark OpenTelemetryMiddleware as experimental (#3574)
    • 57de5fa Support HTTP response trailers in TestClient (#3563)
    • 03f12b7 Allow HTTPException to use non-standard status codes (#3545)
    • 76fd00f Reject WebSocket requests to StaticFiles (#3532)
    • f03f65c docs: fix 'its not available' and 'This ensure' wording (#3526)
    • 485aca4 docs: the test client is built on httpx2, not httpx (#3525)
    • fd662b1 Implement identity on SimpleUser and UnauthenticatedUser (#3271)
    • 41db6a7 Stabilize CodSpeed upload buffer allocations (#3524)
    • Additional commits viewable in compare view

    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) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
    --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Kennedy Co-authored-by: open-swe[bot] --- libs/langgraph/uv.lock | 4 +- libs/prebuilt/uv.lock | 4 +- libs/sdk-py/langgraph_sdk/auth/__init__.py | 2 +- libs/sdk-py/pyproject.toml | 2 +- libs/sdk-py/uv.lock | 98 +++++++++++----------- 5 files changed, 55 insertions(+), 55 deletions(-) diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index 9339a3145..e3162a633 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -1917,13 +1917,13 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-mock" }, { name = "pytest-watch" }, - { name = "ruff", specifier = "==0.16.5" }, + { name = "ruff", specifier = "==0.16.9" }, { name = "starlette" }, { name = "ty" }, ] lint = [ { name = "codespell" }, - { name = "ruff", specifier = "==0.16.5" }, + { name = "ruff", specifier = "==0.16.9" }, { name = "starlette" }, { name = "ty" }, ] diff --git a/libs/prebuilt/uv.lock b/libs/prebuilt/uv.lock index 8f5975073..889fd4513 100644 --- a/libs/prebuilt/uv.lock +++ b/libs/prebuilt/uv.lock @@ -626,13 +626,13 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-mock" }, { name = "pytest-watch" }, - { name = "ruff", specifier = "==0.16.5" }, + { name = "ruff", specifier = "==0.16.9" }, { name = "starlette" }, { name = "ty" }, ] lint = [ { name = "codespell" }, - { name = "ruff", specifier = "==0.16.5" }, + { name = "ruff", specifier = "==0.16.9" }, { name = "starlette" }, { name = "ty" }, ] diff --git a/libs/sdk-py/langgraph_sdk/auth/__init__.py b/libs/sdk-py/langgraph_sdk/auth/__init__.py index 5f812718a..e7fc0904f 100644 --- a/libs/sdk-py/langgraph_sdk/auth/__init__.py +++ b/libs/sdk-py/langgraph_sdk/auth/__init__.py @@ -312,7 +312,7 @@ class _ActionHandler(typing.Protocol[V]): ) -> types.HandlerResult: ... -T = typing.TypeVar("T", covariant=True) +T = typing.TypeVar("T") class _ResourceActionOn(typing.Generic[T]): diff --git a/libs/sdk-py/pyproject.toml b/libs/sdk-py/pyproject.toml index 0df3b3ec4..b4c55bafb 100644 --- a/libs/sdk-py/pyproject.toml +++ b/libs/sdk-py/pyproject.toml @@ -36,7 +36,7 @@ test = [ "pytest-watch", ] lint = [ - "ruff==0.16.5", + "ruff==0.16.9", "codespell", "ty", "starlette", diff --git a/libs/sdk-py/uv.lock b/libs/sdk-py/uv.lock index 8dc8c9423..5bd3b4ae2 100644 --- a/libs/sdk-py/uv.lock +++ b/libs/sdk-py/uv.lock @@ -266,7 +266,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.6.1" +version = "1.6.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -281,9 +281,9 @@ dependencies = [ { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/12/aff76ca89c219ebe6f9dd3c5dbc4e3b1cf5450e9fc7037dccad23d45cd7a/langchain_core-1.6.1.tar.gz", hash = "sha256:1b156cb395aac4f009a8a1b38a574c7d948fe2d5f74c96e0d8a5017b4149e04f", size = 1003359, upload-time = "2026-08-27T19:31:14.956Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/ed/ceecd3bfcfc77a94cf30a9f1506e907fdea447458825e01914a72d2d1a6f/langchain_core-1.6.5.tar.gz", hash = "sha256:bdb059fef65473fd444558dbb18f50901657402c43ceed9dc0fdc7b76a393c6b", size = 1007041, upload-time = "2026-09-24T18:11:06.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/25/f50dd65673c819aa33d3c34df58c115dbb6ec627d19f93e6e401dd0fc8d7/langchain_core-1.6.1-py3-none-any.whl", hash = "sha256:954a84132a5cb0435d27b910e336347b6744ecc18fbeef1e2de7029a0959841a", size = 571478, upload-time = "2026-08-27T19:31:13.34Z" }, + { url = "https://files.pythonhosted.org/packages/59/68/447ac7e734990b7ed9dcfc3b6dee9a4cd4d4169e296429fa71a9a47c1c7b/langchain_core-1.6.5-py3-none-any.whl", hash = "sha256:54c7b0e9314b9084fb04405bb33dc5d32986d512d92b7b8863899cd5f6267556", size = 572136, upload-time = "2026-09-24T18:11:04.837Z" }, ] [[package]] @@ -537,13 +537,13 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-mock" }, { name = "pytest-watch" }, - { name = "ruff", specifier = "==0.16.5" }, + { name = "ruff", specifier = "==0.16.9" }, { name = "starlette" }, { name = "ty" }, ] lint = [ { name = "codespell" }, - { name = "ruff", specifier = "==0.16.5" }, + { name = "ruff", specifier = "==0.16.9" }, { name = "starlette" }, { name = "ty" }, ] @@ -897,14 +897,14 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.15.1" +version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/7f/6ed29931d5c8cd396e7c0a55412e6cc88020373365c8685985dea53d26d7/pytest_mock-3.16.0.tar.gz", hash = "sha256:5a8395528b8f498205f3718f575228d0edaed7425fff638f87d1a6c3e0383636", size = 35362, upload-time = "2026-09-27T14:57:55.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/5b/b83a9bf1a3b4ec222f9fa083147ff6816245223da0ab92370e7e056f113f/pytest_mock-3.16.0-py3-none-any.whl", hash = "sha256:007cfeb257801d88d9c0b2a7b5a15a15e73b71968dfd72e7bf8c4a2f8393aec8", size = 10016, upload-time = "2026-09-27T14:57:54.283Z" }, ] [[package]] @@ -1012,40 +1012,40 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.5" +version = "0.16.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, - { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, - { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, - { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, - { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, - { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, - { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, - { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" }, + { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" }, + { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" }, + { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" }, + { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" }, ] [[package]] name = "starlette" -version = "1.6.0" +version = "1.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/b4/205b0d5241d934e8add0c38aa924c4f9fb7330834ff11e5444db964ec3f9/starlette-1.6.0.tar.gz", hash = "sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b", size = 2716969, upload-time = "2026-08-08T18:27:57.512Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/2b/3850dc6bf7ef71b088962eba31dafc6cffd2f96e577ebb0bb316df96da3e/starlette-1.7.0.tar.gz", hash = "sha256:c79f74ea63cff761804fbbfb182f1e0b440c2d07b164d24700c5a1bab5d6ff5d", size = 2736246, upload-time = "2026-09-23T07:30:26.35Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d6/1ec1b290f9e0fb067899b61e1d37a30c923068bad260b216dbe37a7d2967/starlette-1.7.0-py3-none-any.whl", hash = "sha256:67f8e99895493dd2911a03f11314af6ceebeae4e704bb9f43dfc6a9db151c93e", size = 78980, upload-time = "2026-09-23T07:30:24.567Z" }, ] [[package]] @@ -1113,27 +1113,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.75" +version = "0.0.84" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, - { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, - { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, - { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, - { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, - { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, - { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" }, ] [[package]] From 2053f0b1769a5a369da1aa8405498567588df83b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:53:48 -0700 Subject: [PATCH 10/17] chore(deps-dev): bump the minor-and-patch group in /libs/checkpoint-conformance with 2 updates (#9145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group in /libs/checkpoint-conformance with 2 updates: [ruff](https://github.com/astral-sh/ruff) and [ty](https://github.com/astral-sh/ty). Updates `ruff` from 0.16.5 to 0.16.9
    Release notes

    Sourced from ruff's releases.

    0.16.9

    Release Notes

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    Install ruff 0.16.9

    Install prebuilt binaries via shell script

    curl --proto '=https' --tlsv1.2 -LsSf
    https://releases.astral.sh/github/ruff/releases/download/0.16.9/ruff-installer.sh
    | sh
    

    ... (truncated)

    Changelog

    Sourced from ruff's changelog.

    0.16.9

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    0.16.8

    Released on 2026-09-16.

    Bug fixes

    • Visit functional TypedDict keyword arguments correctly (#28584)
    • [flake8-simplify] Detect nested async with under sync parent (SIM117) (#27821)
    • [flake8-simplify] Preserve operand order in SIM109 fix (#27824)

    ... (truncated)

    Commits
    • 0be08a2 Bump version to 0.16.9 (#28882)
    • b4920b7 Rename ruff_cli to ruff_command_line (#28881)
    • 47c751b Update dependency astral-sh/uv to v0.12.18 (#28880)
    • 8c244e5 [flake8-comprehensions] Document map/generator exception behavior (C417...
    • 5edf5a1 Use target form in rooster.version_files (#28876)
    • 915bb2b [ty] Prefer existing @ paths over response files in Ruff and ty (#28877)
    • 4710e1a ci(github): update version number in placeholder of issue template (#28871)
    • eedfc62 [ty] Propagate outer type context through cast calls (#28855)
    • ceaa6a0 [ty] Contain rendered code within Markdown fences (#28869)
    • dba0f30 authorize ruff-pre-commit dispatch via OIDC (#28867)
    • Additional commits viewable in compare view

    Updates `ty` from 0.0.75 to 0.0.84
    Release notes

    Sourced from ty's releases.

    0.0.84

    Release Notes

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Changelog

    Sourced from ty's changelog.

    0.0.84

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Commits

    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) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
    --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Kennedy Co-authored-by: open-swe[bot] --- libs/checkpoint-conformance/uv.lock | 76 ++++++++++++++--------------- libs/langgraph/tests/test_retry.py | 3 ++ 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/libs/checkpoint-conformance/uv.lock b/libs/checkpoint-conformance/uv.lock index 806e7fa91..3e7c6dc80 100644 --- a/libs/checkpoint-conformance/uv.lock +++ b/libs/checkpoint-conformance/uv.lock @@ -809,27 +809,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.5" +version = "0.16.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, - { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, - { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, - { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, - { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, - { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, - { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, - { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" }, + { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" }, + { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" }, + { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" }, + { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" }, ] [[package]] @@ -897,27 +897,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.75" +version = "0.0.84" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, - { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, - { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, - { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, - { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, - { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, - { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" }, ] [[package]] diff --git a/libs/langgraph/tests/test_retry.py b/libs/langgraph/tests/test_retry.py index aa48b0812..e7f1a8148 100644 --- a/libs/langgraph/tests/test_retry.py +++ b/libs/langgraph/tests/test_retry.py @@ -2755,6 +2755,7 @@ def test_error_handler_resumes_after_crash_multiple_nodes(): call_count = {"a": 0, "b": 0, "handler_a": 0, "handler_b": 0} handler_a_started = threading.Event() + handler_b_started = threading.Event() def node_a(state: State) -> State: call_count["a"] += 1 @@ -2775,6 +2776,7 @@ def test_error_handler_resumes_after_crash_multiple_nodes(): assert "a failed" in str(error.error) handler_a_started.set() if handler_should_fail[0]: + assert handler_b_started.wait(timeout=5), "handler_b never started" raise RuntimeError("handler_a crash") return {"results": [f"recovered_a:{error.node}"]} @@ -2782,6 +2784,7 @@ def test_error_handler_resumes_after_crash_multiple_nodes(): call_count["handler_b"] += 1 assert error.node == "b" assert "b failed" in str(error.error) + handler_b_started.set() if handler_should_fail[0]: raise RuntimeError("handler_b crash") return {"results": [f"recovered_b:{error.node}"]} From 5204e60579052695c16dd2579d6a50af966d86a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:54:00 -0700 Subject: [PATCH 11/17] chore(deps-dev): bump the minor-and-patch group in /libs/checkpoint-sqlite with 3 updates (#9146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group in /libs/checkpoint-sqlite with 3 updates: [pytest-mock](https://github.com/pytest-dev/pytest-mock), [ruff](https://github.com/astral-sh/ruff) and [ty](https://github.com/astral-sh/ty). Updates `pytest-mock` from 3.15.1 to 3.16.0
    Release notes

    Sourced from pytest-mock's releases.

    v3.16.0

    2026-09-27

    • #604: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • #611: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • #606: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • #547: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • #147: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Changelog

    Sourced from pytest-mock's changelog.

    3.16.0

    2026-09-27

    • [#604](https://github.com/pytest-dev/pytest-mock/issues/604) <https://github.com/pytest-dev/pytest-mock/pull/604>_: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • [#611](https://github.com/pytest-dev/pytest-mock/issues/611) <https://github.com/pytest-dev/pytest-mock/pull/611>_: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • [#606](https://github.com/pytest-dev/pytest-mock/issues/606) <https://github.com/pytest-dev/pytest-mock/pull/606>_: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • [#547](https://github.com/pytest-dev/pytest-mock/issues/547) <https://github.com/pytest-dev/pytest-mock/issues/547>_: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • [#147](https://github.com/pytest-dev/pytest-mock/issues/147) <https://github.com/pytest-dev/pytest-mock/issues/147>_: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Commits

    Updates `ruff` from 0.16.5 to 0.16.9
    Release notes

    Sourced from ruff's releases.

    0.16.9

    Release Notes

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    Install ruff 0.16.9

    Install prebuilt binaries via shell script

    curl --proto '=https' --tlsv1.2 -LsSf
    https://releases.astral.sh/github/ruff/releases/download/0.16.9/ruff-installer.sh
    | sh
    

    ... (truncated)

    Changelog

    Sourced from ruff's changelog.

    0.16.9

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    0.16.8

    Released on 2026-09-16.

    Bug fixes

    • Visit functional TypedDict keyword arguments correctly (#28584)
    • [flake8-simplify] Detect nested async with under sync parent (SIM117) (#27821)
    • [flake8-simplify] Preserve operand order in SIM109 fix (#27824)

    ... (truncated)

    Commits
    • 0be08a2 Bump version to 0.16.9 (#28882)
    • b4920b7 Rename ruff_cli to ruff_command_line (#28881)
    • 47c751b Update dependency astral-sh/uv to v0.12.18 (#28880)
    • 8c244e5 [flake8-comprehensions] Document map/generator exception behavior (C417...
    • 5edf5a1 Use target form in rooster.version_files (#28876)
    • 915bb2b [ty] Prefer existing @ paths over response files in Ruff and ty (#28877)
    • 4710e1a ci(github): update version number in placeholder of issue template (#28871)
    • eedfc62 [ty] Propagate outer type context through cast calls (#28855)
    • ceaa6a0 [ty] Contain rendered code within Markdown fences (#28869)
    • dba0f30 authorize ruff-pre-commit dispatch via OIDC (#28867)
    • Additional commits viewable in compare view

    Updates `ty` from 0.0.75 to 0.0.84
    Release notes

    Sourced from ty's releases.

    0.0.84

    Release Notes

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Changelog

    Sourced from ty's changelog.

    0.0.84

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Commits

    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) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
    --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Kennedy Co-authored-by: open-swe[bot] --- libs/checkpoint-sqlite/README.md | 40 ++------- .../langgraph/store/sqlite/aio.py | 2 +- libs/checkpoint-sqlite/uv.lock | 82 +++++++++---------- 3 files changed, 50 insertions(+), 74 deletions(-) diff --git a/libs/checkpoint-sqlite/README.md b/libs/checkpoint-sqlite/README.md index 16ff9dc3c..1064934f2 100644 --- a/libs/checkpoint-sqlite/README.md +++ b/libs/checkpoint-sqlite/README.md @@ -40,24 +40,12 @@ with SqliteSaver.from_conn_string(":memory:") as checkpointer: "v": 4, "ts": "2024-07-31T20:14:19.804150+00:00", "id": "1ef4f797-8335-6428-8001-8a1503f9b875", - "channel_values": { - "my_key": "meow", - "node": "node" - }, - "channel_versions": { - "__start__": 2, - "my_key": 3, - "start:node": 3, - "node": 3 - }, + "channel_values": {"my_key": "meow", "node": "node"}, + "channel_versions": {"__start__": 2, "my_key": 3, "start:node": 3, "node": 3}, "versions_seen": { "__input__": {}, - "__start__": { - "__start__": 1 - }, - "node": { - "start:node": 2 - } + "__start__": {"__start__": 1}, + "node": {"start:node": 2}, }, } @@ -81,24 +69,12 @@ async with AsyncSqliteSaver.from_conn_string(":memory:") as checkpointer: "v": 4, "ts": "2024-07-31T20:14:19.804150+00:00", "id": "1ef4f797-8335-6428-8001-8a1503f9b875", - "channel_values": { - "my_key": "meow", - "node": "node" - }, - "channel_versions": { - "__start__": 2, - "my_key": 3, - "start:node": 3, - "node": 3 - }, + "channel_values": {"my_key": "meow", "node": "node"}, + "channel_versions": {"__start__": 2, "my_key": 3, "start:node": 3, "node": 3}, "versions_seen": { "__input__": {}, - "__start__": { - "__start__": 1 - }, - "node": { - "start:node": 2 - } + "__start__": {"__start__": 1}, + "node": {"start:node": 2}, }, } diff --git a/libs/checkpoint-sqlite/langgraph/store/sqlite/aio.py b/libs/checkpoint-sqlite/langgraph/store/sqlite/aio.py index b0d711dff..5ea5672f1 100644 --- a/libs/checkpoint-sqlite/langgraph/store/sqlite/aio.py +++ b/libs/checkpoint-sqlite/langgraph/store/sqlite/aio.py @@ -547,7 +547,7 @@ class AsyncSqliteStore(AsyncBatchedBaseStore, BaseSqliteStore): await cur.execute(query, params) rows = await cur.fetchall() - if needs_refresh and rows and self.ttl_config: + if needs_refresh and self.ttl_config: keys_to_refresh = [] for row_data in rows: # Assuming row_data[0] is prefix (text), row_data[1] is key (text) diff --git a/libs/checkpoint-sqlite/uv.lock b/libs/checkpoint-sqlite/uv.lock index 1c28fc97a..349221b32 100644 --- a/libs/checkpoint-sqlite/uv.lock +++ b/libs/checkpoint-sqlite/uv.lock @@ -783,14 +783,14 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.15.1" +version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/7f/6ed29931d5c8cd396e7c0a55412e6cc88020373365c8685985dea53d26d7/pytest_mock-3.16.0.tar.gz", hash = "sha256:5a8395528b8f498205f3718f575228d0edaed7425fff638f87d1a6c3e0383636", size = 35362, upload-time = "2026-09-27T14:57:55.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/5b/b83a9bf1a3b4ec222f9fa083147ff6816245223da0ab92370e7e056f113f/pytest_mock-3.16.0-py3-none-any.whl", hash = "sha256:007cfeb257801d88d9c0b2a7b5a15a15e73b71968dfd72e7bf8c4a2f8393aec8", size = 10016, upload-time = "2026-09-27T14:57:54.283Z" }, ] [[package]] @@ -911,27 +911,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.5" +version = "0.16.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, - { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, - { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, - { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, - { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, - { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, - { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, - { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" }, + { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" }, + { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" }, + { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" }, + { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" }, ] [[package]] @@ -1011,27 +1011,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.75" +version = "0.0.84" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, - { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, - { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, - { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, - { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, - { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, - { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" }, ] [[package]] From 157a06dda988d85afeb8751ff27b35ab3f4f8bf4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:57:28 -0700 Subject: [PATCH 12/17] chore(deps-dev): bump the minor-and-patch group across 1 directory with 5 updates (#9155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor-and-patch group with 5 updates in the /libs/langgraph directory: | Package | From | To | | --- | --- | --- | | [langchain-core](https://github.com/langchain-ai/langchain) | `1.6.1` | `1.6.5` | | [pytest-mock](https://github.com/pytest-dev/pytest-mock) | `3.15.1` | `3.16.0` | | [syrupy](https://github.com/syrupy-project/syrupy) | `6.0.0` | `6.1.1` | | [ruff](https://github.com/astral-sh/ruff) | `0.16.5` | `0.16.9` | | [ty](https://github.com/astral-sh/ty) | `0.0.75` | `0.0.84` | Updates `langchain-core` from 1.6.1 to 1.6.5
    Release notes

    Sourced from langchain-core's releases.

    langchain-core==1.6.5

    Changes since langchain-core==1.6.4

    release(core): 1.6.5 (#40816) fix(core): abbreviate long tool IDs in XML buffer strings (#40792)

    langchain-core==1.6.4

    Changes since langchain-core==1.6.3

    release(core): 1.6.4 (#40718) chore(core): deprecate chat message history (#40711) chore(deps): bump anyio from 4.12.0 to 4.14.2 in /libs/core (#40634) chore(deps): bump soupsieve from 2.8.4 to 2.9 in /libs/core (#40574)

    langchain-core==1.6.3

    Changes since langchain-core==1.6.2

    release(core): 1.6.3 (#40407) feat(core): Allow model name and provider tracing metadata override based on gateway response (#40406) test(core): cover the deprecated .text() access path (#40243) docs(core): remove stale Args/Raises entries from FileCallbackHandler._write and ChatGeneration.set_text (#40211)

    langchain-core==1.6.2

    Changes since langchain-core==1.6.1

    release(core): 1.6.2 (#40209) feat(openai): support async tools (#40208) chore(deps): bump mistune from 3.3.0 to 3.3.3 in /libs/core (#40150) chore(deps): bump tornado from 6.5.7 to 6.5.8 in /libs/core (#40113) fix(core): avoid mutation in google-genai standard content (#40023) fix(core): avoid mutation in bedrock converse standard content (#40022)

    Commits

    Updates `pytest-mock` from 3.15.1 to 3.16.0
    Release notes

    Sourced from pytest-mock's releases.

    v3.16.0

    2026-09-27

    • #604: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • #611: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • #606: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • #547: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • #147: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Changelog

    Sourced from pytest-mock's changelog.

    3.16.0

    2026-09-27

    • [#604](https://github.com/pytest-dev/pytest-mock/issues/604) <https://github.com/pytest-dev/pytest-mock/pull/604>_: Fixed duplicate_iterators=True for async functions spied with mocker.spy.
    • [#611](https://github.com/pytest-dev/pytest-mock/issues/611) <https://github.com/pytest-dev/pytest-mock/pull/611>_: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
    • [#606](https://github.com/pytest-dev/pytest-mock/issues/606) <https://github.com/pytest-dev/pytest-mock/pull/606>_: mocker.resetall(return_value=True, side_effect=True) now also applies to non-callable mocks, such as those returned by mocker.create_autospec(SomeClass, instance=True). Previously both arguments were silently ignored for them.
    • [#547](https://github.com/pytest-dev/pytest-mock/issues/547) <https://github.com/pytest-dev/pytest-mock/issues/547>_: Added SpyType for annotating mocker.spy results.
    • Dropped support for EOL Python 3.9.
    • [#147](https://github.com/pytest-dev/pytest-mock/issues/147) <https://github.com/pytest-dev/pytest-mock/issues/147>_: Removed handling of RuntimeError: stop called on unstarted patcher, which can no longer occur in the supported Python versions.
    • Added support for Python 3.15.
    Commits

    Updates `syrupy` from 6.0.0 to 6.1.1
    Release notes

    Sourced from syrupy's releases.

    v6.1.1

    Note: 6.1.0 was skipped due to a CI issue. Use 6.1.1.

    Experimental --snapshot-file-lock for pytest-xdist

    --snapshot-update under pytest-xdist can race when multiple workers rewrite the same multi-entry amber (.ambr) file, silently dropping snapshots (#1237).

    This release adds an experimental opt-in:

    • --snapshot-file-lock — exclusive file lock + atomic replace around amber writes
    • --snapshot-file-lock-timeout — max wait for the lock (default 60s)

    Same-machine workers only; not reliable across remote workers / NFS. See #1239.

    Callout for pytest-xdist users: please try --snapshot-file-lock with --snapshot-update and open an issue if you hit problems. Locking is expected to become the default in a future minor release.

    New Contributors

    Full Changelog: https://github.com/syrupy-project/syrupy/compare/v6.0.0...v6.1.1

    Changelog

    Sourced from syrupy's changelog.

    v6.1.1 (2026-09-13)

    What's Changed

    New Contributors

    Full Changelog: https://github.com/syrupy-project/syrupy/compare/v6.0.0...v6.1.1

    v6.1.0 (2026-09-13)

    What's Changed

    New Contributors

    Full Changelog: https://github.com/syrupy-project/syrupy/compare/v6.0.0...v6.1.0

    Commits
    • 2d251e7 chore(release): 6.1.1 [skip ci]
    • 9b64ec0 chore(release): 6.1.0 [skip ci]
    • 5b4256e fix: opt-in file lock for concurrent amber writes under xdist (#1239)
    • d4cb151 chore(deps): update dependency hypothesis to v6.168.0 (#1238)
    • 16c4cb5 test: add coverage for read_snapshot_data_from_location returning None (#1236)
    • 2e1d3ba chore(deps): update dependency ruff to v0.16.6 (#1235)
    • fcc929d chore(deps): update actions/deploy-pages action to v5.0.1 (#1234)
    • 4f1bad7 chore(deps): update dependency hypothesis to v6.167.1 (#1232)
    • 24eda28 chore(deps): update dependency hypothesis to v6.167.0 (#1231)
    • 0d7e46f chore(deps): update dependency hypothesis to v6.166.0 (#1230)
    • Additional commits viewable in compare view

    Updates `ruff` from 0.16.5 to 0.16.9
    Release notes

    Sourced from ruff's releases.

    0.16.9

    Release Notes

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    Install ruff 0.16.9

    Install prebuilt binaries via shell script

    curl --proto '=https' --tlsv1.2 -LsSf
    https://releases.astral.sh/github/ruff/releases/download/0.16.9/ruff-installer.sh
    | sh
    

    ... (truncated)

    Changelog

    Sourced from ruff's changelog.

    0.16.9

    Released on 2026-09-24.

    Preview features

    • [ruff] Avoid false positives for overloaded division (RUF069) (#28309)

    Bug fixes

    • [flake8-bugbear] Avoid false positives for calls with keyword arguments (B009, B010, B043) (#28776)
    • [flake8-tidy-imports] Allow lazy imports to be used in deferred annotations (TID255) (#28767)

    Rule changes

    • Update LibCST-based fixes for Python 3.15 (#28616)
    • [flake8-pyi] Mention stubs in the diagnostic message (PYI002) (#28542)

    Documentation

    • Fix horizontal overflow on the rules documentation page (#28699)
    • Update rules table with category information (#28651)
    • [flake8-annotations] Clarify that ANN401 checks return types in addition to arguments (#28334)
    • [flake8-bugbear] Document type-checker interaction (B010) (#28509)
    • [flake8-comprehensions] Document map/generator exception behavior (C417) (#27794)
    • [ruff] Mention related isort settings (RUF022) (#28719)

    Contributors

    0.16.8

    Released on 2026-09-16.

    Bug fixes

    • Visit functional TypedDict keyword arguments correctly (#28584)
    • [flake8-simplify] Detect nested async with under sync parent (SIM117) (#27821)
    • [flake8-simplify] Preserve operand order in SIM109 fix (#27824)

    ... (truncated)

    Commits
    • 0be08a2 Bump version to 0.16.9 (#28882)
    • b4920b7 Rename ruff_cli to ruff_command_line (#28881)
    • 47c751b Update dependency astral-sh/uv to v0.12.18 (#28880)
    • 8c244e5 [flake8-comprehensions] Document map/generator exception behavior (C417...
    • 5edf5a1 Use target form in rooster.version_files (#28876)
    • 915bb2b [ty] Prefer existing @ paths over response files in Ruff and ty (#28877)
    • 4710e1a ci(github): update version number in placeholder of issue template (#28871)
    • eedfc62 [ty] Propagate outer type context through cast calls (#28855)
    • ceaa6a0 [ty] Contain rendered code within Markdown fences (#28869)
    • dba0f30 authorize ruff-pre-commit dispatch via OIDC (#28867)
    • Additional commits viewable in compare view

    Updates `ty` from 0.0.75 to 0.0.84
    Release notes

    Sourced from ty's releases.

    0.0.84

    Release Notes

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Changelog

    Sourced from ty's changelog.

    0.0.84

    Released on 2026-09-24.

    This release addresses GHSA-vxvm-j4xq-q7m4, a use-after-free vulnerability during incremental type checking that can result in arbitrary code execution when analyzing a specially crafted Python project. Users who run ty on untrusted code should upgrade to 0.0.84 or newer.

    Bug fixes

    • Fix stale diagnostics from the LSP server after toggling showSyntaxErrors (#28759)

    LSP server

    • Complete string keys from dictionary initializers (#28820)
    • Support LSP requests against closed documents (#28595)
    • Select projects for external files using import search paths (#28594)
    • Use workspace editor settings for external files (#28639)

    Performance

    • Avoid repeated subtyping checks for materialized recursive protocols (#28774)
    • Skip reading notebooks when discovering scripts (#28781)

    Core type checking

    • Avoid incorrect simplification of TypeIs materializations (#28817)
    • Fix disjointness of generic class types (#28787)
    • Fix staticmethod shadowing through generic receivers and unions (#28766)
    • Infer callable signatures from bounded type variables (#28599)
    • Infer constant membership in inline list and set literals (e.g. "foo" in ["foo"] is now inferred as Literal[True]) (#28676)
    • Infer through optional generic containers in the legacy solver (#28791)
    • Preserve call narrowing during cyclic inference (#28708)
    • Preserve intersections of type guard return types (#28796)
    • Use subtyping for constraint-set implication (#28657)

    Configuration

    • Disable invalid-legacy-positional-parameter by default (#28834)

    Other changes

    • Only consider reachable definitions when determining whether a condition should be exempted from redundant-condition(-strict) due to the condition being defined relative to sys.version_info, sys.platform, os.name or typing.TYPE_CHECKING (#28788)

    Contributors

    ... (truncated)

    Commits

    --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Kennedy Co-authored-by: open-swe[bot] --- libs/langgraph/langgraph/graph/message.py | 2 +- libs/langgraph/langgraph/pregel/main.py | 4 +- libs/langgraph/tests/test_messages_state.py | 5 ++ libs/langgraph/uv.lock | 94 ++++++++++----------- 4 files changed, 54 insertions(+), 51 deletions(-) diff --git a/libs/langgraph/langgraph/graph/message.py b/libs/langgraph/langgraph/graph/message.py index 64f375ad2..96aade71b 100644 --- a/libs/langgraph/langgraph/graph/message.py +++ b/libs/langgraph/langgraph/graph/message.py @@ -235,7 +235,7 @@ def add_messages( if format == "langchain-openai": merged = _format_messages(merged) - elif format: + elif cast(str | None, format): msg = f"Unrecognized {format=}. Expected one of 'langchain-openai', None." raise ValueError(msg) else: diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index f4ca48024..6101e26ba 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -2337,9 +2337,7 @@ class Pregel( [item for lst in user_group_by.values() for item in lst], ) - return patch_checkpoint_map( - next_config, saved.metadata if saved else None - ) + return patch_checkpoint_map(next_config, saved.metadata) # task ids can be provided in the StateUpdate, but if not, # we use the task id generated by prepare_next_tasks diff --git a/libs/langgraph/tests/test_messages_state.py b/libs/langgraph/tests/test_messages_state.py index f41552b16..a5775323f 100644 --- a/libs/langgraph/tests/test_messages_state.py +++ b/libs/langgraph/tests/test_messages_state.py @@ -152,6 +152,11 @@ def test_empty_inputs(): ] +def test_invalid_format(): + with pytest.raises(ValueError, match="Unrecognized format="): + add_messages([], [], format="invalid") + + def test_non_list_inputs(): left = HumanMessage(content="Hello", id="1") right = AIMessage(content="Hi there!", id="2") diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index e3162a633..df179835f 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -1404,7 +1404,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.6.1" +version = "1.6.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1418,9 +1418,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/12/aff76ca89c219ebe6f9dd3c5dbc4e3b1cf5450e9fc7037dccad23d45cd7a/langchain_core-1.6.1.tar.gz", hash = "sha256:1b156cb395aac4f009a8a1b38a574c7d948fe2d5f74c96e0d8a5017b4149e04f", size = 1003359, upload-time = "2026-08-27T19:31:14.956Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/ed/ceecd3bfcfc77a94cf30a9f1506e907fdea447458825e01914a72d2d1a6f/langchain_core-1.6.5.tar.gz", hash = "sha256:bdb059fef65473fd444558dbb18f50901657402c43ceed9dc0fdc7b76a393c6b", size = 1007041, upload-time = "2026-09-24T18:11:06.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/25/f50dd65673c819aa33d3c34df58c115dbb6ec627d19f93e6e401dd0fc8d7/langchain_core-1.6.1-py3-none-any.whl", hash = "sha256:954a84132a5cb0435d27b910e336347b6744ecc18fbeef1e2de7029a0959841a", size = 571478, upload-time = "2026-08-27T19:31:13.34Z" }, + { url = "https://files.pythonhosted.org/packages/59/68/447ac7e734990b7ed9dcfc3b6dee9a4cd4d4169e296429fa71a9a47c1c7b/langchain_core-1.6.5-py3-none-any.whl", hash = "sha256:54c7b0e9314b9084fb04405bb33dc5d32986d512d92b7b8863899cd5f6267556", size = 572136, upload-time = "2026-09-24T18:11:04.837Z" }, ] [[package]] @@ -2907,14 +2907,14 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.15.1" +version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/7f/6ed29931d5c8cd396e7c0a55412e6cc88020373365c8685985dea53d26d7/pytest_mock-3.16.0.tar.gz", hash = "sha256:5a8395528b8f498205f3718f575228d0edaed7425fff638f87d1a6c3e0383636", size = 35362, upload-time = "2026-09-27T14:57:55.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/5b/b83a9bf1a3b4ec222f9fa083147ff6816245223da0ab92370e7e056f113f/pytest_mock-3.16.0-py3-none-any.whl", hash = "sha256:007cfeb257801d88d9c0b2a7b5a15a15e73b71968dfd72e7bf8c4a2f8393aec8", size = 10016, upload-time = "2026-09-27T14:57:54.283Z" }, ] [[package]] @@ -3352,27 +3352,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.5" +version = "0.16.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, - { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, - { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, - { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, - { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, - { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, - { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, - { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" }, + { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" }, + { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" }, + { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" }, + { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" }, + { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" }, ] [[package]] @@ -3475,14 +3475,14 @@ wheels = [ [[package]] name = "syrupy" -version = "6.0.0" +version = "6.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d2/a7/3c797c76994dc817582abdda90ee31867a9883567b0add47dbc0ccb12ce4/syrupy-6.0.0.tar.gz", hash = "sha256:bf4f5f662a7b23a9e5711bd5597e301cd09c00f31be5fa4095f79da7b0585f3d", size = 99435, upload-time = "2026-08-22T20:48:39.493Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/2c/8ffc1414d1951895d0d5b7dc83c0b079ebc48c3bf27ffbb0064646367500/syrupy-6.1.1.tar.gz", hash = "sha256:7e584a8df4ede6dd3c1c44a77f9edc46186b6767a73dea6f0083044946839a59", size = 109705, upload-time = "2026-09-13T20:25:21.599Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/ff/4d8ae6d6070ea73ee9d466701584a75db9586ce09de98f8f1b3276efad5f/syrupy-6.0.0-py3-none-any.whl", hash = "sha256:3efa16d767bd8614413cc20057552e4aec14428c6e5ea788c1f7e03047e8a5e4", size = 58047, upload-time = "2026-08-22T20:48:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/2c/b5/bf2a8a335a6a813869dd482cedbcde252f582b5afbfe50eb78ec09962800/syrupy-6.1.1-py3-none-any.whl", hash = "sha256:6ac9ac6a42d70ae44156f5f157a9563b9bb8cc96dcf9364327f330463bff889a", size = 63436, upload-time = "2026-09-13T20:25:20.294Z" }, ] [[package]] @@ -3611,27 +3611,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.75" +version = "0.0.84" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/f6/34f8869c45ea87fe6a931ffa824b1fe4428167173543d92988d631add355/ty-0.0.84.tar.gz", hash = "sha256:0cefdb0cd5d399418dbe83c349ba605047b7dff815ae6f460c80e8522b40be67", size = 7409537, upload-time = "2026-09-24T13:16:24.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, - { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, - { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, - { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, - { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, - { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, - { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/600648778e1c79133ce6b6a5c4c2531ae16b90c9b30f5041b38b574f61b8/ty-0.0.84-py3-none-linux_armv6l.whl", hash = "sha256:868438e2b9abfc835dd5b5b07ed693db95d8348ac40e3228252511d63ae87695", size = 13935333, upload-time = "2026-09-24T13:15:31.167Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/27f06f49945c36170765fad82076898039ae3873338e9865f876dd62f12c/ty-0.0.84-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b00f39b521f0b559cde0f6abf8dd47213b513b04f33d58efeb9b98486a16bb17", size = 13511328, upload-time = "2026-09-24T13:15:34.446Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/b12dd130e7f30141f0645614f454c925991cd389b1cc6c8284fd6bf32326/ty-0.0.84-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f78c2915567f2ee62ce0c24d86091bc76f81577677755e242d09951db588b9a", size = 13445445, upload-time = "2026-09-24T13:15:37.476Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/f012de4b3c1d9a4773326621380d768b4d48c47e620dc012775c5e01c7d7/ty-0.0.84-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61155cad0921e890d86d1d911a40b3a0e924ef4bdc3b4f3865a6c89c7e3012dc", size = 13471235, upload-time = "2026-09-24T13:15:40.64Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/dca630f5f5353fa7bf67eebccf1549df99b16656d21071d0df6677c826a5/ty-0.0.84-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:417c3bf14947ed16b0b92976ab333c27915590354e96d4f581709068d3811355", size = 13680704, upload-time = "2026-09-24T13:15:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/8d/33702c8f62f05ad45b33644fb914d483de6a0a7bc8e51884e9b8773e6d69/ty-0.0.84-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e06ef117759f654f5379efe9ae79bda8a186016c016f1023d05c34f8e15c1546", size = 14518923, upload-time = "2026-09-24T13:15:46.802Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/da3246e6ef2ae0e842bca4058b25c54bee55ab058ffff4ee54e72517ec01/ty-0.0.84-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a05d1d53db8b64410ab38c6e32c5898510584869d1d5dd9c0605db81c9aef7df", size = 15027731, upload-time = "2026-09-24T13:15:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/b0099098a560aeb4f9fde6c912f9a81622c3e44e6555e1fbe322b368ec31/ty-0.0.84-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92400494ab855cfb1204ac1101e3f53b2d09a8e21d171758b2cf9acdab1809e", size = 14748260, upload-time = "2026-09-24T13:15:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/60/d5/32cd67aedb271f006e716ad093806c96c5d7e2af86be8255469dbd3d76c8/ty-0.0.84-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af17eb391ae3027fed9be1780bea555c2b8a25ba68e184c627b4981b2c9eaab7", size = 14205858, upload-time = "2026-09-24T13:15:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/638ac62ca2e8eca1b92e3e16273085744fada728b993500819b5c258ca15/ty-0.0.84-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:44cf06af0e7ec78ea6d8ff33a3450fc19627756a4fba681f7410d4e3ef63047a", size = 14573196, upload-time = "2026-09-24T13:15:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f6/9ee81c5d8921e5fd5f10889ae563c95cd3272b31df2ada528919ca1cef6b/ty-0.0.84-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c6e56b443ff9ba462e34bfbc1e582004c2719ed35f8f93a433744b0ba0afb2d9", size = 13480760, upload-time = "2026-09-24T13:16:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/f6/83/fbe5d1177667ae16d84c3242846b143809c9cc09568b333e5a9b4d5908f6/ty-0.0.84-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b6562fcc48820c1030e2a4509ef2c14ef93043ca4860280074314ac3a825b95", size = 13696346, upload-time = "2026-09-24T13:16:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/86/9a/1b4ac9ddc34cc0ab4624bc219825d66af52f1ad87f4778ad08ad03fa06d5/ty-0.0.84-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dee7421451efcbd70f03e95a8f2c8d9e5ec3d35edcd27b4095ee00ee062abfc4", size = 13959410, upload-time = "2026-09-24T13:16:08.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/04/70e289437a67dfd686056ab4fd91880261a8233033a6a95d29393741fb97/ty-0.0.84-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:31e96307cf621babe969307dd657783d672e2d8bdbd06493710da635bf63fa07", size = 14350294, upload-time = "2026-09-24T13:16:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f4/ae7dbd118425c850981d23efb850f0d9d7f6b8d2b852bde2767434f668b4/ty-0.0.84-py3-none-win32.whl", hash = "sha256:4bdc85f91cbaae35825f2b17bb4ac5de0563367875cb0f42b2bf07a1bf7ff2ba", size = 13176439, upload-time = "2026-09-24T13:16:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/29/ec/994d87252f065dda5a500bc8afc726d18f6582d81d6f4ff1feb8a267c800/ty-0.0.84-py3-none-win_amd64.whl", hash = "sha256:71cc9aa7a7c89d4e12c6a814e9c0c3310ad5bee44efedbd6f5a42d91e9ac0f0d", size = 13962879, upload-time = "2026-09-24T13:16:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/e6e91852b72566b1ab9aca504712857dfb89324c12092a5bd61d42d34427/ty-0.0.84-py3-none-win_arm64.whl", hash = "sha256:6c9d3718c4a0d318bc9c92af9b2aa85c3294e7dff54cf1d69ad79568f56516da", size = 13656453, upload-time = "2026-09-24T13:16:21.51Z" }, ] [[package]] From e6cf9ea667ccfdec27dc229a69255f2b938d0991 Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Fri, 2 Oct 2026 11:02:27 -0400 Subject: [PATCH 13/17] fix(langgraph): stop reporting answered interrupts in get_state (#9103) Execution, snapshots, and resume checks interpreted the same pending writes separately. When one of several interrupted tasks finished after resuming, `get_state()` still reported its answered question as pending. Use `read_task_statuses()` across execution, state reads, and resume checks. An output write marks a task finished; `RESUME` alone does not, since the task may pause again. Record `NO_WRITES` when a resumed task completes without output, so it isn't run again. Latest-state reads now omit interrupts from finished tasks. History and explicit checkpoint reads still show the last recorded interrupt per task. A task paused at its second interrupt appears in `next` with `result=None`. `Command(resume=value)` without an id now raises when more than one interrupt is pending, including in a subgraph. Threads paused before this change, where a resumed task finished without output, still show that task as unfinished. It appears in `next` and its answered interrupt counts as pending, so if another interrupt is also pending, `Command(resume=value)` without an id raises. Resume these threads by interrupt id. On SQLite and Postgres, insert-ignore can leave an old `RESUME` value for a finished task; that task does not run again. Fixes #6956 Fixes #8579 Thanks to @gautamvarmadatla and @russeell for reporting these. --- libs/langgraph/langgraph/pregel/_loop.py | 46 +- libs/langgraph/langgraph/pregel/_runner.py | 6 +- .../langgraph/pregel/_task_status.py | 129 +++++ libs/langgraph/langgraph/pregel/debug.py | 51 +- libs/langgraph/langgraph/pregel/main.py | 76 +-- libs/langgraph/langgraph/types.py | 8 +- libs/langgraph/tests/test_interrupt_state.py | 534 ++++++++++++++++++ 7 files changed, 746 insertions(+), 104 deletions(-) create mode 100644 libs/langgraph/langgraph/pregel/_task_status.py create mode 100644 libs/langgraph/tests/test_interrupt_state.py diff --git a/libs/langgraph/langgraph/pregel/_loop.py b/libs/langgraph/langgraph/pregel/_loop.py index 371037768..796fb5efc 100644 --- a/libs/langgraph/langgraph/pregel/_loop.py +++ b/libs/langgraph/langgraph/pregel/_loop.py @@ -119,6 +119,7 @@ from langgraph.pregel._io import ( ) from langgraph.pregel._messages import ensure_message_ids from langgraph.pregel._read import PregelNode +from langgraph.pregel._task_status import read_task_statuses from langgraph.pregel._utils import get_new_channel_versions, is_xxh3_128_hexdigest from langgraph.pregel.debug import ( map_debug_checkpoint, @@ -736,17 +737,14 @@ class PregelLoop: def _reapply_writes_to_succeeded_nodes( self, tasks: Mapping[str, PregelExecutableTask] ) -> None: - """Restore successful channel writes from checkpoint to in-memory tasks. + """Restore the output of finished tasks from checkpoint to in-memory tasks. - Skips control signals (ERROR, ERROR_SOURCE_NODE, INTERRUPT, RESUME) - so that failed/interrupted tasks remain with empty writes and will be - re-executed (or routed to error handlers) by the runner. + Unfinished (failed or interrupted) tasks keep empty writes, so the + runner re-executes them or routes them to error handlers. """ - for tid, k, v in self.checkpoint_pending_writes: - if k in (ERROR, ERROR_SOURCE_NODE, INTERRUPT, RESUME): - continue + for tid, status in read_task_statuses(self.checkpoint_pending_writes).items(): if task := tasks.get(tid): - task.writes.append((k, v)) + task.writes.extend(status.output) def _resume_error_handlers_if_applicable(self) -> None: """On resume, schedule error handlers for tasks that failed in a prior run. @@ -816,35 +814,13 @@ class PregelLoop: self.tasks[handler_task.id] = handler_task def _pending_interrupts(self) -> set[str]: - """Return the set of interrupt ids that are pending without corresponding resume values.""" - # mapping of task ids to interrupt ids - pending_interrupts: dict[str, str] = {} - - # set of resume task ids - pending_resumes: set[str] = set() - - for task_id, write_type, value in self.checkpoint_pending_writes: - if write_type == INTERRUPT: - # interrupts is always a list, but there should only be one element - pending_interrupts[task_id] = value[0].id - elif write_type == RESUME: - pending_resumes.add(task_id) - - resumed_interrupt_ids = { - pending_interrupts[task_id] - for task_id in pending_resumes - if task_id in pending_interrupts + """Return the ids of interrupts that are still waiting for an answer.""" + return { + interrupt.id + for status in read_task_statuses(self.checkpoint_pending_writes).values() + for interrupt in status.pending_interrupts } - # Keep only interrupts whose interrupt_id is not resumed - hanging_interrupts: set[str] = { - interrupt_id - for interrupt_id in pending_interrupts.values() - if interrupt_id not in resumed_interrupt_ids - } - - return hanging_interrupts - def _first( self, *, input_keys: str | Sequence[str], updated_channels: set[str] | None ) -> set[str] | None: diff --git a/libs/langgraph/langgraph/pregel/_runner.py b/libs/langgraph/langgraph/pregel/_runner.py index 4d53b9f9d..5e283716f 100644 --- a/libs/langgraph/langgraph/pregel/_runner.py +++ b/libs/langgraph/langgraph/pregel/_runner.py @@ -45,6 +45,7 @@ from langgraph.errors import GraphBubbleUp, GraphInterrupt from langgraph.pregel._algo import Call from langgraph.pregel._executor import Submit from langgraph.pregel._retry import arun_with_retry, run_with_retry +from langgraph.pregel._task_status import CONTROL_WRITES from langgraph.types import ( CachePolicy, PregelExecutableTask, @@ -606,8 +607,9 @@ class PregelRunner: task.config is None or TAG_HIDDEN not in task.config.get("tags", []) ): self.node_finished(task.name) - if not task.writes: - # add no writes marker + if all(chan in CONTROL_WRITES for chan, _ in task.writes): + # record that the task finished, even if it produced no output + # (see `langgraph.pregel._task_status`) task.writes.append((NO_WRITES, None)) # save task writes to checkpointer self.put_writes()(task.id, task.writes) # type: ignore[misc] diff --git a/libs/langgraph/langgraph/pregel/_task_status.py b/libs/langgraph/langgraph/pregel/_task_status.py new file mode 100644 index 000000000..44b79ce8e --- /dev/null +++ b/libs/langgraph/langgraph/pregel/_task_status.py @@ -0,0 +1,129 @@ +"""Read the status of each task from the writes recorded for a superstep. + +While a superstep is open, the checkpointer keeps a log of writes for each +task in that step. Entries are added as tasks run and are only discarded when +the whole superstep finishes and a new checkpoint is saved. When a task runs +again, for example after being resumed, its earlier entries stay in the log. + +This module is the single place that turns that log into task status. Code that +needs to know whether a task finished, which interrupts it raised, which of them +are still waiting for an answer, or which output it produced must use +`read_task_statuses` instead of inspecting the writes directly. + +The log uses two kinds of writes: + +- Control writes describe what happened to a task: `INTERRUPT` (the task asked + a question), `RESUME` (answers the task has received), `ERROR`, and + `ERROR_SOURCE_NODE`. `INTERRUPT`, `RESUME` and `ERROR` each have a fixed slot + per task (`WRITES_IDX_MAP`), so a newer write of the same kind can replace an + older one. +- Every other write is output: channel writes, `RETURN` for functional tasks, + and the `NO_WRITES` marker. + +The rules are: + +1. When a task that ran finishes successfully, `PregelRunner.commit` records at + least one output write, adding `NO_WRITES` if the task produced no other + output. +2. A task that pauses at an interrupt records only control writes. +3. A task is therefore treated as finished if and only if it has an output + write. +4. Because `INTERRUPT` is stored in a fixed slot, its recorded value is the most + recent question the task asked. That question is waiting for an answer only + while the task is unfinished. + +A `RESUME` write never means a task is finished: it can hold the answer to an +earlier question while the task waits on a later one. + +What these rules cannot see: + +- A task whose result came from the cache does not go through + `PregelRunner.commit`, so nothing is recorded for it. It reads as not + finished. +- A task that fails can record partial output writes along with its error. It + reads as finished, which is how the executor has always treated it. +- Writes recorded before rule 1 existed may describe a finished task with no + output using only control writes. Those tasks read as unfinished. The + executor treated them the same way before and runs them again. Their last + interrupt now counts as pending, so they appear in `next` and a resume + without an interrupt id raises if another interrupt is also pending. +""" + +from __future__ import annotations + +from collections.abc import Iterable, Sequence +from dataclasses import dataclass +from typing import Any + +from langgraph.checkpoint.base import PendingWrite + +from langgraph._internal._constants import ( + ERROR, + ERROR_SOURCE_NODE, + INTERRUPT, + NULL_TASK_ID, + RESUME, +) +from langgraph.types import Interrupt + +__all__ = ("CONTROL_WRITES", "TaskStatus", "read_task_statuses") + +CONTROL_WRITES = frozenset((ERROR, ERROR_SOURCE_NODE, INTERRUPT, RESUME)) +"""Channels that describe what happened to a task rather than what it produced.""" + + +@dataclass(frozen=True, slots=True) +class TaskStatus: + """The status of one task, read from the writes recorded for its superstep.""" + + output: tuple[tuple[str, Any], ...] = () + """Output writes in recorded order. Empty if the task has not finished.""" + + interrupts: tuple[Interrupt, ...] = () + """The most recent interrupts the task raised, whether or not they were answered.""" + + error: BaseException | None = None + """The recorded error, if any.""" + + @property + def finished(self) -> bool: + """Whether the task ran to completion.""" + return bool(self.output) + + @property + def pending_interrupts(self) -> tuple[Interrupt, ...]: + """Interrupts waiting for an answer. Always empty for a finished task.""" + return () if self.finished else self.interrupts + + +def read_task_statuses( + pending_writes: Iterable[PendingWrite], +) -> dict[str, TaskStatus]: + """Return the status of every task that has recorded writes, keyed by task id. + + Writes from `NULL_TASK_ID` are input to the superstep, not task activity, so + they are not included. + """ + output: dict[str, list[tuple[str, Any]]] = {} + interrupts: dict[str, list[Interrupt]] = {} + errors: dict[str, BaseException] = {} + for task_id, channel, value in pending_writes: + if task_id == NULL_TASK_ID: + continue + output.setdefault(task_id, []) + if channel == INTERRUPT: + interrupts.setdefault(task_id, []).extend( + value if isinstance(value, Sequence) else [value] + ) + elif channel == ERROR: + errors.setdefault(task_id, value) + elif channel not in CONTROL_WRITES: + output[task_id].append((channel, value)) + return { + task_id: TaskStatus( + output=tuple(task_output), + interrupts=tuple(interrupts.get(task_id, ())), + error=errors.get(task_id), + ) + for task_id, task_output in output.items() + } diff --git a/libs/langgraph/langgraph/pregel/debug.py b/libs/langgraph/langgraph/pregel/debug.py index b3ef5a874..46f92eb5b 100644 --- a/libs/langgraph/langgraph/pregel/debug.py +++ b/libs/langgraph/langgraph/pregel/debug.py @@ -26,6 +26,7 @@ from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel from langgraph.constants import TAG_HIDDEN from langgraph.pregel._io import read_channels +from langgraph.pregel._task_status import TaskStatus, read_task_statuses from langgraph.types import ( CheckpointPayload, PregelExecutableTask, @@ -37,6 +38,8 @@ from langgraph.types import ( TASK_NAMESPACE = UUID("6ba7b831-9dad-11d1-80b4-00c04fd430c8") +_NOT_STARTED = TaskStatus() + def map_debug_tasks(tasks: Iterable[PregelExecutableTask]) -> Iterator[TaskPayload]: """Produce "task" events for stream_mode=debug.""" @@ -211,35 +214,21 @@ def tasks_w_writes( pending_writes: list[PendingWrite] | None, states: dict[str, RunnableConfig | StateSnapshot] | None, output_keys: str | Sequence[str], + *, + live: bool = False, ) -> tuple[PregelTask, ...]: - """Apply writes / subgraph states to tasks to be returned in a StateSnapshot.""" - pending_writes = pending_writes or [] + """Apply writes / subgraph states to tasks to be returned in a StateSnapshot. + + With `live=True`, tasks report only the interrupts still waiting for an + answer, as of the most recent writes. Otherwise tasks report the interrupts + they raised in the step, including answered ones, as a record of the step. + """ + statuses = read_task_statuses(pending_writes or []) out: list[PregelTask] = [] for task in tasks: - rtn = next( - ( - val - for tid, chan, val in pending_writes - if tid == task.id and chan == RETURN - ), - MISSING, - ) - task_error = next( - (exc for tid, n, exc in pending_writes if tid == task.id and n == ERROR), - None, - ) - task_interrupts = tuple( - v - for tid, n, vv in pending_writes - if tid == task.id and n == INTERRUPT - for v in (vv if isinstance(vv, Sequence) else [vv]) - ) - - task_writes = [ - (chan, val) - for tid, chan, val in pending_writes - if tid == task.id and chan not in (ERROR, INTERRUPT, RETURN) - ] + status = statuses.get(task.id, _NOT_STARTED) + rtn = next((val for chan, val in status.output if chan == RETURN), MISSING) + task_writes = [(chan, val) for chan, val in status.output if chan != RETURN] if rtn is not MISSING: task_result = rtn @@ -261,19 +250,15 @@ def tasks_w_writes( mapped_writes = map_task_result_writes(filtered_writes) task_result = mapped_writes if filtered_writes else {} - has_writes = rtn is not MISSING or any( - w[0] == task.id and w[1] not in (ERROR, INTERRUPT) for w in pending_writes - ) - out.append( PregelTask( task.id, task.name, task.path, - task_error, - task_interrupts, + status.error, + status.pending_interrupts if live else status.interrupts, states.get(task.id) if states else None, - task_result if has_writes else None, + task_result if status.finished else None, ) ) return tuple(out) diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index 6101e26ba..bcbfbfbb9 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -79,7 +79,6 @@ from langgraph._internal._constants import ( CONFIG_KEY_STREAM_MESSAGES_V2, CONFIG_KEY_TASK_ID, CONFIG_KEY_THREAD_ID, - ERROR, INPUT, INTERRUPT, NS_END, @@ -149,6 +148,7 @@ from langgraph.pregel._messages import ( from langgraph.pregel._read import DEFAULT_BOUND, PregelNode from langgraph.pregel._retry import RetryPolicy from langgraph.pregel._runner import PregelRunner +from langgraph.pregel._task_status import read_task_statuses from langgraph.pregel._tools import StreamToolCallHandler from langgraph.pregel._utils import ( get_new_channel_versions, @@ -1147,8 +1147,16 @@ class Pregel( config: RunnableConfig, saved: CheckpointTuple | None, recurse: BaseCheckpointSaver | None = None, - apply_pending_writes: bool = False, + live: bool = False, ) -> StateSnapshot: + """Build a `StateSnapshot` from a saved checkpoint and its pending writes. + + With `live=True` the snapshot shows current status: values include the + output of tasks that already finished, `next` lists only tasks that still + need to run, and `interrupts` lists only questions still waiting for an + answer. Otherwise the snapshot is a record of the step: values as of the + start of the step, every task in the step, and the interrupts they raised. + """ if not saved: return StateSnapshot( values={}, @@ -1236,13 +1244,10 @@ class Pregel( None, self.trigger_to_nodes, ) - if apply_pending_writes and saved.pending_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 live and saved.pending_writes: + for tid, status in read_task_statuses(saved.pending_writes).items(): + if tid in next_tasks: + next_tasks[tid].writes.extend(status.output) if tasks := [t for t in next_tasks.values() if t.writes]: apply_writes( saved.checkpoint, channels, tasks, None, self.trigger_to_nodes @@ -1252,6 +1257,7 @@ class Pregel( saved.pending_writes, task_states, self.stream_channels_asis, + live=live, ) # assemble the state snapshot return StateSnapshot( @@ -1270,8 +1276,16 @@ class Pregel( config: RunnableConfig, saved: CheckpointTuple | None, recurse: BaseCheckpointSaver | None = None, - apply_pending_writes: bool = False, + live: bool = False, ) -> StateSnapshot: + """Build a `StateSnapshot` from a saved checkpoint and its pending writes. + + With `live=True` the snapshot shows current status: values include the + output of tasks that already finished, `next` lists only tasks that still + need to run, and `interrupts` lists only questions still waiting for an + answer. Otherwise the snapshot is a record of the step: values as of the + start of the step, every task in the step, and the interrupts they raised. + """ if not saved: return StateSnapshot( values={}, @@ -1359,13 +1373,10 @@ class Pregel( None, self.trigger_to_nodes, ) - if apply_pending_writes and saved.pending_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 live and saved.pending_writes: + for tid, status in read_task_statuses(saved.pending_writes).items(): + if tid in next_tasks: + next_tasks[tid].writes.extend(status.output) if tasks := [t for t in next_tasks.values() if t.writes]: apply_writes( saved.checkpoint, channels, tasks, None, self.trigger_to_nodes @@ -1376,6 +1387,7 @@ class Pregel( saved.pending_writes, task_states, self.stream_channels_asis, + live=live, ) # assemble the state snapshot return StateSnapshot( @@ -1430,7 +1442,7 @@ class Pregel( config, saved, recurse=checkpointer if subgraphs else None, - apply_pending_writes=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], + live=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], ) async def aget_state( @@ -1474,7 +1486,7 @@ class Pregel( config, saved, recurse=checkpointer if subgraphs else None, - apply_pending_writes=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], + live=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], ) def get_state_history( @@ -1710,13 +1722,12 @@ class Pregel( checkpointer.get_next_version, self.trigger_to_nodes, ) - # apply writes from tasks that already ran - for tid, k, v in saved.pending_writes or []: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) + # apply writes from tasks that already finished + for tid, status in read_task_statuses( + saved.pending_writes or [] + ).items(): + if tid in next_tasks: + next_tasks[tid].writes.extend(status.output) # clear all current tasks apply_writes( checkpoint, @@ -2174,13 +2185,12 @@ class Pregel( checkpointer.get_next_version, self.trigger_to_nodes, ) - # apply writes from tasks that already ran - for tid, k, v in saved.pending_writes or []: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) + # apply writes from tasks that already finished + for tid, status in read_task_statuses( + saved.pending_writes or [] + ).items(): + if tid in next_tasks: + next_tasks[tid].writes.extend(status.output) # clear all current tasks apply_writes( checkpoint, diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 5e2a1a897..54510c8fc 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -726,7 +726,13 @@ class StateSnapshot(NamedTuple): tasks: tuple[PregelTask, ...] """Tasks to execute in this step. If already attempted, may contain an error.""" interrupts: tuple[Interrupt, ...] - """Interrupts that occurred in this step that are pending resolution.""" + """Interrupts that occurred in this step. + + When reading the latest state (`get_state` without a `checkpoint_id`), this + contains only interrupts still waiting for an answer. When reading a specific + checkpoint or state history, it contains the most recent interrupt each task + raised in that step, including ones answered later in the same step. + """ class Send: diff --git a/libs/langgraph/tests/test_interrupt_state.py b/libs/langgraph/tests/test_interrupt_state.py new file mode 100644 index 000000000..882d254b4 --- /dev/null +++ b/libs/langgraph/tests/test_interrupt_state.py @@ -0,0 +1,534 @@ +"""State reads while some tasks of a superstep are finished and others are paused. + +When parallel tasks each call `interrupt()` and only some of them are resumed, +the superstep stays open. Its recorded writes then contain the old interrupt of +each finished task next to that task's output. These tests check that state +reads, which are rebuilt from the checkpointer, report only the interrupts that +still need an answer. +""" + +import operator +import sys +import uuid +from collections import Counter +from typing import Annotated, Any + +import pytest +from langgraph.checkpoint.base import BaseCheckpointSaver +from typing_extensions import TypedDict + +from langgraph._internal._constants import ( + ERROR, + INTERRUPT, + NO_WRITES, + NULL_TASK_ID, + RESUME, + RETURN, +) +from langgraph.func import entrypoint, task +from langgraph.graph import END, START, StateGraph +from langgraph.pregel._task_status import read_task_statuses +from langgraph.types import Command, Durability, Interrupt, Send, interrupt + +pytestmark = pytest.mark.anyio + +NEEDS_CONTEXTVARS = pytest.mark.skipif( + sys.version_info < (3, 11), + reason="Python 3.11+ is required for async contextvars support", +) + + +class State(TypedDict, total=False): + log: Annotated[list[str], operator.add] + count: int + + +def _config() -> dict[str, Any]: + return {"configurable": {"thread_id": str(uuid.uuid4())}} + + +def _build_parallel( + checkpointer: BaseCheckpointSaver, + calls: Counter[str], + *, + a_questions: int = 1, + a_returns: Any = "log", +): + """Build a graph where nodes `a` and `b` start in parallel and both ask questions. + + `a` asks `a_questions` questions in a row. `a_returns` controls what `a` + returns after its last answer. The default `"log"` returns the answers in + `log`. Any other value is returned as-is. + """ + + def a(state: State) -> Any: + calls["a"] += 1 + answers = [interrupt(f"A{i + 1}") for i in range(a_questions)] + if a_returns == "log": + return {"log": [f"a:{answer}" for answer in answers]} + return a_returns + + def b(state: State) -> State: + calls["b"] += 1 + return {"log": [f"b:{interrupt('B')}"]} + + builder = StateGraph(State) + builder.add_node("a", a) + builder.add_node("b", b) + builder.add_edge(START, "a") + builder.add_edge(START, "b") + builder.add_edge("a", END) + builder.add_edge("b", END) + return builder.compile(checkpointer=checkpointer) + + +def _interrupt_by_value(snapshot: Any, value: str) -> Interrupt: + return next(i for i in snapshot.interrupts if i.value == value) + + +def _task(snapshot: Any, name: str) -> Any: + return next(t for t in snapshot.tasks if t.name == name) + + +def _interrupt_values(interrupts: Any) -> list[str]: + return sorted(i.value for i in interrupts) + + +# --- Task A answered and finished, task B still paused --- + + +def test_finished_task_does_not_report_answered_interrupt( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(sync_checkpointer, calls) + config = _config() + + graph.invoke({"log": []}, config, durability=durability) + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["A1", "B"] + + graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "yes"}), + config, + durability=durability, + ) + + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["B"] + assert snapshot.next == ("b",) + assert _task(snapshot, "a").interrupts == () + assert _task(snapshot, "a").result == {"log": ["a:yes"]} + assert _interrupt_values(_task(snapshot, "b").interrupts) == ["B"] + assert _task(snapshot, "b").result is None + + # Reading the same checkpoint by id gives the record of the step: every task + # in it, and every question asked, including the one A already answered. + record = graph.get_state(snapshot.config) + assert sorted(record.next) == ["a", "b"] + assert _interrupt_values(record.interrupts) == ["A1", "B"] + assert _interrupt_values(_task(record, "a").interrupts) == ["A1"] + assert _task(record, "a").result == {"log": ["a:yes"]} + + # B can still be answered, and the graph finishes normally. + result = graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "B").id: "ok"}), + config, + durability=durability, + ) + assert sorted(result["log"]) == ["a:yes", "b:ok"] + assert calls == {"a": 2, "b": 3} + snapshot = graph.get_state(config) + assert snapshot.next == () + assert snapshot.interrupts == () + + # History still shows where each question was asked. + asked = [ + _interrupt_values(s.interrupts) + for s in graph.get_state_history(config) + if s.interrupts + ] + if durability != "exit": + assert asked == [["A1", "B"]] + + +@NEEDS_CONTEXTVARS +async def test_finished_task_does_not_report_answered_interrupt_async( + async_checkpointer: BaseCheckpointSaver, +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(async_checkpointer, calls) + config = _config() + + await graph.ainvoke({"log": []}, config) + snapshot = await graph.aget_state(config) + await graph.ainvoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "yes"}), config + ) + + snapshot = await graph.aget_state(config) + assert _interrupt_values(snapshot.interrupts) == ["B"] + assert snapshot.next == ("b",) + assert _task(snapshot, "a").interrupts == () + assert _task(snapshot, "a").result == {"log": ["a:yes"]} + assert _interrupt_values(_task(snapshot, "b").interrupts) == ["B"] + + record = await graph.aget_state(snapshot.config) + assert _interrupt_values(record.interrupts) == ["A1", "B"] + assert _interrupt_values(_task(record, "a").interrupts) == ["A1"] + + result = await graph.ainvoke( + Command(resume={_interrupt_by_value(snapshot, "B").id: "ok"}), config + ) + assert sorted(result["log"]) == ["a:yes", "b:ok"] + assert calls == {"a": 2, "b": 3} + + +# --- Task A answered its first question and asked a second one --- + + +def test_task_paused_at_second_question_stays_pending( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(sync_checkpointer, calls, a_questions=2) + config = _config() + + graph.invoke({"log": []}, config) + snapshot = graph.get_state(config) + graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "one"}), config + ) + + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["A2", "B"] + # A is not finished: it has a saved answer, but no output. + assert sorted(snapshot.next) == ["a", "b"] + assert _interrupt_values(_task(snapshot, "a").interrupts) == ["A2"] + assert _task(snapshot, "a").result is None + assert _interrupt_values(_task(snapshot, "b").interrupts) == ["B"] + + # Both remaining questions can be answered together. + result = graph.invoke( + Command( + resume={ + _interrupt_by_value(snapshot, "A2").id: "two", + _interrupt_by_value(snapshot, "B").id: "ok", + } + ), + config, + ) + assert sorted(result["log"]) == ["a:one", "a:two", "b:ok"] + snapshot = graph.get_state(config) + assert snapshot.next == () + assert snapshot.interrupts == () + + +@NEEDS_CONTEXTVARS +async def test_task_paused_at_second_question_stays_pending_async( + async_checkpointer: BaseCheckpointSaver, +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(async_checkpointer, calls, a_questions=2) + config = _config() + + await graph.ainvoke({"log": []}, config) + snapshot = await graph.aget_state(config) + await graph.ainvoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "one"}), config + ) + + snapshot = await graph.aget_state(config) + assert _interrupt_values(snapshot.interrupts) == ["A2", "B"] + assert sorted(snapshot.next) == ["a", "b"] + assert _interrupt_values(_task(snapshot, "a").interrupts) == ["A2"] + assert _task(snapshot, "a").result is None + + +def test_task_paused_at_second_question_then_other_task_finishes( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(sync_checkpointer, calls, a_questions=2) + config = _config() + + graph.invoke({"log": []}, config) + snapshot = graph.get_state(config) + graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "one"}), config + ) + snapshot = graph.get_state(config) + graph.invoke(Command(resume={_interrupt_by_value(snapshot, "B").id: "ok"}), config) + + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["A2"] + assert snapshot.next == ("a",) + assert _task(snapshot, "b").interrupts == () + assert _task(snapshot, "b").result == {"log": ["b:ok"]} + + result = graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "A2").id: "two"}), config + ) + assert sorted(result["log"]) == ["a:one", "a:two", "b:ok"] + + +def test_resume_without_id_rejected_when_second_question_and_other_task_pending( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(sync_checkpointer, calls, a_questions=2) + config = _config() + + graph.invoke({"log": []}, config) + snapshot = graph.get_state(config) + graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "one"}), config + ) + + # A2 and B are both waiting, so a resume value without an id is ambiguous. + with pytest.raises(RuntimeError, match="multiple pending interrupts"): + graph.invoke(Command(resume="ambiguous"), config) + + +def test_resume_without_id_rejected_when_subgraph_has_parallel_interrupts( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + # A subgraph node whose child graph pauses in two parallel nodes records + # both interrupts under one parent task. Both count as pending, so a resume + # value without an id is ambiguous. (Before, only the first was counted and + # the value went to whichever interrupt consumed it first.) + child_builder = StateGraph(State) + child_builder.add_node("a", lambda s: {"log": [f"a:{interrupt('A')}"]}) + child_builder.add_node("b", lambda s: {"log": [f"b:{interrupt('B')}"]}) + child_builder.add_edge(START, "a") + child_builder.add_edge(START, "b") + + builder = StateGraph(State) + builder.add_node("child", child_builder.compile()) + builder.add_edge(START, "child") + graph = builder.compile(checkpointer=sync_checkpointer) + config = _config() + + graph.invoke({"log": []}, config) + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["A", "B"] + + with pytest.raises(RuntimeError, match="multiple pending interrupts"): + graph.invoke(Command(resume="ambiguous"), config) + + result = graph.invoke( + Command( + resume={ + _interrupt_by_value(snapshot, "A").id: "x", + _interrupt_by_value(snapshot, "B").id: "y", + } + ), + config, + ) + assert sorted(result["log"]) == ["a:x", "b:y"] + + +# --- Task A finished with an empty or falsy result --- + + +@pytest.mark.parametrize( + "a_returns", + [None, {}, {"count": 0}, {"log": []}], + ids=["none", "empty_dict", "zero", "empty_list"], +) +def test_task_finished_with_falsy_result( + sync_checkpointer: BaseCheckpointSaver, a_returns: Any +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(sync_checkpointer, calls, a_returns=a_returns) + config = _config() + + graph.invoke({"log": []}, config) + snapshot = graph.get_state(config) + graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "yes"}), config + ) + + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["B"] + assert snapshot.next == ("b",) + assert _task(snapshot, "a").interrupts == () + + graph.invoke(Command(resume={_interrupt_by_value(snapshot, "B").id: "ok"}), config) + # A already finished, so resuming B must not run A again. + assert calls == {"a": 2, "b": 3} + snapshot = graph.get_state(config) + assert snapshot.next == () + assert snapshot.interrupts == () + + +@pytest.mark.parametrize("a_returns", [None, {"count": 0}], ids=["none", "zero"]) +@NEEDS_CONTEXTVARS +async def test_task_finished_with_falsy_result_async( + async_checkpointer: BaseCheckpointSaver, a_returns: Any +) -> None: + calls: Counter[str] = Counter() + graph = _build_parallel(async_checkpointer, calls, a_returns=a_returns) + config = _config() + + await graph.ainvoke({"log": []}, config) + snapshot = await graph.aget_state(config) + await graph.ainvoke( + Command(resume={_interrupt_by_value(snapshot, "A1").id: "yes"}), config + ) + + snapshot = await graph.aget_state(config) + assert _interrupt_values(snapshot.interrupts) == ["B"] + assert snapshot.next == ("b",) + assert _task(snapshot, "a").interrupts == () + + await graph.ainvoke( + Command(resume={_interrupt_by_value(snapshot, "B").id: "ok"}), config + ) + assert calls == {"a": 2, "b": 3} + + +# --- Subgraphs and the functional API --- + + +def test_parallel_subgraphs_report_only_pending_interrupts( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + class ChildState(TypedDict): + prompt: str + answers: Annotated[list[str], operator.add] + + def ask(state: ChildState) -> dict[str, Any]: + return {"answers": [interrupt(state["prompt"])]} + + child_builder = StateGraph(ChildState) + child_builder.add_node("ask", ask) + child_builder.add_edge(START, "ask") + child = child_builder.compile() + + class ParentState(TypedDict): + answers: Annotated[list[str], operator.add] + + builder = StateGraph(ParentState) + builder.add_node("child", child) + builder.add_conditional_edges( + START, + lambda _: [Send("child", {"prompt": p, "answers": []}) for p in ("a", "b")], + ["child"], + ) + graph = builder.compile(checkpointer=sync_checkpointer) + config = _config() + + graph.invoke({"answers": []}, config) + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["a", "b"] + graph.invoke(Command(resume={_interrupt_by_value(snapshot, "a").id: "x"}), config) + + snapshot = graph.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["b"] + assert snapshot.next == ("child",) + finished = next(t for t in snapshot.tasks if t.result is not None) + assert finished.interrupts == () + assert finished.result == {"answers": ["x"]} + + result = graph.invoke( + Command(resume={_interrupt_by_value(snapshot, "b").id: "y"}), config + ) + assert sorted(result["answers"]) == ["x", "y"] + + +def test_functional_task_finished_with_none_is_not_rerun( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + calls: Counter[str] = Counter() + + @task + def ask_a() -> None: + calls["a"] += 1 + interrupt("A") + + @task + def ask_b() -> str: + calls["b"] += 1 + return interrupt("B") + + @entrypoint(checkpointer=sync_checkpointer) + def workflow(_: Any) -> list[Any]: + a, b = ask_a(), ask_b() + return [a.result(), b.result()] + + config = _config() + workflow.invoke(1, config) + snapshot = workflow.get_state(config) + workflow.invoke( + Command(resume={_interrupt_by_value(snapshot, "A").id: "x"}), config + ) + + snapshot = workflow.get_state(config) + assert _interrupt_values(snapshot.interrupts) == ["B"] + + result = workflow.invoke( + Command(resume={_interrupt_by_value(snapshot, "B").id: "y"}), config + ) + assert result == [None, "y"] + assert calls == {"a": 2, "b": 3} + + +# --- Reading task status from recorded writes --- + + +def test_read_task_statuses() -> None: + a1 = Interrupt(value="A1", id="a") + a2 = Interrupt(value="A2", id="a") + b = Interrupt(value="B", id="b") + error = ValueError("boom") + + statuses = read_task_statuses( + [ + # answered and finished: old interrupt stays recorded + ("finished", INTERRUPT, (a1,)), + ("finished", RESUME, ["yes"]), + ("finished", "log", ["a:yes"]), + # answered once, then paused at a second question + ("paused", INTERRUPT, (a2,)), + ("paused", RESUME, ["one"]), + # finished with no output + ("no_output", INTERRUPT, (b,)), + ("no_output", RESUME, ["ok"]), + ("no_output", NO_WRITES, None), + # functional task that returned None + ("returned_none", RETURN, None), + # failed + ("failed", ERROR, error), + # not a task + (NULL_TASK_ID, RESUME, "global"), + ] + ) + + assert set(statuses) == { + "finished", + "paused", + "no_output", + "returned_none", + "failed", + } + + assert statuses["finished"].finished + assert statuses["finished"].interrupts == (a1,) + assert statuses["finished"].pending_interrupts == () + assert statuses["finished"].output == (("log", ["a:yes"]),) + + assert not statuses["paused"].finished + assert statuses["paused"].interrupts == (a2,) + assert statuses["paused"].pending_interrupts == (a2,) + assert statuses["paused"].output == () + + assert statuses["no_output"].finished + assert statuses["no_output"].interrupts == (b,) + assert statuses["no_output"].pending_interrupts == () + + assert statuses["returned_none"].finished + assert statuses["returned_none"].output == ((RETURN, None),) + + assert not statuses["failed"].finished + assert statuses["failed"].error is error From 3af263175f3170662440a16517f828e4f4d426f3 Mon Sep 17 00:00:00 2001 From: Elior Nataf Lackritz Date: Fri, 2 Oct 2026 11:26:54 -0400 Subject: [PATCH 14/17] fix(langgraph): hydrate subgraph delta channels with the caller-resolved saver (#8538) Fixes langchain-ai/langgraph#8470 Fixes langchain-ai/langgraph#8653 Reported by @gururafiki, with a self-contained `InMemorySaver` repro and the observation that non-delta channels in the same namespace hydrate fine, which is what makes the failure silent. @doniyor2109 reported #8653, the same missing saver at the root: a graph compiled without a checkpointer, whose saver arrives through the config, read the channel back empty in `get_state`, `get_state_history` and `update_state`. ### The bug, in one read One subgraph state, two channels written by the same nodes: `delta` is a `DeltaChannel`, `plain` is an `operator.add` list. Pause the subgraph before its second node and read it through the documented `get_state(config, subgraphs=True).tasks[0].state`: ``` main: {'delta': [], 'plain': ['a1']} branch: {'delta': ['a1'], 'plain': ['a1']} ``` Same call, same instant, same state object. If this were a rule about how subgraphs manage state, both channels would be empty. Only the delta one is, because it is the one channel type that stores nothing in `channel_values` and has to replay its writes through a saver. ### Why only the delta channel A subgraph has no saver of its own. It either inherits the parent's (the default, "per-invocation") or opts in with `checkpointer=True` ("per-thread"), and in both cases the saver reaches it through `CONFIG_KEY_CHECKPOINTER`. All four public readers resolve it from there. `_prepare_state_snapshot` then ignored that and re-derived the saver from `self.checkpointer`: - per-invocation: `self.checkpointer` is `None` - per-thread: `self.checkpointer` is `True`, and `isinstance(True, BaseCheckpointSaver)` is `False` So the explicit per-thread opt-in, the mode that exists to make subgraph state readable across invocations, was broken too. With no saver the ancestor walk never runs and the channel falls through to `from_checkpoint(MISSING)`: an empty value, indistinguishable from never written, raising nothing. The saver can't be recovered inside the function: `get_state_history` passes the checkpointer's stored config, which never carries a live saver. The caller is the only place it exists, so the caller passes it, as a required keyword argument. ### What stays unchanged The two modes from the [subgraph persistence docs](https://docs.langchain.com/oss/python/langgraph/use-subgraphs#subgraph-persistence) where an empty read is correct are pinned by tests that pass before and after: - `checkpointer=False` persists nothing, so there is no subgraph namespace to read. - after a subgraph completes, `subgraphs=True` has no pending task to attach state to, for every channel type alike. ### The write path loses data on disk `bulk_update_state` / `abulk_update_state` carried the same expression. When an update reaches the channel's snapshot cadence, `create_checkpoint` persists a `_DeltaSnapshot` built from the empty hydration, so `update_state` on a subgraph namespace replaces `['a1', 'b1', 'b2']` with `['manual']` permanently. At the default `snapshot_frequency=1000` no snapshot is written and the loss stays latent; the tests use `snapshot_frequency=2` to reach it. `prepare_next_tasks` in `_prepare_state_snapshot` now receives the same saver too. It was inert there (the task config falls back to `CONFIG_KEY_CHECKPOINTER`, and `PregelTask` exposes no config), but one expression is easier to trust than two. ### One saver lookup and namespace rule The run loop and all six state methods now share one saver lookup (`_resolve_checkpointer`) and one namespace rule for `checkpointer=True` subgraphs, and the state methods find the subgraph a namespaced config addresses through one helper (`_subgraph_for_namespace`). Before, only `get_state` / `aget_state` stripped the task id, so `get_state_history` and `update_state` given the subgraph task's config looked under `name:`, where the run never stores anything: the history came back empty and the update was silently lost. The namespace rule strips only the task ids: a task that calls the same `checkpointer=True` subgraph twice stores the second call under `|1`, and `recast_checkpoint_ns` would drop that counter. Sharing the lookup with runs also fixes two places where a state method disagreed with a run: a `checkpointer=False` graph ignores a saver lent through the config, so `update_state` raises "No checkpointer set" instead of writing a checkpoint no run reads, and a `checkpointer=True` graph used as root raises the run's `checkpointer=True cannot be used for root graphs.` instead of an AttributeError. `channels_from_checkpoint` now raises when a `DeltaChannel` has a version but no stored value and no saver or config was passed, so a caller that forgets either fails instead of reading `[]`. Keying on the version keeps a never-written channel reading as empty. ### Verification `tests/test_delta_channel_subgraph.py` runs against the shared `sync_checkpointer` / `async_checkpointer` fixtures (memory, sqlite, postgres), and every assertion compares both channels: | surface | on main | | -- | -- | | `get_state` / `aget_state` on a subgraph ns | `delta: []` | | `get_state_history` / `aget_state_history` | every snapshot `delta: []` | | two levels of nesting | `delta: []` | | `get_state(subgraphs=True)` / async, interrupted, per-invocation and per-thread | `delta: []` | | `update_state` / `aupdate_state` | `delta: ['manual']`, prior history lost | | `get_state_history` / `update_state` from a `checkpointer=True` subgraph's task config, sync and async | no snapshots / update lost | | `channels_from_checkpoint` with a written delta channel and no saver or config | silent `[]` | | `get_state` / `update_state` / `get_state_history` on a graph compiled without a checkpointer, saver lent through the config (#8653), sync and async | `delta: []` | | `get_state` of a `checkpointer=True` subgraph's second call in one task (`node\|1`), sync and async | the first call's state | 159 cases: 144 fail on main, all 159 pass here. The 15 that pass on main are the two unchanged-mode controls and the never-written case. --------- Co-authored-by: gururafiki <22777967+gururafiki@users.noreply.github.com> Co-authored-by: Yuan Gao <119447586+DavidGao520@users.noreply.github.com> --- .../langgraph/langgraph/pregel/_checkpoint.py | 16 + libs/langgraph/langgraph/pregel/main.py | 319 +++++------ .../tests/test_delta_channel_subgraph.py | 496 ++++++++++++++++++ 3 files changed, 640 insertions(+), 191 deletions(-) create mode 100644 libs/langgraph/tests/test_delta_channel_subgraph.py diff --git a/libs/langgraph/langgraph/pregel/_checkpoint.py b/libs/langgraph/langgraph/pregel/_checkpoint.py index c336f75a6..69ca88708 100644 --- a/libs/langgraph/langgraph/pregel/_checkpoint.py +++ b/libs/langgraph/langgraph/pregel/_checkpoint.py @@ -226,6 +226,20 @@ def _needs_replay(spec: BaseChannel, stored: object) -> bool: return stored is MISSING +def _require_saver_for_history( + checkpoint: Checkpoint, + delta_channels: list[str], + saver: BaseCheckpointSaver | None, + config: RunnableConfig | None, +) -> None: + written = [k for k in delta_channels if k in checkpoint["channel_versions"]] + if written and (saver is None or config is None): + raise ValueError( + f"DeltaChannel {written} has history to replay but no checkpointer " + "or config was passed to read it" + ) + + def channels_from_checkpoint( specs: Mapping[str, BaseChannel | ManagedValueSpec], checkpoint: Checkpoint, @@ -256,6 +270,7 @@ def channels_from_checkpoint( for k, spec in channel_specs.items() if _needs_replay(spec, checkpoint["channel_values"].get(k, MISSING)) ] + _require_saver_for_history(checkpoint, delta_channels, saver, config) histories: Mapping[str, Any] = {} if delta_channels and saver is not None and config is not None: histories = saver.get_delta_channel_history( @@ -298,6 +313,7 @@ async def achannels_from_checkpoint( for k, spec in channel_specs.items() if _needs_replay(spec, checkpoint["channel_values"].get(k, MISSING)) ] + _require_saver_for_history(checkpoint, delta_channels, saver, config) histories: Mapping[str, Any] = {} if delta_channels and saver is not None and config is not None: histories = await saver.aget_delta_channel_history( diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index bcbfbfbb9..c32f8cb37 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -835,6 +835,64 @@ class Pregel( if auto_validate: self.validate() + def _resolve_checkpointer( + self, config: RunnableConfig + ) -> BaseCheckpointSaver | None: + """The saver runs and state methods use: none for `checkpointer=False`, + else the one a parent lends a subgraph through the config, else this + graph's own.""" + if self.checkpointer is False: + return None + conf = config.get(CONF, {}) + if CONFIG_KEY_CHECKPOINTER in conf: + checkpointer = conf[CONFIG_KEY_CHECKPOINTER] + elif self.checkpointer is True: + raise RuntimeError("checkpointer=True cannot be used for root graphs.") + else: + checkpointer = self.checkpointer + if isinstance(checkpointer, BaseCheckpointSaver): + checkpointer = self._apply_checkpointer_allowlist(checkpointer) + return checkpointer + + def _state_checkpointer(self, config: RunnableConfig) -> BaseCheckpointSaver: + checkpointer = self._resolve_checkpointer(ensure_config(config)) + if not isinstance(checkpointer, BaseCheckpointSaver): + raise ValueError("No checkpointer set") + return checkpointer + + def _own_checkpoint_config(self, config: RunnableConfig) -> RunnableConfig: + """A `checkpointer=True` subgraph keeps one history per thread, stored + under its namespace with the task ids removed.""" + if self.checkpointer is not True: + return config + ns = config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + # Unlike `recast_checkpoint_ns`, keep the numeric parts: a task that + # calls the same subgraph again stores that call's history under one. + return patch_configurable( + config, + { + CONFIG_KEY_CHECKPOINT_NS: NS_SEP.join( + part.split(NS_END)[0] for part in ns.split(NS_SEP) + ) + }, + ) + + def _subgraph_for_namespace( + self, config: RunnableConfig, checkpointer: BaseCheckpointSaver + ) -> tuple[PregelProtocol, RunnableConfig] | None: + """The subgraph a state method's namespaced config addresses, and the + config to call it with, lending it `checkpointer`. `None` when the + config is for this graph.""" + checkpoint_ns = config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + if not checkpoint_ns or CONFIG_KEY_CHECKPOINTER in config[CONF]: + return None + recast = recast_checkpoint_ns(checkpoint_ns) + for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): + return pregel, patch_configurable( + config, {CONFIG_KEY_CHECKPOINTER: checkpointer} + ) + raise ValueError(f"Subgraph {recast} not found") + def _apply_checkpointer_allowlist( self, checkpointer: BaseCheckpointSaver | None ) -> BaseCheckpointSaver | None: @@ -1146,7 +1204,9 @@ class Pregel( self, config: RunnableConfig, saved: CheckpointTuple | None, - recurse: BaseCheckpointSaver | None = None, + *, + saver: BaseCheckpointSaver, + recurse: bool = False, live: bool = False, ) -> StateSnapshot: """Build a `StateSnapshot` from a saved checkpoint and its pending writes. @@ -1177,9 +1237,7 @@ class Pregel( channels, managed = channels_from_checkpoint( self.channels, saved.checkpoint, - saver=self.checkpointer - if isinstance(self.checkpointer, BaseCheckpointSaver) - else None, + saver=saver, config=saved.config, ) # tasks for this checkpoint @@ -1194,11 +1252,7 @@ class Pregel( stop, for_execution=True, store=self.store, - checkpointer=( - self.checkpointer - if isinstance(self.checkpointer, BaseCheckpointSaver) - else None - ), + checkpointer=saver, manager=None, ) # get the subgraphs @@ -1225,7 +1279,7 @@ class Pregel( # get the state of the subgraph config = { CONF: { - CONFIG_KEY_CHECKPOINTER: recurse, + CONFIG_KEY_CHECKPOINTER: saver, "thread_id": saved.config[CONF]["thread_id"], CONFIG_KEY_CHECKPOINT_NS: task_ns, } @@ -1275,7 +1329,9 @@ class Pregel( self, config: RunnableConfig, saved: CheckpointTuple | None, - recurse: BaseCheckpointSaver | None = None, + *, + saver: BaseCheckpointSaver, + recurse: bool = False, live: bool = False, ) -> StateSnapshot: """Build a `StateSnapshot` from a saved checkpoint and its pending writes. @@ -1306,9 +1362,7 @@ class Pregel( channels, managed = await achannels_from_checkpoint( self.channels, saved.checkpoint, - saver=self.checkpointer - if isinstance(self.checkpointer, BaseCheckpointSaver) - else None, + saver=saver, config=saved.config, ) # tasks for this checkpoint @@ -1323,11 +1377,7 @@ class Pregel( stop, for_execution=True, store=self.store, - checkpointer=( - self.checkpointer - if isinstance(self.checkpointer, BaseCheckpointSaver) - else None - ), + checkpointer=saver, manager=None, ) # get the subgraphs @@ -1354,7 +1404,7 @@ class Pregel( # get the state of the subgraph config = { CONF: { - CONFIG_KEY_CHECKPOINTER: recurse, + CONFIG_KEY_CHECKPOINTER: saver, "thread_id": saved.config[CONF]["thread_id"], CONFIG_KEY_CHECKPOINT_NS: task_ns, } @@ -1405,34 +1455,14 @@ class Pregel( self, config: RunnableConfig, *, subgraphs: bool = False ) -> StateSnapshot: """Get the current state of the graph.""" - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if isinstance(checkpointer, BaseCheckpointSaver): - checkpointer = self._apply_checkpointer_allowlist(checkpointer) - if not checkpointer: - raise ValueError("No checkpointer set") + checkpointer = self._state_checkpointer(config) - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): - return pregel.get_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - subgraphs=subgraphs, - ) - else: - raise ValueError(f"Subgraph {recast} not found") + if subgraph := self._subgraph_for_namespace(config, checkpointer): + pregel, subgraph_config = subgraph + return pregel.get_state(subgraph_config, subgraphs=subgraphs) config = merge_configs(self.config, config) if self.config else config - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config = merge_configs( - config, {CONF: {CONFIG_KEY_CHECKPOINT_NS: recast_checkpoint_ns(ns)}} - ) + config = self._own_checkpoint_config(config) thread_id = config[CONF][CONFIG_KEY_THREAD_ID] if not isinstance(thread_id, str): config[CONF][CONFIG_KEY_THREAD_ID] = str(thread_id) @@ -1441,7 +1471,8 @@ class Pregel( return self._prepare_state_snapshot( config, saved, - recurse=checkpointer if subgraphs else None, + saver=checkpointer, + recurse=subgraphs, live=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], ) @@ -1449,34 +1480,14 @@ class Pregel( self, config: RunnableConfig, *, subgraphs: bool = False ) -> StateSnapshot: """Get the current state of the graph.""" - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if isinstance(checkpointer, BaseCheckpointSaver): - checkpointer = self._apply_checkpointer_allowlist(checkpointer) - if not checkpointer: - raise ValueError("No checkpointer set") + checkpointer = self._state_checkpointer(config) - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): - return await pregel.aget_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - subgraphs=subgraphs, - ) - else: - raise ValueError(f"Subgraph {recast} not found") + if subgraph := self._subgraph_for_namespace(config, checkpointer): + pregel, subgraph_config = subgraph + return await pregel.aget_state(subgraph_config, subgraphs=subgraphs) config = merge_configs(self.config, config) if self.config else config - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config = merge_configs( - config, {CONF: {CONFIG_KEY_CHECKPOINT_NS: recast_checkpoint_ns(ns)}} - ) + config = self._own_checkpoint_config(config) thread_id = config[CONF][CONFIG_KEY_THREAD_ID] if not isinstance(thread_id, str): config[CONF][CONFIG_KEY_THREAD_ID] = str(thread_id) @@ -1485,7 +1496,8 @@ class Pregel( return await self._aprepare_state_snapshot( config, saved, - recurse=checkpointer if subgraphs else None, + saver=checkpointer, + recurse=subgraphs, live=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], ) @@ -1499,47 +1511,34 @@ class Pregel( ) -> Iterator[StateSnapshot]: """Get the history of the state of the graph.""" config = ensure_config(config) - checkpointer: BaseCheckpointSaver | None = config[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if isinstance(checkpointer, BaseCheckpointSaver): - checkpointer = self._apply_checkpointer_allowlist(checkpointer) - if not checkpointer: - raise ValueError("No checkpointer set") + checkpointer = self._state_checkpointer(config) - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): - yield from pregel.get_state_history( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - filter=filter, - before=before, - limit=limit, - ) - return - else: - raise ValueError(f"Subgraph {recast} not found") + if subgraph := self._subgraph_for_namespace(config, checkpointer): + pregel, subgraph_config = subgraph + yield from pregel.get_state_history( + subgraph_config, filter=filter, before=before, limit=limit + ) + return config = merge_configs( self.config, config, { CONF: { - CONFIG_KEY_CHECKPOINT_NS: checkpoint_ns, + CONFIG_KEY_CHECKPOINT_NS: config[CONF].get( + CONFIG_KEY_CHECKPOINT_NS, "" + ), CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID]), } }, ) + config = self._own_checkpoint_config(config) # eagerly consume list() to avoid holding up the db cursor for checkpoint_tuple in list( checkpointer.list(config, before=before, limit=limit, filter=filter) ): yield self._prepare_state_snapshot( - checkpoint_tuple.config, checkpoint_tuple + checkpoint_tuple.config, checkpoint_tuple, saver=checkpointer ) async def aget_state_history( @@ -1552,42 +1551,29 @@ class Pregel( ) -> AsyncIterator[StateSnapshot]: """Asynchronously get the history of the state of the graph.""" config = ensure_config(config) - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if isinstance(checkpointer, BaseCheckpointSaver): - checkpointer = self._apply_checkpointer_allowlist(checkpointer) - if not checkpointer: - raise ValueError("No checkpointer set") + checkpointer = self._state_checkpointer(config) - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): - async for state in pregel.aget_state_history( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - filter=filter, - before=before, - limit=limit, - ): - yield state - return - else: - raise ValueError(f"Subgraph {recast} not found") + if subgraph := self._subgraph_for_namespace(config, checkpointer): + pregel, subgraph_config = subgraph + async for state in pregel.aget_state_history( + subgraph_config, filter=filter, before=before, limit=limit + ): + yield state + return config = merge_configs( self.config, config, { CONF: { - CONFIG_KEY_CHECKPOINT_NS: checkpoint_ns, + CONFIG_KEY_CHECKPOINT_NS: config[CONF].get( + CONFIG_KEY_CHECKPOINT_NS, "" + ), CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID]), } }, ) + config = self._own_checkpoint_config(config) # eagerly consume list() to avoid holding up the db cursor for checkpoint_tuple in [ c @@ -1596,7 +1582,7 @@ class Pregel( ) ]: yield await self._aprepare_state_snapshot( - checkpoint_tuple.config, checkpoint_tuple + checkpoint_tuple.config, checkpoint_tuple, saver=checkpointer ) def bulk_update_state( @@ -1620,13 +1606,7 @@ class Pregel( RunnableConfig: The updated config. """ - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if isinstance(checkpointer, BaseCheckpointSaver): - checkpointer = self._apply_checkpointer_allowlist(checkpointer) - if not checkpointer: - raise ValueError("No checkpointer set") + checkpointer = self._state_checkpointer(config) if len(supersteps) == 0: raise ValueError("No supersteps provided") @@ -1634,26 +1614,17 @@ class Pregel( if any(len(u) == 0 for u in supersteps): raise ValueError("No updates provided") - # delegate to subgraph - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): - return pregel.bulk_update_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - supersteps, - ) - else: - raise ValueError(f"Subgraph {recast} not found") + if subgraph := self._subgraph_for_namespace(config, checkpointer): + pregel, subgraph_config = subgraph + return pregel.bulk_update_state(subgraph_config, supersteps) def perform_superstep( input_config: RunnableConfig, updates: Sequence[StateUpdate] ) -> RunnableConfig: # get last checkpoint - config = ensure_config(self.config, input_config) + config = self._own_checkpoint_config( + ensure_config(self.config, input_config) + ) saved = checkpointer.get_tuple(config) if saved is not None: self._migrate_checkpoint(saved.checkpoint) @@ -1678,10 +1649,7 @@ class Pregel( channels, managed = channels_from_checkpoint( self.channels, checkpoint, - saver=self.checkpointer - if saved is not None - and isinstance(self.checkpointer, BaseCheckpointSaver) - else None, + saver=checkpointer, config=saved.config if saved is not None else None, ) values, as_node = updates[0][:2] @@ -2085,13 +2053,7 @@ class Pregel( RunnableConfig: The updated config. """ - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if isinstance(checkpointer, BaseCheckpointSaver): - checkpointer = self._apply_checkpointer_allowlist(checkpointer) - if not checkpointer: - raise ValueError("No checkpointer set") + checkpointer = self._state_checkpointer(config) if len(supersteps) == 0: raise ValueError("No supersteps provided") @@ -2099,26 +2061,17 @@ class Pregel( if any(len(u) == 0 for u in supersteps): raise ValueError("No updates provided") - # delegate to subgraph - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): - return await pregel.abulk_update_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - supersteps, - ) - else: - raise ValueError(f"Subgraph {recast} not found") + if subgraph := self._subgraph_for_namespace(config, checkpointer): + pregel, subgraph_config = subgraph + return await pregel.abulk_update_state(subgraph_config, supersteps) async def aperform_superstep( input_config: RunnableConfig, updates: Sequence[StateUpdate] ) -> RunnableConfig: # get last checkpoint - config = ensure_config(self.config, input_config) + config = self._own_checkpoint_config( + ensure_config(self.config, input_config) + ) saved = await checkpointer.aget_tuple(config) if saved is not None: self._migrate_checkpoint(saved.checkpoint) @@ -2143,10 +2096,7 @@ class Pregel( channels, managed = await achannels_from_checkpoint( self.channels, checkpoint, - saver=self.checkpointer - if saved is not None - and isinstance(self.checkpointer, BaseCheckpointSaver) - else None, + saver=checkpointer, config=saved.config if saved is not None else None, ) values, as_node = updates[0][:2] @@ -2584,16 +2534,7 @@ class Pregel( stream_modes.add(print_mode) else: stream_modes.update(print_mode) - if self.checkpointer is False: - checkpointer: BaseCheckpointSaver | None = None - elif CONFIG_KEY_CHECKPOINTER in config.get(CONF, {}): - checkpointer = config[CONF][CONFIG_KEY_CHECKPOINTER] - elif self.checkpointer is True: - raise RuntimeError("checkpointer=True cannot be used for root graphs.") - else: - checkpointer = self.checkpointer - if isinstance(checkpointer, BaseCheckpointSaver): - checkpointer = self._apply_checkpointer_allowlist(checkpointer) + checkpointer = self._resolve_checkpointer(config) if checkpointer and not config.get(CONF): raise ValueError( "Checkpointer requires one or more of the following 'configurable' " @@ -2812,9 +2753,7 @@ class Pregel( "`durability` has no effect when no checkpointer is present.", ) # set up subgraph checkpointing - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config[CONF][CONFIG_KEY_CHECKPOINT_NS] = recast_checkpoint_ns(ns) + config = self._own_checkpoint_config(config) # set up messages stream mode if "messages" in stream_modes: ns_ = cast(str | None, config[CONF].get(CONFIG_KEY_CHECKPOINT_NS)) @@ -3239,9 +3178,7 @@ class Pregel( "`durability` has no effect when no checkpointer is present.", ) # set up subgraph checkpointing - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config[CONF][CONFIG_KEY_CHECKPOINT_NS] = recast_checkpoint_ns(ns) + config = self._own_checkpoint_config(config) # set up messages stream mode if "messages" in stream_modes: # namespace can be None in a root level graph? diff --git a/libs/langgraph/tests/test_delta_channel_subgraph.py b/libs/langgraph/tests/test_delta_channel_subgraph.py new file mode 100644 index 000000000..097e4355d --- /dev/null +++ b/libs/langgraph/tests/test_delta_channel_subgraph.py @@ -0,0 +1,496 @@ +import operator +from typing import Annotated, Any, Literal + +import pytest +from langchain_core.runnables import RunnableConfig +from langgraph.checkpoint.base import BaseCheckpointSaver +from langgraph.checkpoint.memory import InMemorySaver +from typing_extensions import TypedDict + +from langgraph._internal._constants import CONFIG_KEY_CHECKPOINTER +from langgraph.channels.delta import DeltaChannel +from langgraph.graph import END, START, StateGraph +from langgraph.pregel._checkpoint import ( + achannels_from_checkpoint, + channels_from_checkpoint, + empty_checkpoint, +) + +pytestmark = pytest.mark.anyio + + +def _extend(state: list | None, writes: list[Any]) -> list: + out = list(state or []) + for write in writes: + out.extend(write if isinstance(write, list) else [write]) + return out + + +def _state_schema(snapshot_frequency: int = 1000) -> type: + class State(TypedDict, total=False): + delta: Annotated[ + list, DeltaChannel(_extend, snapshot_frequency=snapshot_frequency) + ] + plain: Annotated[list, operator.add] + + return State + + +def _both(*items: str) -> dict: + return {"delta": list(items), "plain": list(items)} + + +def _child_builder(*, snapshot_frequency: int = 1000) -> StateGraph: + builder = StateGraph(_state_schema(snapshot_frequency)) + builder.add_node("a", lambda state: _both("a1")) + builder.add_node("b", lambda state: _both("b1", "b2")) + builder.add_edge(START, "a") + builder.add_edge("a", "b") + builder.add_edge("b", END) + return builder + + +def _wrap( + inner: StateGraph, + *, + checkpointer: bool | None = None, + interrupt_before: list[str] | None = None, +) -> StateGraph: + builder = StateGraph(inner.state_schema) + builder.add_node( + "child", + inner.compile(checkpointer=checkpointer, interrupt_before=interrupt_before), + ) + builder.add_edge(START, "child") + builder.add_edge("child", END) + return builder + + +def _nested_app( + checkpointer: BaseCheckpointSaver, + *, + depth: int = 1, + snapshot_frequency: int = 1000, + pause_before_b: bool = False, + subgraph_checkpointer: bool | None = None, +) -> Any: + graph = _child_builder(snapshot_frequency=snapshot_frequency) + for _ in range(depth): + graph = _wrap( + graph, + checkpointer=subgraph_checkpointer, + interrupt_before=["b"] if pause_before_b else None, + ) + return graph.compile(checkpointer=checkpointer) + + +def _scoped(config: dict, namespace: str) -> dict: + return {"configurable": {**config["configurable"], "checkpoint_ns": namespace}} + + +def _child_namespace(app: Any, config: dict, *, depth: int = 1) -> str: + namespace = "" + for level in range(depth): + scoped = _scoped(config, namespace) if namespace else config + namespace = next( + ( + task.state["configurable"]["checkpoint_ns"] + for snapshot in app.get_state_history(scoped) + for task in snapshot.tasks + if task.name == "child" and isinstance(task.state, dict) + ), + "", + ) + assert namespace, f"no `child` subgraph task at nesting level {level}" + return namespace + + +async def _achild_namespace(app: Any, config: dict) -> str: + async for snapshot in app.aget_state_history(config): + for task in snapshot.tasks: + if task.name == "child" and isinstance(task.state, dict): + return task.state["configurable"]["checkpoint_ns"] + raise AssertionError("no `child` subgraph task") + + +HISTORY = [_both("a1", "b1", "b2"), _both("a1"), _both(), _both()] + + +def test_subgraph_get_state(sync_checkpointer: BaseCheckpointSaver) -> None: + app = _nested_app(sync_checkpointer) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + + child = _scoped(config, _child_namespace(app, config)) + + assert app.get_state(config).values == _both("a1", "b1", "b2") + assert app.get_state(child).values == _both("a1", "b1", "b2") + + +async def test_subgraph_aget_state(async_checkpointer: BaseCheckpointSaver) -> None: + app = _nested_app(async_checkpointer) + config = {"configurable": {"thread_id": "1"}} + await app.ainvoke({}, config) + + child = _scoped(config, await _achild_namespace(app, config)) + + assert (await app.aget_state(child)).values == _both("a1", "b1", "b2") + + +def test_subgraph_get_state_history(sync_checkpointer: BaseCheckpointSaver) -> None: + app = _nested_app(sync_checkpointer) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + + child = _scoped(config, _child_namespace(app, config)) + + assert [s.values for s in app.get_state_history(child)] == HISTORY + + +async def test_subgraph_aget_state_history( + async_checkpointer: BaseCheckpointSaver, +) -> None: + app = _nested_app(async_checkpointer) + config = {"configurable": {"thread_id": "1"}} + await app.ainvoke({}, config) + + child = _scoped(config, await _achild_namespace(app, config)) + + assert [s.values async for s in app.aget_state_history(child)] == HISTORY + + +def test_doubly_nested_subgraph_get_state( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + app = _nested_app(sync_checkpointer, depth=2) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + + child = _scoped(config, _child_namespace(app, config, depth=2)) + + assert app.get_state(child).values == _both("a1", "b1", "b2") + + +@pytest.mark.parametrize("persistence", ["per-invocation", "per-thread"]) +def test_interrupted_subgraph_task_state( + sync_checkpointer: BaseCheckpointSaver, + persistence: Literal["per-invocation", "per-thread"], +) -> None: + app = _nested_app( + sync_checkpointer, + pause_before_b=True, + subgraph_checkpointer=True if persistence == "per-thread" else None, + ) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + + (task,) = app.get_state(config, subgraphs=True).tasks + + assert task.state.values == _both("a1") + + +@pytest.mark.parametrize("persistence", ["per-invocation", "per-thread"]) +async def test_interrupted_subgraph_task_state_async( + async_checkpointer: BaseCheckpointSaver, + persistence: Literal["per-invocation", "per-thread"], +) -> None: + app = _nested_app( + async_checkpointer, + pause_before_b=True, + subgraph_checkpointer=True if persistence == "per-thread" else None, + ) + config = {"configurable": {"thread_id": "1"}} + await app.ainvoke({}, config) + + (task,) = (await app.aget_state(config, subgraphs=True)).tasks + + assert task.state.values == _both("a1") + + +@pytest.mark.parametrize("persistence", ["per-invocation", "per-thread"]) +def test_interrupted_subgraph_history_from_its_task_config( + sync_checkpointer: BaseCheckpointSaver, + persistence: Literal["per-invocation", "per-thread"], +) -> None: + app = _nested_app( + sync_checkpointer, + pause_before_b=True, + subgraph_checkpointer=True if persistence == "per-thread" else None, + ) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + (task,) = app.get_state(config).tasks + + history = list(app.get_state_history(task.state)) + + assert [snapshot.values for snapshot in history][:1] == [_both("a1")] + + +@pytest.mark.parametrize("persistence", ["per-invocation", "per-thread"]) +async def test_interrupted_subgraph_ahistory_from_its_task_config( + async_checkpointer: BaseCheckpointSaver, + persistence: Literal["per-invocation", "per-thread"], +) -> None: + app = _nested_app( + async_checkpointer, + pause_before_b=True, + subgraph_checkpointer=True if persistence == "per-thread" else None, + ) + config = {"configurable": {"thread_id": "1"}} + await app.ainvoke({}, config) + (task,) = (await app.aget_state(config)).tasks + + history = [snapshot async for snapshot in app.aget_state_history(task.state)] + + assert [snapshot.values for snapshot in history][:1] == [_both("a1")] + + +@pytest.mark.parametrize("persistence", ["per-invocation", "per-thread"]) +def test_interrupted_subgraph_update_state_from_its_task_config( + sync_checkpointer: BaseCheckpointSaver, + persistence: Literal["per-invocation", "per-thread"], +) -> None: + app = _nested_app( + sync_checkpointer, + pause_before_b=True, + subgraph_checkpointer=True if persistence == "per-thread" else None, + ) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + (task,) = app.get_state(config).tasks + + app.update_state(task.state, _both("edit"), as_node="a") + + (task,) = app.get_state(config, subgraphs=True).tasks + assert task.state.values == _both("a1", "edit") + + +@pytest.mark.parametrize("persistence", ["per-invocation", "per-thread"]) +async def test_interrupted_subgraph_aupdate_state_from_its_task_config( + async_checkpointer: BaseCheckpointSaver, + persistence: Literal["per-invocation", "per-thread"], +) -> None: + app = _nested_app( + async_checkpointer, + pause_before_b=True, + subgraph_checkpointer=True if persistence == "per-thread" else None, + ) + config = {"configurable": {"thread_id": "1"}} + await app.ainvoke({}, config) + (task,) = (await app.aget_state(config)).tasks + + await app.aupdate_state(task.state, _both("edit"), as_node="a") + + (task,) = (await app.aget_state(config, subgraphs=True)).tasks + assert task.state.values == _both("a1", "edit") + + +def test_subgraph_update_state_keeps_history( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + app = _nested_app(sync_checkpointer, snapshot_frequency=2) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + + child = _scoped(config, _child_namespace(app, config)) + app.update_state(child, _both("manual")) + + assert app.get_state(child).values == _both("a1", "b1", "b2", "manual") + + +async def test_subgraph_aupdate_state_keeps_history( + async_checkpointer: BaseCheckpointSaver, +) -> None: + app = _nested_app(async_checkpointer, snapshot_frequency=2) + config = {"configurable": {"thread_id": "1"}} + await app.ainvoke({}, config) + + child = _scoped(config, await _achild_namespace(app, config)) + await app.aupdate_state(child, _both("manual")) + + assert (await app.aget_state(child)).values == _both("a1", "b1", "b2", "manual") + + +def test_stateless_subgraph_persists_nothing( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + app = _nested_app(sync_checkpointer, subgraph_checkpointer=False) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + + child_tasks = [ + task + for snapshot in app.get_state_history(config) + for task in snapshot.tasks + if task.name == "child" and isinstance(task.state, dict) + ] + + assert child_tasks == [] + assert app.get_state(config).values == _both("a1", "b1", "b2") + + +def test_completed_subgraph_exposes_no_task_state( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + app = _nested_app(sync_checkpointer) + config = {"configurable": {"thread_id": "1"}} + app.invoke({}, config) + + assert app.get_state(config, subgraphs=True).tasks == () + + +def _written_delta_checkpoint() -> Any: + checkpoint = empty_checkpoint() + checkpoint["channel_versions"]["delta"] = 1 + return checkpoint + + +def test_hydrating_written_delta_channel_without_saver_raises() -> None: + with pytest.raises(ValueError, match="no checkpointer"): + channels_from_checkpoint( + {"delta": DeltaChannel(_extend)}, _written_delta_checkpoint() + ) + + +async def test_ahydrating_written_delta_channel_without_saver_raises() -> None: + with pytest.raises(ValueError, match="no checkpointer"): + await achannels_from_checkpoint( + {"delta": DeltaChannel(_extend)}, _written_delta_checkpoint() + ) + + +def test_hydrating_written_delta_channel_without_config_raises() -> None: + with pytest.raises(ValueError, match="no checkpointer"): + channels_from_checkpoint( + {"delta": DeltaChannel(_extend)}, + _written_delta_checkpoint(), + saver=InMemorySaver(), + ) + + +def test_hydrating_unwritten_delta_channel_without_saver_is_empty() -> None: + channels, _ = channels_from_checkpoint( + {"delta": DeltaChannel(_extend)}, empty_checkpoint() + ) + assert channels["delta"].get() == [] + + +def test_root_checkpointer_true_graph_state_read_raises() -> None: + app = _child_builder().compile(checkpointer=True) + with pytest.raises(RuntimeError, match="checkpointer=True cannot be used"): + app.get_state({"configurable": {"thread_id": "1"}}) + + +def test_stateless_graph_update_state_ignores_lent_saver() -> None: + saver = InMemorySaver() + app = _child_builder().compile(checkpointer=False) + config = { + "configurable": { + "thread_id": "1", + "checkpoint_ns": "child:1", + CONFIG_KEY_CHECKPOINTER: saver, + } + } + with pytest.raises(ValueError, match="No checkpointer set"): + app.update_state(config, _both("x"), as_node="a") + assert list(saver.list(None)) == [] + + +def test_graph_without_a_checkpointer_reads_through_a_lent_saver( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + app = _child_builder().compile() + config = { + "configurable": {"thread_id": "1", CONFIG_KEY_CHECKPOINTER: sync_checkpointer} + } + app.invoke(_both("in"), config) + + assert app.get_state(config).values == _both("in", "a1", "b1", "b2") + app.update_state(config, _both("edit"), as_node="b") + assert app.get_state(config).values == _both("in", "a1", "b1", "b2", "edit") + assert next(iter(app.get_state_history(config))).values == _both( + "in", "a1", "b1", "b2", "edit" + ) + + +async def test_graph_without_a_checkpointer_areads_through_a_lent_saver( + async_checkpointer: BaseCheckpointSaver, +) -> None: + app = _child_builder().compile() + config = { + "configurable": {"thread_id": "1", CONFIG_KEY_CHECKPOINTER: async_checkpointer} + } + await app.ainvoke(_both("in"), config) + + assert (await app.aget_state(config)).values == _both("in", "a1", "b1", "b2") + await app.aupdate_state(config, _both("edit"), as_node="b") + assert (await app.aget_state(config)).values == _both( + "in", "a1", "b1", "b2", "edit" + ) + history = [s async for s in app.aget_state_history(config)] + assert history[0].values == _both("in", "a1", "b1", "b2", "edit") + + +def test_second_call_of_a_checkpointer_true_subgraph_reads_its_own_history( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + child = _child_builder().compile(checkpointer=True) + + def node(state: dict) -> dict: + child.invoke(_both("first")) + child.invoke(_both("second")) + return {} + + builder = StateGraph(_state_schema()) + builder.add_node("node", node) + builder.add_edge(START, "node") + builder.compile(checkpointer=sync_checkpointer).invoke( + _both(), {"configurable": {"thread_id": "1"}} + ) + + def read(namespace: str) -> dict: + return child.get_state( + { + "configurable": { + "thread_id": "1", + "checkpoint_ns": namespace, + CONFIG_KEY_CHECKPOINTER: sync_checkpointer, + } + } + ).values + + assert read("node") == _both("first", "a1", "b1", "b2") + assert read("node|1") == _both("second", "a1", "b1", "b2") + + +async def test_second_call_of_a_checkpointer_true_subgraph_areads_its_own_history( + async_checkpointer: BaseCheckpointSaver, +) -> None: + child = _child_builder().compile(checkpointer=True) + + async def node(state: dict, config: RunnableConfig) -> dict: + await child.ainvoke(_both("first"), config) + await child.ainvoke(_both("second"), config) + return {} + + builder = StateGraph(_state_schema()) + builder.add_node("node", node) + builder.add_edge(START, "node") + await builder.compile(checkpointer=async_checkpointer).ainvoke( + _both(), {"configurable": {"thread_id": "1"}} + ) + + async def read(namespace: str) -> dict: + snapshot = await child.aget_state( + { + "configurable": { + "thread_id": "1", + "checkpoint_ns": namespace, + CONFIG_KEY_CHECKPOINTER: async_checkpointer, + } + } + ) + return snapshot.values + + assert await read("node") == _both("first", "a1", "b1", "b2") + assert await read("node|1") == _both("second", "a1", "b1", "b2") From 1fd716c44ade4661609a8dc9002a0687d1e06e46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:14:30 -0700 Subject: [PATCH 15/17] chore(deps): bump virtualenv from 21.7.12 to 21.7.13 in /libs/cli (#9167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.7.12 to 21.7.13.
    Release notes

    Sourced from virtualenv's releases.

    21.7.13

    What's Changed

    Full Changelog: https://github.com/pypa/virtualenv/compare/21.7.12...21.7.13

    Changelog

    Sourced from virtualenv's changelog.

    Bugfixes - 21.7.13

    • Fix activate and activate.fish running commands embedded in the virtual environment path or in the interpreter's Tcl/Tk library paths. (:issue:3252)

    v21.7.12 (2026-09-18)


    Commits

    [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=virtualenv&package-manager=uv&previous-version=21.7.12&new-version=21.7.13)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show 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).
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/cli/uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/cli/uv.lock b/libs/cli/uv.lock index 101c6d6d7..b5ddfbbad 100644 --- a/libs/cli/uv.lock +++ b/libs/cli/uv.lock @@ -2375,7 +2375,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.7.12" +version = "21.7.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -2384,9 +2384,9 @@ dependencies = [ { name = "python-discovery" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/db/2aae91b0237f5f6b1d1229bf4953f3b8bdd2c215312ad2d2b3f57fd831df/virtualenv-21.7.12.tar.gz", hash = "sha256:5b6576455f10637067cc20c3f9f7c677575eecaa8c95fc049db2f650652befd5", size = 5359483, upload-time = "2026-09-18T01:40:43.877Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/50/c9b84eb106d0db420b9878dac0a386c5791726f127ce20b06897f6e3a1e9/virtualenv-21.7.13.tar.gz", hash = "sha256:0355558b6f33619aab31347e43643b0ebc97f61ea3acf617b2b69e1f8a843d11", size = 5360306, upload-time = "2026-09-18T04:35:49.354Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/ef/39ecac9ca33d0b7994e63accc86d7224a4341d2e1b42b6da1aa5c24e6a5c/virtualenv-21.7.12-py3-none-any.whl", hash = "sha256:3572a2e13deac1c2727e48ec43dc962f698c453c5c90eb2bd737c81bcd2ac0b0", size = 5328681, upload-time = "2026-09-18T01:40:41.848Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ce/e74453531b0c49a58d0e8a4f9bab4495705859fee4a4f7de27d58f4a791a/virtualenv-21.7.13-py3-none-any.whl", hash = "sha256:1bea5af7463f59c4719db48fe739579a2a4f569c96f26c086edda85c96da9f59", size = 5328680, upload-time = "2026-09-18T04:35:47.194Z" }, ] [[package]] From 7dc9195e4141c8fbd8118581b3dd61d158628aa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:14:46 -0700 Subject: [PATCH 16/17] chore(deps): bump notebook from 7.5.6 to 7.5.7 in /libs/langgraph (#9168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [notebook](https://github.com/jupyter/notebook) from 7.5.6 to 7.5.7.
    Release notes

    Sourced from notebook's releases.

    v7.5.7

    7.5.7

    (Full Changelog)

    Maintenance and upkeep improvements

    Contributors to this release

    The following people contributed discussions, new ideas, code and documentation contributions, and review. See our definition of contributors.

    (GitHub contributors page for this release)

    @​jtpio (activity)

    Changelog

    Sourced from notebook's changelog.

    7.5.7

    (Full Changelog)

    Maintenance and upkeep improvements

    Contributors to this release

    The following people contributed discussions, new ideas, code and documentation contributions, and review. See our definition of contributors.

    (GitHub contributors page for this release)

    @​jtpio (activity)

    Commits

    [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=notebook&package-manager=uv&previous-version=7.5.6&new-version=7.5.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show 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).
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/langgraph/uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index df179835f..27f74c043 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -2146,7 +2146,7 @@ wheels = [ [[package]] name = "notebook" -version = "7.5.6" +version = "7.5.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, @@ -2155,9 +2155,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/c2/cf59bd2e6f2c8b976b52477e3e53bf6f97bc714ed046a51821afb428eaee/notebook-7.5.6.tar.gz", hash = "sha256:621174aade80108f0020b0f00738000b215f75fa3cd90771ad7aa0f24536a4e1", size = 14170814, upload-time = "2026-04-30T11:46:26.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/c4/f71f8716f2903e9e817a47f534b9fd84831e155e2acb32c26691c8e06243/notebook-7.5.7.tar.gz", hash = "sha256:d6d59288a25303b25e1dcb71e9b017ec3a785f7d92f38b9bc288ca1970d5b0a8", size = 14171612, upload-time = "2026-06-04T18:33:45.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/d6/1fd0646b9bbd9efbb0b8ae21b2325fbef515769a5621c03e31d8eb8da587/notebook-7.5.6-py3-none-any.whl", hash = "sha256:4dde3f8fb55fa8fb7946d58c6e869ce9baf46d00fc070664f62604569d0faca0", size = 14581730, upload-time = "2026-04-30T11:46:22.342Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4d/b3347f7073a377273531efe4ffc738fc910e93718fd2838c7ebf6736c6af/notebook-7.5.7-py3-none-any.whl", hash = "sha256:1f95f79d117e47d20b5555b5c85a397d2cfecf136978aaab767cf0314b09165b", size = 14583767, upload-time = "2026-06-04T18:33:40.987Z" }, ] [[package]] From 9a0394d88b2211f299dcd69df92db3480c69ee61 Mon Sep 17 00:00:00 2001 From: Elior Nataf Lackritz Date: Sat, 3 Oct 2026 08:55:59 -0400 Subject: [PATCH 17/17] fix(langgraph): don't replay an abandoned branch into a DeltaChannel fork (#8548) Fixes #8443 Fixes #9089 A checkpoint keeps the pending writes that produced its child, and nothing records which child consumed them. When a new branch starts from a checkpoint that already has pending writes (going back in time, or new input on an interrupted head), the `DeltaChannel` ancestor walk replays those writes into the new branch too. The live run is correct; only a reload is wrong: ``` fork base: ['in-1', 'first-out'] fork returns: ['in-1', 'first-out', 'in-3', 'third-out'] reload gives: ['in-1', 'first-out', 'in-2', 'in-3', 'third-out'] ^^^^^^ from the branch the fork replaced ``` Plain channels store their full value and are unaffected, so the tests use one as the oracle. ## Fix The first checkpoint of a new branch snapshots the delta channels its base has pending writes for, so the walk stops inside the branch. Only the base's own writes are branch-specific; everything above it is shared history. A base with no pending writes has nothing to leak, so an ordinary turn that addresses the head (as clients commonly do) stores nothing. `bulk_update_state` takes the set from its first superstep only: a `__copy__` is stored under the base's parent, so nothing after it walks the base's writes. A resume that is not replaying reuses the head's pending writes instead of rerunning their tasks, so it seals only the loaded writes that don't go back to their task: a finished task whose `Send` a `Command(goto=...)` replaced, or an error handler that runs again. A plain resume stores nothing. A resume addressed by `checkpoint_id` reruns them, so it still seals. `put` only stores a blob for a channel whose version moved since the last stored checkpoint, so a snapshot of one that didn't move needs a version bump, and scheduling reads versions. `create_checkpoint` therefore advances every `versions_seen` entry that had seen the old version, including the interrupt tracker. Without the advance, the bump re-fires `interrupt_before` on resume and reruns the channel's subscribers. For each entry it advances, `SNAPSHOT_BUMPS` keeps the version the node really read, so `update_state`'s `as_node` inference reads `versions_seen` as if the bump never happened. A never-written channel gets a version only for the seal; the cadence and a fresh thread's first `update_state` skip it. `update_state` no longer records its narrower `updated_channels` when it snapshots; it skipped a deferred node listed in `next` on resume (#9089). The same seal fixes two `update_state` calls on one checkpoint (editing the same message twice): both store their writes there under the same task id, the saver keeps the first, and the second branch read back the first one's edit. Two things this touches were also wrong on `main`: a resumed error handler that runs again left its stored writes on the head (an exit reload read them twice), and `aupdate_state` on a thread seeded only by updates raised "Ambiguous update" where `update_state` applied the update as the input. `update_state` and `aupdate_state` now share one `as_node` inference. Exit durability has a separate replay bug on `main` when a resumed checkpoint already holds writes (duplicated or reordered replay), unrelated to forks. It's fixed in #9114; the resume test here marks exit durability as a strict expected failure until then. `tests/memory_assert.py` now compares against the checkpoint as read back: a delta channel a step didn't write is refilled on read, which the old comparison reported as a mutation. Cost: 300 turns addressing the head store no snapshots, as on `main`. A resume that reruns finished tasks seals every time. After a parallel task finished, 30 turns of resuming with the head's `checkpoint_id` (what Studio sends) stored 30 snapshots, 191 KB, against 12 KB of delta writes, and a subgraph resume with a finished sibling does the same, since a subgraph loop always counts as replaying. That seal is what keeps a rerun task's new write from being replayed as its old one: without it, a subgraph task that returns something different on the rerun reads back its first result. The reruns happen on `main` too, and stopping them would remove this cost. 276 of 464 cases in `test_delta_channel_fork.py` fail on `main` and pass here (memory, sqlite and postgres, all durabilities). #9089's own case is in `test_delta_channel_update_state.py`, the cadence case in `test_delta_channel_supersteps_bound.py`, and the `as_node` cases in `test_pregel.py`. ## Limits - Threads forked before this change keep their state: the ownership was never recorded, so there is nothing to recover. - With exit durability, a fork at a finished turn stores its writes on the shared base, so the original branch then replays them too (`['h1', 'ai', 'h2-edited', 'ai', 'h2', 'ai']`). Same on `main`. - `Command(update=..., goto=...)` sent to an old checkpoint stores the update there, so the original branch replays it too. The fork itself is correct now; the original branch is the same as on `main`. - #8551 (the mirror case: `update_state`'s own writes leaking into the abandoned branch) is fixed in #9165, stacked on this PR. It builds on this snapshot, but keys off whether the addressed checkpoint is the thread's latest rather than on pending writes, which a finished turn that a later run continued from doesn't have. Thanks to @AnnaSuSu for the report, the reproduction and the snapshot approach, and to @UditDewan for the implementation in #8476. Both are co-authors. --------- Co-authored-by: AnnaSuSu <64579968+AnnaSuSu@users.noreply.github.com> Co-authored-by: UditDewan <194863456+UditDewan@users.noreply.github.com> --- .../langgraph/_internal/_constants.py | 4 + .../langgraph/langgraph/pregel/_checkpoint.py | 157 +++- libs/langgraph/langgraph/pregel/_loop.py | 85 +- libs/langgraph/langgraph/pregel/main.py | 165 ++-- libs/langgraph/tests/memory_assert.py | 8 +- .../tests/test_delta_channel_fork.py | 731 ++++++++++++++++++ .../test_delta_channel_supersteps_bound.py | 30 +- .../tests/test_delta_channel_update_state.py | 57 ++ libs/langgraph/tests/test_pregel.py | 186 +++++ 9 files changed, 1309 insertions(+), 114 deletions(-) create mode 100644 libs/langgraph/tests/test_delta_channel_fork.py diff --git a/libs/langgraph/langgraph/_internal/_constants.py b/libs/langgraph/langgraph/_internal/_constants.py index 58a4e7917..7a369b891 100644 --- a/libs/langgraph/langgraph/_internal/_constants.py +++ b/libs/langgraph/langgraph/_internal/_constants.py @@ -23,6 +23,9 @@ RETURN = sys.intern("__return__") # for writes of a task where we simply record the return value PREVIOUS = sys.intern("__previous__") # the implicit branch that handles each node's Control values +SNAPSHOT_BUMPS = sys.intern("__snapshot_bumps__") +# `versions_seen` key recording which entries a snapshot-only version bump +# advanced, and the versions those nodes really read # --- Reserved cache namespaces --- @@ -116,6 +119,7 @@ RESERVED = { ERROR, ERROR_SOURCE_NODE, NO_WRITES, + SNAPSHOT_BUMPS, # reserved config.configurable keys CONFIG_KEY_SEND, CONFIG_KEY_READ, diff --git a/libs/langgraph/langgraph/pregel/_checkpoint.py b/libs/langgraph/langgraph/pregel/_checkpoint.py index 69ca88708..42c8f51a0 100644 --- a/libs/langgraph/langgraph/pregel/_checkpoint.py +++ b/libs/langgraph/langgraph/pregel/_checkpoint.py @@ -8,13 +8,15 @@ from typing import Any, cast from langchain_core.runnables import RunnableConfig from langgraph.checkpoint.base import ( BaseCheckpointSaver, + ChannelVersions, Checkpoint, + PendingWrite, ) from langgraph.checkpoint.base.id import uuid6 from langgraph.checkpoint.serde.types import _DeltaSnapshot from langgraph._internal._config import DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT -from langgraph._internal._constants import PUSH +from langgraph._internal._constants import NS_END, NS_SEP, PUSH, SNAPSHOT_BUMPS from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel from langgraph.channels.delta import DeltaChannel @@ -50,17 +52,23 @@ def exit_delta_task_id(step: int, task_id: str) -> str: def delta_channels_to_snapshot( channels: Mapping[str, BaseChannel], counters_since_delta_snapshot: Mapping[str, tuple[int, int]], + channel_versions: ChannelVersions, ) -> set[str]: """Return the set of DeltaChannel names that should snapshot now. A channel snapshots when EITHER its accumulated update count reaches `snapshot_frequency` OR the total supersteps since its last snapshot - reaches `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. This is a pure - predicate — no mutation. + reaches `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. A channel without a version + was never written on this branch, so it has nothing to snapshot. This is a + pure predicate — no mutation. """ result: set[str] = set() for name, ch in channels.items(): - if not isinstance(ch, DeltaChannel) or not ch.is_available(): + if ( + not isinstance(ch, DeltaChannel) + or not ch.is_available() + or name not in channel_versions + ): continue updates, supersteps = counters_since_delta_snapshot.get(name, (0, 0)) if ( @@ -80,12 +88,31 @@ def get_updated_channels_from_tasks( def get_delta_channels_from_all_channels( channels: Mapping[str, BaseChannel], + channel_versions: ChannelVersions, ) -> set[str]: - """DeltaChannels to snapshot on the first update_state of a fresh thread.""" + """DeltaChannels to snapshot on the first update_state of a fresh thread: + the ones it wrote, as the rest have no version and nothing to store.""" return { k for k, ch in channels.items() - if isinstance(ch, DeltaChannel) and ch.is_available() + if isinstance(ch, DeltaChannel) and ch.is_available() and k in channel_versions + } + + +def delta_channels_with_pending_writes( + specs: Mapping[str, Any], + pending_writes: Iterable[PendingWrite] | None, +) -> set[str]: + """DeltaChannels a branch starting from this checkpoint must snapshot. + + A checkpoint's pending writes belong to the child that consumed them, and + nothing records which child that was. A new branch snapshots every delta + channel they touch, so its ancestor walk never replays them. + """ + return { + ch + for _, ch, _ in pending_writes or () + if isinstance(specs.get(ch), DeltaChannel) } @@ -122,6 +149,8 @@ def create_checkpoint_plan_for_update_state_api( parents: dict[str, Any], saved_metadata: Mapping[str, Any] | None, is_fresh_thread: bool, + fork_channels: set[str], + channel_versions: ChannelVersions, ) -> tuple[set[str], dict[str, Any]]: """Return ``(channels_to_snapshot, metadata)`` for an update_state head.""" metadata: dict[str, Any] = { @@ -130,14 +159,19 @@ def create_checkpoint_plan_for_update_state_api( "parents": parents, } if is_fresh_thread: - return get_delta_channels_from_all_channels(channels), metadata + return get_delta_channels_from_all_channels( + channels, channel_versions + ), metadata new_counters = create_metadata_for_update_state_api( channels, updated_channels, prev_metadata=saved_metadata, ) - channels_to_snapshot = delta_channels_to_snapshot(channels, new_counters) + channels_to_snapshot = ( + delta_channels_to_snapshot(channels, new_counters, channel_versions) + | fork_channels + ) for k in channels_to_snapshot: new_counters[k] = (0, 0) non_zero = {k: v for k, v in new_counters.items() if v != (0, 0)} @@ -155,6 +189,7 @@ def create_checkpoint( updated_channels: set[str] | None = None, get_next_version: GetNextVersion | None = None, channels_to_snapshot: set[str] | None = None, + stored_versions: ChannelVersions | None = None, ) -> Checkpoint: """Build a new Checkpoint from the previous one and live channel state. @@ -164,9 +199,14 @@ def create_checkpoint( from `checkpoint_writes`. Callers compute the set via `delta_channels_to_snapshot(channels, counters)`; defaults to empty (no snapshots) when not provided. + + `stored_versions` are the channel versions of the last checkpoint the + saver stored. A snapshotted channel whose version has not moved since + then is bumped. """ ts = datetime.now(timezone.utc).isoformat() channels_to_snapshot = channels_to_snapshot or set() + bumped: dict[str, tuple[Any, Any]] = {} if channels is None: values = checkpoint["channel_values"] channel_versions = checkpoint["channel_versions"] @@ -174,30 +214,29 @@ def create_checkpoint( values = {} channel_versions = dict(checkpoint["channel_versions"]) for k in channels: - if k not in channel_versions: - continue ch = channels[k] + if k not in channel_versions: + # A forced snapshot of a never-written channel still has to + # land to stop the ancestor walk, and `put` only stores blobs + # for versioned channels. + if k in channels_to_snapshot and get_next_version is not None: + channel_versions[k] = get_next_version(None, None) + bumped[k] = (None, channel_versions[k]) + values[k] = _DeltaSnapshot(ch.get()) + continue if k in channels_to_snapshot: - # Callers force a full snapshot blob here: exit mode when a - # delta channel reaches its snapshot cadence, and update_state - # on a fresh thread (no ancestor to replay writes from). The - # manual version-bump below only applies to the exit-mode case. - # - # In exit mode, the snapshot decision is deferred to exit - # time (intermediate steps have do_checkpoint=False). The - # channel's count may have reached snapshot_frequency over - # several supersteps, but the LAST superstep may not have - # written to this channel. In that case apply_writes() - # (in _algo.py) didn't bump this channel's version, so - # saver.put() wouldn't include it in new_versions and - # the snapshot blob would be silently dropped. The manual - # bump below closes the gap. In sync/async durability this - # branch is effectively dead code (the step that pushes - # the count to freq always writes the channel). - if get_next_version is not None and ( - updated_channels is None or k not in updated_channels + # `put` only stores a blob for a channel whose version moved, + # so snapshotting a channel this step did not write needs a + # bump: exit mode reaching the cadence on a superstep that + # skipped the channel, and a fork's first checkpoint. + if ( + get_next_version is not None + and stored_versions is not None + and channel_versions[k] == stored_versions.get(k) ): - channel_versions[k] = get_next_version(channel_versions[k], None) + old = channel_versions[k] + channel_versions[k] = get_next_version(old, None) + bumped[k] = (old, channel_versions[k]) values[k] = _DeltaSnapshot(ch.get()) else: v = ch.checkpoint() @@ -209,11 +248,69 @@ def create_checkpoint( id=id or str(uuid6(clock_seq=step)), channel_values=values, channel_versions=channel_versions, - versions_seen=checkpoint["versions_seen"], + versions_seen=_mark_bumps_seen(checkpoint["versions_seen"], bumped), updated_channels=None if updated_channels is None else sorted(updated_channels), ) +def _mark_bumps_seen( + versions_seen: dict[str, ChannelVersions], + bumped: Mapping[str, tuple[Any, Any]], +) -> dict[str, ChannelVersions]: + """Advance whoever had seen a bumped channel's old version to the new one. + + A bump that only stores a snapshot is not a write. Left unseen, it would + re-fire `interrupt_before` and rerun the channel's subscribers. For each + entry it advances, `SNAPSHOT_BUMPS` keeps the new version and the one the + node really read, so `versions_seen_without_bumps` can put the read back. + """ + if not bumped: + return versions_seen + out = dict(versions_seen) + marks = dict(versions_seen.get(SNAPSHOT_BUMPS, {})) + for node, seen in versions_seen.items(): + if node == SNAPSHOT_BUMPS: + continue + for k, (old, new) in bumped.items(): + if seen.get(k) != old: + continue + advanced, read = _bump_keys(node, k) + # If an earlier bump set `old`, the real read is already recorded. + if old is not None and marks.get(advanced) != old: + marks[read] = old + marks[advanced] = new + out[node] = {**out[node], k: new} + if marks: + out[SNAPSHOT_BUMPS] = marks + return out + + +def _bump_keys(node: str, channel: str) -> tuple[str, str]: + # Node names can't contain either separator, so the keys can't collide. + return f"{node}{NS_SEP}{channel}", f"{node}{NS_END}{channel}" + + +def versions_seen_without_bumps( + versions_seen: dict[str, ChannelVersions], +) -> dict[str, ChannelVersions]: + """`versions_seen` as the nodes read it: an entry a bump advanced goes back + to the version the node really read, or away if it never read one.""" + if not (marks := versions_seen.get(SNAPSHOT_BUMPS)): + return versions_seen + out: dict[str, ChannelVersions] = {} + for node, seen in versions_seen.items(): + if node == SNAPSHOT_BUMPS: + continue + out[node] = {} + for k, v in seen.items(): + advanced, read = _bump_keys(node, k) + if marks.get(advanced) != v: + out[node][k] = v + elif read in marks: + out[node][k] = marks[read] + return out + + def _needs_replay(spec: BaseChannel, stored: object) -> bool: """True if `spec` is a `DeltaChannel` and no value is stored at this checkpoint, requiring an ancestor walk to reconstruct. diff --git a/libs/langgraph/langgraph/pregel/_loop.py b/libs/langgraph/langgraph/pregel/_loop.py index 796fb5efc..a079fa4b0 100644 --- a/libs/langgraph/langgraph/pregel/_loop.py +++ b/libs/langgraph/langgraph/pregel/_loop.py @@ -102,6 +102,7 @@ from langgraph.pregel._checkpoint import ( copy_checkpoint, create_checkpoint, delta_channels_to_snapshot, + delta_channels_with_pending_writes, empty_checkpoint, exit_delta_task_id, ) @@ -223,10 +224,18 @@ class PregelLoop: # under the saver's `ORDER BY task_id, idx` sorting. _exit_delta_writes: list[tuple[int, str, str, Any]] | None = None - # Delta channels that saw an Overwrite since the last checkpoint. These - # channels must snapshot after live update applies overwrite semantics so - # sparse replay starts from the same post-overwrite value. - _delta_channels_with_overwrite: set[str] + # Delta channels that must snapshot at the next checkpoint, whatever their + # cadence counters say: + # * an Overwrite arrived since the last checkpoint, so sparse replay has to + # start from the post-overwrite value; + # * the checkpoint this run starts from has pending writes to them; see + # `delta_channels_with_pending_writes`. + _delta_channels_forced_snapshot: set[str] + # Set by `_first` for a resume: the writes it loaded with the checkpoint, + # which `after_tick` checks against the ones reapply handed back. + _resume_loaded_writes: Sequence[PendingWrite] = () + # Tasks `_reapply_writes_to_succeeded_nodes` handed loaded writes back to. + _reapplied_task_ids: set[str] # The checkpoint_config that points at the parent loaded at `__enter__` # (or the synthetic-empty checkpoint, on first run). We capture it @@ -582,7 +591,7 @@ class PregelLoop: # save the new task self.tasks[pushed.id] = pushed # match any pending writes to the new task - if not self.is_replaying: + if self._reapplies_pending_writes: self._reapply_writes_to_succeeded_nodes({pushed.id: pushed}) # return the new task, to be started if not run before return pushed @@ -660,7 +669,7 @@ class PregelLoop: return False # if there are pending writes from a previous loop, apply them - if not self.is_replaying and self.checkpoint_pending_writes: + if self._reapplies_pending_writes and self.checkpoint_pending_writes: self._reapply_writes_to_succeeded_nodes(self.tasks) self._resume_error_handlers_if_applicable() @@ -684,11 +693,26 @@ class PregelLoop: def after_tick(self) -> None: # finish superstep writes = [w for t in self.tasks.values() for w in t.writes] - self._delta_channels_with_overwrite.update( + self._delta_channels_forced_snapshot.update( ch for ch, v in writes if isinstance(self.specs.get(ch), DeltaChannel) and _get_overwrite(v)[0] ) + if self._resume_loaded_writes: + # A loaded write reapply didn't hand back belongs to a task this run + # drops or reruns: a `Send` that `Command(goto=...)` replaced, or an + # error handler that runs again. + self._delta_channels_forced_snapshot.update( + delta_channels_with_pending_writes( + self.specs, + [ + w + for w in self._resume_loaded_writes + if w[0] not in self._reapplied_task_ids + ], + ) + ) + self._resume_loaded_writes = () # all tasks have finished self.updated_channels = apply_writes( self.checkpoint, @@ -734,6 +758,12 @@ class PregelLoop: # private + @property + def _reapplies_pending_writes(self) -> bool: + """Whether the writes loaded with the checkpoint go back to the tasks + that made them, instead of those tasks rerunning.""" + return not self.is_replaying + def _reapply_writes_to_succeeded_nodes( self, tasks: Mapping[str, PregelExecutableTask] ) -> None: @@ -745,6 +775,7 @@ class PregelLoop: for tid, status in read_task_statuses(self.checkpoint_pending_writes).items(): if task := tasks.get(tid): task.writes.extend(status.output) + self._reapplied_task_ids.add(tid) def _resume_error_handlers_if_applicable(self) -> None: """On resume, schedule error handlers for tasks that failed in a prior run. @@ -874,6 +905,23 @@ class PregelLoop: self.checkpoint_pending_writes = [ w for w in self.checkpoint_pending_writes if w[1] != RESUME ] + # A resume that reapplies the head's pending writes only learns which + # of them go back to their tasks once those are scheduled, so + # `after_tick` seals the rest. Kept apart from the writes this run adds. + reapplies = is_resuming and self._reapplies_pending_writes + self._resume_loaded_writes = ( + [w for w in self.checkpoint_pending_writes if w[0] != NULL_TASK_ID] + if reapplies + else () + ) + self._reapplied_task_ids = set() + self._delta_channels_forced_snapshot = ( + set() + if reapplies + else delta_channels_with_pending_writes( + self.specs, self.checkpoint_pending_writes + ) + ) # map command to writes if input_is_command: @@ -967,7 +1015,7 @@ class PregelLoop: manager=None, updated_channels=updated_channels, ) - self._delta_channels_with_overwrite.update( + self._delta_channels_forced_snapshot.update( c for c, v in input_writes if isinstance(self.specs.get(c), DeltaChannel) and _get_overwrite(v)[0] @@ -1111,8 +1159,10 @@ class PregelLoop: ) # create new checkpoint channels_to_snapshot = ( - delta_channels_to_snapshot(self.channels, new_counters) - | self._delta_channels_with_overwrite + delta_channels_to_snapshot( + self.channels, new_counters, self.checkpoint["channel_versions"] + ) + | self._delta_channels_forced_snapshot if do_checkpoint else set() ) @@ -1126,11 +1176,12 @@ class PregelLoop: if do_checkpoint else None, channels_to_snapshot=channels_to_snapshot, + stored_versions=self.checkpoint_previous_versions, ) for k in channels_to_snapshot: new_counters[k] = (0, 0) if do_checkpoint: - self._delta_channels_with_overwrite.difference_update(channels_to_snapshot) + self._delta_channels_forced_snapshot.difference_update(channels_to_snapshot) non_zero = {k: v for k, v in new_counters.items() if v != (0, 0)} if non_zero: self.checkpoint_metadata["counters_since_delta_snapshot"] = non_zero @@ -1214,8 +1265,10 @@ class PregelLoop: self.checkpoint_metadata.get("counters_since_delta_snapshot") or {} ) channels_to_snapshot = ( - delta_channels_to_snapshot(self.channels, counters) - | self._delta_channels_with_overwrite + delta_channels_to_snapshot( + self.channels, counters, self.checkpoint["channel_versions"] + ) + | self._delta_channels_forced_snapshot ) pending = [ @@ -1576,7 +1629,7 @@ class SyncPregelLoop(PregelLoop, AbstractContextManager): if handler_task is None: return None self.tasks[handler_task.id] = handler_task - if not self.is_replaying: + if self._reapplies_pending_writes: self._reapply_writes_to_succeeded_nodes({handler_task.id: handler_task}) for task in self.match_cached_writes(): self.output_writes(task.id, task.writes, cached=True) @@ -1660,7 +1713,6 @@ class SyncPregelLoop(PregelLoop, AbstractContextManager): ) self._delta_write_futs = [] self._error_handler_write_futs = [] - self._delta_channels_with_overwrite = set() self._exit_delta_writes = ( [] if self.durability == "exit" and self.checkpointer is not None else None ) @@ -1831,7 +1883,7 @@ class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager): if handler_task is None: return None self.tasks[handler_task.id] = handler_task - if not self.is_replaying: + if self._reapplies_pending_writes: self._reapply_writes_to_succeeded_nodes({handler_task.id: handler_task}) for task in await self.amatch_cached_writes(): self.output_writes(task.id, task.writes, cached=True) @@ -1918,7 +1970,6 @@ class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager): ) self._delta_write_futs = [] self._error_handler_write_futs = [] - self._delta_channels_with_overwrite = set() self._exit_delta_writes = ( [] if self.durability == "exit" and self.checkpointer is not None else None ) diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index c32f8cb37..ffb828cbc 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -46,6 +46,7 @@ from langchain_core.runnables.schema import StreamEvent from langgraph.cache.base import BaseCache from langgraph.checkpoint.base import ( BaseCheckpointSaver, + ChannelVersions, Checkpoint, CheckpointTuple, ) @@ -132,8 +133,10 @@ from langgraph.pregel._checkpoint import ( copy_checkpoint, create_checkpoint, create_checkpoint_plan_for_update_state_api, + delta_channels_with_pending_writes, empty_checkpoint, get_updated_channels_from_tasks, + versions_seen_without_bumps, ) from langgraph.pregel._draw import draw_graph from langgraph.pregel._io import map_input, read_channels @@ -1585,6 +1588,27 @@ class Pregel( checkpoint_tuple.config, checkpoint_tuple, saver=checkpointer ) + def _infer_as_node(self, versions_seen: dict[str, ChannelVersions]) -> str | None: + if len(self.nodes) == 1: + return next(iter(self.nodes)) + seen = versions_seen_without_bumps(versions_seen) + if not any(v for vv in seen.values() for v in vv.values()): + if ( + isinstance(self.input_channels, str) + and self.input_channels in self.nodes + ): + return self.input_channels + return None + last_seen_by_node = sorted( + (v, n) for n, s in seen.items() if n in self.nodes for v in s.values() + ) + # if two nodes updated the state at the same time, it's ambiguous + if len(last_seen_by_node) == 1 or ( + last_seen_by_node and last_seen_by_node[-1][0] != last_seen_by_node[-2][0] + ): + return last_seen_by_node[-1][1] + return None + def bulk_update_state( self, config: RunnableConfig, @@ -1619,13 +1643,24 @@ class Pregel( return pregel.bulk_update_state(subgraph_config, supersteps) def perform_superstep( - input_config: RunnableConfig, updates: Sequence[StateUpdate] + input_config: RunnableConfig, + updates: Sequence[StateUpdate], + is_first: bool = False, ) -> RunnableConfig: # get last checkpoint config = self._own_checkpoint_config( ensure_config(self.config, input_config) ) saved = checkpointer.get_tuple(config) + # Later supersteps, including the one after a `__copy__` (stored + # under the base's parent), never walk through the base's writes. + fork_pending = ( + delta_channels_with_pending_writes( + self.channels, saved.pending_writes if saved else None + ) + if is_first + else set() + ) if saved is not None: self._migrate_checkpoint(saved.checkpoint) checkpoint = ( @@ -1705,9 +1740,17 @@ class Pregel( self.trigger_to_nodes, ) # save checkpoint + next_checkpoint = create_checkpoint( + checkpoint, + channels, + step, + get_next_version=checkpointer.get_next_version, + channels_to_snapshot=fork_pending, + stored_versions=checkpoint_previous_versions, + ) next_config = checkpointer.put( checkpoint_config, - create_checkpoint(checkpoint, channels, step), + next_checkpoint, { "source": "update", "step": step + 1, @@ -1715,7 +1758,7 @@ class Pregel( }, get_new_channel_versions( checkpoint_previous_versions, - checkpoint["channel_versions"], + next_checkpoint["channel_versions"], ), ) return patch_checkpoint_map( @@ -1744,9 +1787,17 @@ class Pregel( if saved and saved.metadata.get("step") is not None else -1 ) + next_checkpoint = create_checkpoint( + checkpoint, + channels, + next_step, + get_next_version=checkpointer.get_next_version, + channels_to_snapshot=fork_pending, + stored_versions=checkpoint_previous_versions, + ) next_config = checkpointer.put( checkpoint_config, - create_checkpoint(checkpoint, channels, next_step), + next_checkpoint, { "source": "input", "step": next_step, @@ -1756,7 +1807,7 @@ class Pregel( }, get_new_channel_versions( checkpoint_previous_versions, - checkpoint["channel_versions"], + next_checkpoint["channel_versions"], ), ) @@ -1885,31 +1936,8 @@ class Pregel( if len(updates) == 1: values, as_node, task_id = updates[0] # find last node that updated the state, if not provided - if as_node is None and len(self.nodes) == 1: - as_node = tuple(self.nodes)[0] - elif as_node is None and not any( - v - for vv in checkpoint["versions_seen"].values() - for v in vv.values() - ): - if ( - isinstance(self.input_channels, str) - and self.input_channels in self.nodes - ): - as_node = self.input_channels - elif as_node is None: - last_seen_by_node = sorted( - (v, n) - for n, seen in checkpoint["versions_seen"].items() - if n in self.nodes - for v in seen.values() - ) - # if two nodes updated the state at the same time, it's ambiguous - if last_seen_by_node: - if len(last_seen_by_node) == 1: - as_node = last_seen_by_node[0][1] - elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]: - as_node = last_seen_by_node[-1][1] + if as_node is None: + as_node = self._infer_as_node(checkpoint["versions_seen"]) if as_node is None: raise InvalidUpdateError("Ambiguous update, specify as_node") if as_node not in self.nodes: @@ -1999,17 +2027,19 @@ class Pregel( parents=saved.metadata.get("parents", {}) if saved else {}, saved_metadata=saved.metadata if saved else None, is_fresh_thread=saved is None, + fork_channels=fork_pending, + channel_versions=checkpoint["channel_versions"], ) ) checkpoint = create_checkpoint( checkpoint, channels, step + 1, - updated_channels=updated_channels if channels_to_snapshot else None, get_next_version=checkpointer.get_next_version if channels_to_snapshot else None, channels_to_snapshot=channels_to_snapshot, + stored_versions=checkpoint_previous_versions, ) next_config = checkpointer.put( checkpoint_config, @@ -2028,8 +2058,8 @@ class Pregel( current_config = patch_configurable( config, {CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID])} ) - for superstep in supersteps: - current_config = perform_superstep(current_config, superstep) + for i, superstep in enumerate(supersteps): + current_config = perform_superstep(current_config, superstep, i == 0) return current_config async def abulk_update_state( @@ -2066,13 +2096,24 @@ class Pregel( return await pregel.abulk_update_state(subgraph_config, supersteps) async def aperform_superstep( - input_config: RunnableConfig, updates: Sequence[StateUpdate] + input_config: RunnableConfig, + updates: Sequence[StateUpdate], + is_first: bool = False, ) -> RunnableConfig: # get last checkpoint config = self._own_checkpoint_config( ensure_config(self.config, input_config) ) saved = await checkpointer.aget_tuple(config) + # Later supersteps, including the one after a `__copy__` (stored + # under the base's parent), never walk through the base's writes. + fork_pending = ( + delta_channels_with_pending_writes( + self.channels, saved.pending_writes if saved else None + ) + if is_first + else set() + ) if saved is not None: self._migrate_checkpoint(saved.checkpoint) checkpoint = ( @@ -2150,16 +2191,25 @@ class Pregel( self.trigger_to_nodes, ) # save checkpoint + next_checkpoint = create_checkpoint( + checkpoint, + channels, + step, + get_next_version=checkpointer.get_next_version, + channels_to_snapshot=fork_pending, + stored_versions=checkpoint_previous_versions, + ) next_config = await checkpointer.aput( checkpoint_config, - create_checkpoint(checkpoint, channels, step), + next_checkpoint, { "source": "update", "step": step + 1, "parents": saved.metadata.get("parents", {}) if saved else {}, }, get_new_channel_versions( - checkpoint_previous_versions, checkpoint["channel_versions"] + checkpoint_previous_versions, + next_checkpoint["channel_versions"], ), ) return patch_checkpoint_map( @@ -2188,9 +2238,17 @@ class Pregel( if saved and saved.metadata.get("step") is not None else -1 ) + next_checkpoint = create_checkpoint( + checkpoint, + channels, + next_step, + get_next_version=checkpointer.get_next_version, + channels_to_snapshot=fork_pending, + stored_versions=checkpoint_previous_versions, + ) next_config = await checkpointer.aput( checkpoint_config, - create_checkpoint(checkpoint, channels, next_step), + next_checkpoint, { "source": "input", "step": next_step, @@ -2200,7 +2258,7 @@ class Pregel( }, get_new_channel_versions( checkpoint_previous_versions, - checkpoint["channel_versions"], + next_checkpoint["channel_versions"], ), ) @@ -2328,27 +2386,8 @@ class Pregel( if len(updates) == 1: values, as_node, task_id = updates[0] # find last node that updated the state, if not provided - if as_node is None and len(self.nodes) == 1: - as_node = tuple(self.nodes)[0] - elif as_node is None and not saved: - if ( - isinstance(self.input_channels, str) - and self.input_channels in self.nodes - ): - as_node = self.input_channels - elif as_node is None: - last_seen_by_node = sorted( - (v, n) - for n, seen in checkpoint["versions_seen"].items() - if n in self.nodes - for v in seen.values() - ) - # if two nodes updated the state at the same time, it's ambiguous - if last_seen_by_node: - if len(last_seen_by_node) == 1: - as_node = last_seen_by_node[0][1] - elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]: - as_node = last_seen_by_node[-1][1] + if as_node is None: + as_node = self._infer_as_node(checkpoint["versions_seen"]) if as_node is None: raise InvalidUpdateError("Ambiguous update, specify as_node") if as_node not in self.nodes: @@ -2438,17 +2477,19 @@ class Pregel( parents=saved.metadata.get("parents", {}) if saved else {}, saved_metadata=saved.metadata if saved else None, is_fresh_thread=saved is None, + fork_channels=fork_pending, + channel_versions=checkpoint["channel_versions"], ) ) checkpoint = create_checkpoint( checkpoint, channels, step + 1, - updated_channels=updated_channels if channels_to_snapshot else None, get_next_version=checkpointer.get_next_version if channels_to_snapshot else None, channels_to_snapshot=channels_to_snapshot, + stored_versions=checkpoint_previous_versions, ) next_config = await checkpointer.aput( checkpoint_config, @@ -2466,8 +2507,8 @@ class Pregel( current_config = patch_configurable( config, {CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID])} ) - for superstep in supersteps: - current_config = await aperform_superstep(current_config, superstep) + for i, superstep in enumerate(supersteps): + current_config = await aperform_superstep(current_config, superstep, i == 0) return current_config def update_state( diff --git a/libs/langgraph/tests/memory_assert.py b/libs/langgraph/tests/memory_assert.py index 3987d422b..136fcc34c 100644 --- a/libs/langgraph/tests/memory_assert.py +++ b/libs/langgraph/tests/memory_assert.py @@ -85,11 +85,13 @@ class MemorySaverAssertImmutable(InMemorySaver): ) == saved ), config["configurable"]["checkpoint_ns"] + next_config = super().put(config, checkpoint, metadata, new_versions) + # Read back, not the object handed in: a DeltaChannel a step did not + # write is refilled on read from the blob its inherited version points at. self.storage_for_copies[thread_id][checkpoint_ns][checkpoint["id"]] = ( - self.serde.dumps_typed(checkpoint) + self.serde.dumps_typed(super().get(next_config)) ) - # call super to write checkpoint - return super().put(config, checkpoint, metadata, new_versions) + return next_config class MemorySaverNoPending(InMemorySaver): diff --git a/libs/langgraph/tests/test_delta_channel_fork.py b/libs/langgraph/tests/test_delta_channel_fork.py new file mode 100644 index 000000000..5736dc22e --- /dev/null +++ b/libs/langgraph/tests/test_delta_channel_fork.py @@ -0,0 +1,731 @@ +"""Forking a thread must not replay the abandoned branch into the fork. + +Every graph carries a `DeltaChannel` and a plain reducer channel fed the same +values; the plain channel needs no replay, so it is the oracle. +""" + +import sys +import threading +from collections.abc import Sequence +from operator import add +from typing import Annotated, Any + +import pytest +from langchain_core.runnables import RunnableConfig +from langgraph.checkpoint.base import BaseCheckpointSaver +from langgraph.checkpoint.serde.types import _DeltaSnapshot +from typing_extensions import TypedDict + +from langgraph._internal._constants import INPUT +from langgraph.channels.delta import DeltaChannel +from langgraph.errors import NodeError +from langgraph.graph import END, START, StateGraph +from langgraph.types import ( + Command, + Durability, + Send, + StateSnapshot, + StateUpdate, + interrupt, +) + +pytestmark = pytest.mark.anyio +NEEDS_CONTEXTVARS = pytest.mark.skipif( + sys.version_info < (3, 11), + reason="Python 3.11+ is required for async contextvars support", +) + + +def _append(current: list | None, writes: Sequence[Any]) -> list: + out = list(current or []) + for write in writes: + out.extend(write if isinstance(write, list) else [write]) + return out + + +class _State(TypedDict): + log: Annotated[list, DeltaChannel(_append, snapshot_frequency=1000)] + plain: Annotated[list, add] + other: Annotated[list, add] + + +def _build(checkpointer: BaseCheckpointSaver, tag: str) -> Any: + def node(state: _State) -> dict: + return {"log": [f"{tag}-out"], "plain": [f"{tag}-out"]} + + builder = StateGraph(_State) + builder.add_node("n", node) + builder.set_entry_point("n") + builder.set_finish_point("n") + return builder.compile(checkpointer=checkpointer) + + +def _build_without_delta_writes(checkpointer: BaseCheckpointSaver, tag: str) -> Any: + def node(state: _State) -> dict: + return {"other": [f"{tag}-other"]} + + builder = StateGraph(_State) + builder.add_node("n", node) + builder.set_entry_point("n") + builder.set_finish_point("n") + return builder.compile(checkpointer=checkpointer) + + +def _thread(thread_id: str) -> RunnableConfig: + return {"configurable": {"thread_id": thread_id}} + + +def _at(config: RunnableConfig, snapshot: StateSnapshot) -> RunnableConfig: + return { + "configurable": { + **config["configurable"], + "checkpoint_ns": "", + "checkpoint_id": snapshot.config["configurable"]["checkpoint_id"], + } + } + + +def _both(marker: str) -> dict: + return {"log": [marker], "plain": [marker]} + + +def _snapshotted_checkpoints( + checkpointer: BaseCheckpointSaver, config: RunnableConfig +) -> list[str]: + return [ + tuple_.config["configurable"]["checkpoint_id"] + for tuple_ in checkpointer.list(config) + if isinstance(tuple_.checkpoint["channel_values"].get("log"), _DeltaSnapshot) + ] + + +def _assert_fork_is_clean(state: StateSnapshot, abandoned: str) -> None: + assert state.values["log"] == state.values["plain"], ( + f"delta channel diverged from the plain channel: " + f"{state.values['log']} != {state.values['plain']}" + ) + assert abandoned not in state.values["log"], ( + f"{abandoned!r} belongs to the branch the fork replaced, " + f"but was replayed into {state.values['log']}" + ) + + +def test_fork_by_invoke( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + _build(sync_checkpointer, "first").invoke( + _both("in-1"), config, durability=durability + ) + graph = _build(sync_checkpointer, "second") + graph.invoke(_both("in-2"), config, durability=durability) + abandoned_head = graph.get_state(config) + + base = next( + snapshot + for snapshot in graph.get_state_history(config) + if "in-2" not in snapshot.values["log"] + ) + _build(sync_checkpointer, "third").invoke( + _both("in-3"), _at(config, base), durability=durability + ) + + state = graph.get_state(config) + _assert_fork_is_clean(state, "in-2") + assert state.values["log"] == [*base.values["log"], "in-3", "third-out"] + + abandoned = graph.get_state(abandoned_head.config).values + assert abandoned["log"] == abandoned["plain"] == abandoned_head.values["log"] + + +async def test_afork_by_invoke( + async_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + await _build(async_checkpointer, "first").ainvoke( + _both("in-1"), config, durability=durability + ) + graph = _build(async_checkpointer, "second") + await graph.ainvoke(_both("in-2"), config, durability=durability) + abandoned_head = await graph.aget_state(config) + + base = await anext( + snapshot + async for snapshot in graph.aget_state_history(config) + if "in-2" not in snapshot.values["log"] + ) + await _build(async_checkpointer, "third").ainvoke( + _both("in-3"), _at(config, base), durability=durability + ) + + state = await graph.aget_state(config) + _assert_fork_is_clean(state, "in-2") + assert state.values["log"] == [*base.values["log"], "in-3", "third-out"] + + abandoned = (await graph.aget_state(abandoned_head.config)).values + assert abandoned["log"] == abandoned["plain"] == abandoned_head.values["log"] + + +def test_fork_off_checkpoint_before_first_input( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build(sync_checkpointer, "first") + graph.invoke(_both("in-1"), config, durability=durability) + + root = list(graph.get_state_history(config))[-1] + assert root.values["log"] == [] + + _build(sync_checkpointer, "third").invoke( + _both("in-9"), _at(config, root), durability=durability + ) + + state = graph.get_state(config) + _assert_fork_is_clean(state, "in-1") + assert state.values["log"] == ["in-9", "third-out"] + + +async def test_afork_off_checkpoint_before_first_input( + async_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build(async_checkpointer, "first") + await graph.ainvoke(_both("in-1"), config, durability=durability) + + root = [snapshot async for snapshot in graph.aget_state_history(config)][-1] + assert root.values["log"] == [] + + await _build(async_checkpointer, "third").ainvoke( + _both("in-9"), _at(config, root), durability=durability + ) + + state = await graph.aget_state(config) + _assert_fork_is_clean(state, "in-1") + assert state.values["log"] == ["in-9", "third-out"] + + +def test_fork_by_update_state(sync_checkpointer: BaseCheckpointSaver) -> None: + config = _thread("t") + _build(sync_checkpointer, "first").invoke(_both("in-1"), config) + graph = _build(sync_checkpointer, "second") + graph.invoke(_both("in-2"), config) + + base = next( + snapshot + for snapshot in graph.get_state_history(config) + if "in-2" not in snapshot.values["log"] + ) + forked = graph.update_state(_at(config, base), _both("patched")) + + state = graph.get_state(forked) + _assert_fork_is_clean(state, "in-2") + assert state.values["log"] == [*base.values["log"], "patched"] + + +async def test_afork_by_update_state( + async_checkpointer: BaseCheckpointSaver, +) -> None: + config = _thread("t") + await _build(async_checkpointer, "first").ainvoke(_both("in-1"), config) + graph = _build(async_checkpointer, "second") + await graph.ainvoke(_both("in-2"), config) + + base = await anext( + snapshot + async for snapshot in graph.aget_state_history(config) + if "in-2" not in snapshot.values["log"] + ) + forked = await graph.aupdate_state(_at(config, base), _both("patched")) + + state = await graph.aget_state(forked) + _assert_fork_is_clean(state, "in-2") + assert state.values["log"] == [*base.values["log"], "patched"] + + +def test_unaddressed_run_keeps_snapshot_cadence( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build(sync_checkpointer, "first") + graph.invoke(_both("in-1"), config, durability=durability) + graph.invoke(_both("in-2"), config, durability=durability) + + assert not _snapshotted_checkpoints(sync_checkpointer, config) + + +def test_fork_before_first_value_when_fork_never_writes_the_channel( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build(sync_checkpointer, "first") + graph.invoke(_both("in-1"), config, durability=durability) + + root = list(graph.get_state_history(config))[-1] + assert root.values["log"] == [] + + _build_without_delta_writes(sync_checkpointer, "third").invoke( + {"other": ["in-9"]}, _at(config, root), durability=durability + ) + + state = graph.get_state(config) + _assert_fork_is_clean(state, "in-1") + assert state.values["log"] == [] + + +async def test_afork_before_first_value_when_fork_never_writes_the_channel( + async_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build(async_checkpointer, "first") + await graph.ainvoke(_both("in-1"), config, durability=durability) + + root = [snapshot async for snapshot in graph.aget_state_history(config)][-1] + assert root.values["log"] == [] + + await _build_without_delta_writes(async_checkpointer, "third").ainvoke( + {"other": ["in-9"]}, _at(config, root), durability=durability + ) + + state = await graph.aget_state(config) + _assert_fork_is_clean(state, "in-1") + assert state.values["log"] == [] + + +def test_fork_before_first_value_by_bulk_update( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + config = _thread("t") + graph = _build(sync_checkpointer, "first") + graph.invoke(_both("in-1"), config) + + root = list(graph.get_state_history(config))[-1] + assert root.values["log"] == [] + + forked = graph.bulk_update_state( + _at(config, root), + [ + [StateUpdate({"other": ["s1"]}, "n")], + [StateUpdate(_both("s2"), "n")], + ], + ) + + state = graph.get_state(forked) + _assert_fork_is_clean(state, "in-1") + assert state.values["log"] == ["s2"] + + +@pytest.mark.parametrize("first_as_node", [INPUT, END, "__copy__"]) +def test_fork_by_bulk_update_whose_first_superstep_skips_the_plan( + sync_checkpointer: BaseCheckpointSaver, first_as_node: str +) -> None: + config = _thread("t") + _build(sync_checkpointer, "first").invoke(_both("in-1"), config) + graph = _build(sync_checkpointer, "second") + graph.invoke(_both("in-2"), config) + + base = next( + snapshot + for snapshot in graph.get_state_history(config) + if "in-2" not in snapshot.values["log"] + ) + first = ( + StateUpdate(_both("first-step"), first_as_node) + if first_as_node == INPUT + else StateUpdate(None, first_as_node) + ) + forked = graph.bulk_update_state( + _at(config, base), + [[first], [StateUpdate(_both("second-step"), "n")]], + ) + + state = graph.get_state(forked) + assert state.values["log"] == state.values["plain"], ( + f"delta channel diverged from the plain channel: " + f"{state.values['log']} != {state.values['plain']}" + ) + + +def test_bulk_update_after_a_copy_stores_no_snapshot( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + config = _thread("t") + _build(sync_checkpointer, "first").invoke(_both("in-1"), config) + graph = _build(sync_checkpointer, "second") + graph.invoke(_both("in-2"), config) + base = next( + s for s in graph.get_state_history(config) if "in-2" not in s.values["log"] + ) + + forked = graph.bulk_update_state( + _at(config, base), + [[StateUpdate(None, "__copy__")], [StateUpdate(_both("s2"), "n")]], + ) + + assert graph.get_state(forked).values["log"] == ["in-1", "first-out", "s2"] + assert not _snapshotted_checkpoints(sync_checkpointer, config) + + +def test_unaddressed_bulk_update_keeps_snapshot_cadence( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + config = _thread("t") + graph = _build(sync_checkpointer, "first") + graph.invoke(_both("in-1"), config) + + graph.bulk_update_state( + config, + [[StateUpdate(_both(f"u{i}"), "n")] for i in range(4)], + ) + + assert not _snapshotted_checkpoints(sync_checkpointer, config) + + +def _build_paused_before_b(checkpointer: BaseCheckpointSaver) -> Any: + builder = StateGraph(_State) + builder.add_node("a", lambda state: _both("a")) + builder.add_node("b", lambda state: _both("b")) + builder.add_edge(START, "a") + builder.add_edge("a", "b") + builder.add_edge("b", END) + return builder.compile(checkpointer=checkpointer, interrupt_before=["b"]) + + +def _build_parallel_interrupt(checkpointer: BaseCheckpointSaver) -> Any: + def ask(state: _State) -> dict: + interrupt("approve?") + return {"other": ["q"]} + + builder = StateGraph(_State) + builder.add_node("p", lambda state: _both("p")) + builder.add_node("q", ask) + builder.add_edge(START, "p") + builder.add_edge(START, "q") + return builder.compile(checkpointer=checkpointer) + + +def test_resume_at_interrupt_before_with_the_head_checkpoint_id_runs_the_node( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build_paused_before_b(sync_checkpointer) + graph.invoke(_both("in"), config, durability=durability) + + graph.invoke(None, graph.get_state(config).config, durability=durability) + + state = graph.get_state(config) + assert state.next == (), f"resume paused again before {state.next}" + assert state.values["log"] == state.values["plain"] == ["in", "a", "b"] + + +async def test_aresume_at_interrupt_before_with_the_head_checkpoint_id_runs_the_node( + async_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build_paused_before_b(async_checkpointer) + await graph.ainvoke(_both("in"), config, durability=durability) + + await graph.ainvoke( + None, (await graph.aget_state(config)).config, durability=durability + ) + + state = await graph.aget_state(config) + assert state.next == (), f"resume paused again before {state.next}" + assert state.values["log"] == state.values["plain"] == ["in", "a", "b"] + + +def test_replay_from_a_paused_checkpoint_runs_the_node_once( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + config = _thread("t") + graph = _build_paused_before_b(sync_checkpointer) + graph.invoke(_both("in"), config) + paused = graph.get_state(config).config + graph.invoke(None, config) + + graph.invoke(None, paused) + + state = graph.get_state(config) + assert state.next == (), f"replay paused again before {state.next}" + assert state.values["log"] == state.values["plain"] == ["in", "a", "b"] + + +@pytest.mark.parametrize("addressed", [False, True]) +def test_new_input_on_an_interrupted_head_does_not_replay_its_pending_writes( + sync_checkpointer: BaseCheckpointSaver, durability: Durability, addressed: bool +) -> None: + config = _thread("t") + graph = _build_parallel_interrupt(sync_checkpointer) + graph.invoke(_both("in-1"), config, durability=durability) + head = graph.get_state(config).config + + graph.invoke(_both("in-2"), head if addressed else config, durability=durability) + + state = graph.get_state(config) + assert state.values["log"] == state.values["plain"] == ["in-1", "in-2", "p"] + + +def _build_deferred_after_interrupt(checkpointer: BaseCheckpointSaver) -> Any: + builder = StateGraph(_State) + builder.add_node("a", lambda state: _both("a")) + builder.add_node("b", lambda state: _both("b"), defer=True) + builder.add_node("c", lambda state: {}) + builder.add_edge(START, "a") + builder.add_edge("a", "b") + builder.add_edge("a", "c") + return builder.compile(checkpointer=checkpointer, interrupt_after=["a"]) + + +@pytest.mark.parametrize( + "durability", + [ + "sync", + "async", + pytest.param( + "exit", + marks=pytest.mark.xfail( + reason="exit durability stores a resumed run's loaded writes twice", + strict=True, + ), + ), + ], +) +def test_resume_on_an_interrupted_head_consumes_its_writes_without_a_snapshot( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build_parallel_interrupt(sync_checkpointer) + graph.invoke(_both("in-1"), config, durability=durability) + + graph.invoke(Command(resume="yes"), config, durability=durability) + + state = graph.get_state(config) + assert state.next == () + assert state.values["log"] == state.values["plain"] == ["in-1", "p"] + assert not _snapshotted_checkpoints(sync_checkpointer, config) + + +def test_resume_whose_node_writes_the_delta_channel_stores_no_snapshot( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + def ask(state: _State) -> dict: + return _both(interrupt("continue?")) + + graph = ( + StateGraph(_State) + .add_node("ask", ask) + .add_edge(START, "ask") + .compile(checkpointer=sync_checkpointer) + ) + config = _thread("t") + graph.invoke(_both("in"), config, durability=durability) + + graph.invoke(Command(resume="yes"), config, durability=durability) + + state = graph.get_state(config) + assert state.values["log"] == state.values["plain"] == ["in", "yes"] + assert not _snapshotted_checkpoints(sync_checkpointer, config) + + +def test_resume_after_interrupt_before_stores_no_snapshot( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build(sync_checkpointer, "n") + graph.invoke(_both("in"), config, interrupt_before=["n"], durability=durability) + + graph.invoke(None, config, durability=durability) + + state = graph.get_state(config) + assert state.values["log"] == state.values["plain"] == ["in", "n-out"] + assert not _snapshotted_checkpoints(sync_checkpointer, config) + + +def _build_send_fan_out(checkpointer: BaseCheckpointSaver) -> Any: + def q(state: _State) -> dict: + interrupt("continue?") + return _both("q") + + builder = StateGraph(_State) + builder.add_node("p", lambda state: _both("p")) + builder.add_node("q", q) + builder.add_conditional_edges( + START, lambda state: [Send("p", state), Send("q", state)], ["p", "q"] + ) + return builder.compile(checkpointer=checkpointer) + + +def test_resume_that_replaces_the_pending_sends_drops_the_finished_task( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + graph = _build_send_fan_out(sync_checkpointer) + config = _thread("t") + graph.invoke(_both("in"), config, durability=durability) + + live = graph.invoke( + Command(resume="yes", goto=[Send("q", _both("unused"))]), + config, + durability=durability, + ) + + state = graph.get_state(config) + assert state.values["log"] == state.values["plain"] == live["log"], ( + f"'p' ran in the fan-out the resume replaced, but the reload reads " + f"{state.values['log']} against the live {live['log']}" + ) + + +@NEEDS_CONTEXTVARS +async def test_aresume_that_replaces_the_pending_sends_drops_the_finished_task( + async_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + graph = _build_send_fan_out(async_checkpointer) + config = _thread("t") + await graph.ainvoke(_both("in"), config, durability=durability) + + live = await graph.ainvoke( + Command(resume="yes", goto=[Send("q", _both("unused"))]), + config, + durability=durability, + ) + + state = await graph.aget_state(config) + assert state.values["log"] == state.values["plain"] == live["log"], ( + f"'p' ran in the fan-out the resume replaced, but the reload reads " + f"{state.values['log']} against the live {live['log']}" + ) + + +def test_replay_interrupted_in_its_first_step_still_seals_the_fork( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + graph = _build_send_fan_out(sync_checkpointer) + config = _thread("t") + graph.invoke(_both("in"), config, durability=durability) + + graph.invoke(None, graph.get_state(config).config, durability=durability) + + state = graph.get_state(config) + assert state.values["log"] == state.values["plain"] == ["in", "p"], ( + f"the replay reran p, so the fork must not also replay the first p, " + f"but it reads {state.values['log']}" + ) + + +@NEEDS_CONTEXTVARS +async def test_areplay_interrupted_in_its_first_step_still_seals_the_fork( + async_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + graph = _build_send_fan_out(async_checkpointer) + config = _thread("t") + await graph.ainvoke(_both("in"), config, durability=durability) + + await graph.ainvoke( + None, (await graph.aget_state(config)).config, durability=durability + ) + + state = await graph.aget_state(config) + assert state.values["log"] == state.values["plain"] == ["in", "p"], ( + f"the replay reran p, so the fork must not also replay the first p, " + f"but it reads {state.values['log']}" + ) + + +def test_resume_addressed_at_an_interrupted_head_reruns_its_tasks_once( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + config = _thread("t") + graph = _build_parallel_interrupt(sync_checkpointer) + graph.invoke(_both("in-1"), config, durability=durability) + + graph.invoke( + Command(resume="yes"), graph.get_state(config).config, durability=durability + ) + + state = graph.get_state(config) + assert state.next == () + assert state.values["log"] == state.values["plain"] == ["in-1", "p"] + + +def test_update_state_with_the_head_checkpoint_id_keeps_a_deferred_node( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + graph = _build_deferred_after_interrupt(sync_checkpointer) + config = _thread("t") + graph.invoke(_both("in"), config) + + graph.update_state(graph.get_state(config).config, _both("u"), as_node="c") + graph.invoke(None, config) + + state = graph.get_state(config) + assert state.next == (), f"deferred node never ran, still pending: {state.next}" + assert state.values["log"] == state.values["plain"] == ["in", "a", "u", "b"] + + +async def test_aupdate_state_with_the_head_checkpoint_id_keeps_a_deferred_node( + async_checkpointer: BaseCheckpointSaver, +) -> None: + graph = _build_deferred_after_interrupt(async_checkpointer) + config = _thread("t") + await graph.ainvoke(_both("in"), config) + + await graph.aupdate_state( + (await graph.aget_state(config)).config, _both("u"), as_node="c" + ) + await graph.ainvoke(None, config) + + state = await graph.aget_state(config) + assert state.next == (), f"deferred node never ran, still pending: {state.next}" + assert state.values["log"] == state.values["plain"] == ["in", "a", "u", "b"] + + +def test_turns_addressed_at_the_head_store_no_snapshot( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + config = _thread("t") + graph = _build(sync_checkpointer, "turn") + graph.invoke(_both("in-1"), config) + for turn in range(2, 5): + graph.invoke(_both(f"in-{turn}"), graph.get_state(config).config) + + assert not _snapshotted_checkpoints(sync_checkpointer, config) + assert ( + graph.get_state(config).values["log"] == graph.get_state(config).values["plain"] + ) + + +def test_resume_that_reruns_an_error_handler_drops_its_stored_writes( + sync_checkpointer: BaseCheckpointSaver, durability: Durability +) -> None: + handled = threading.Event() + attempts = {"c": 0} + + def fails(state: _State) -> dict: + raise RuntimeError("f always fails") + + def handler(state: _State, error: NodeError) -> dict: + handled.set() + return _both("h") + + def flaky(state: _State) -> dict: + attempts["c"] += 1 + if attempts["c"] == 1: + assert handled.wait(5) + raise ValueError("c fails once") + return _both("c") + + graph = ( + StateGraph(_State) + .add_node("f", fails, error_handler=handler) + .add_node("c", flaky) + .add_edge(START, "f") + .add_edge(START, "c") + .compile(checkpointer=sync_checkpointer) + ) + config = _thread("t") + with pytest.raises(ValueError): + graph.invoke(_both("in"), config, durability=durability) + + live = graph.invoke(None, config, durability=durability) + + state = graph.get_state(config) + assert state.values["log"] == state.values["plain"] == live["log"] diff --git a/libs/langgraph/tests/test_delta_channel_supersteps_bound.py b/libs/langgraph/tests/test_delta_channel_supersteps_bound.py index a2c3a2776..098fe78f4 100644 --- a/libs/langgraph/tests/test_delta_channel_supersteps_bound.py +++ b/libs/langgraph/tests/test_delta_channel_supersteps_bound.py @@ -94,6 +94,28 @@ async def test_forced_snapshot_single_run() -> None: assert "seed-a" in state.values["a"] +async def test_supersteps_bound_skips_a_channel_never_written() -> None: + with patch( + "langgraph.pregel._checkpoint.DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT", + 3, + ): + saver = InMemorySaver() + graph = _build_two_channel_graph(saver, n_loops=4) + config = {"configurable": {"thread_id": "never-written"}} + + graph.invoke({"a": ["seed-a"]}, config) + + minted = [ + t.config["configurable"]["checkpoint_id"] + for t in saver.list(config) + if "b" in t.checkpoint["channel_versions"] + ] + assert not minted, ( + f"b was never written, but {len(minted)} checkpoints minted it a version" + ) + assert graph.get_state(config).values["b"] == [] + + async def test_forced_snapshot_accumulates_across_runs() -> None: """Supersteps counter for an unwritten channel persists across separate invoke() calls. After enough runs, the channel is force-snapshotted.""" @@ -139,13 +161,17 @@ async def test_predicate_fires_on_supersteps_overflow() -> None: channels = {"x": ch_instance} counters: dict[str, tuple[int, int]] = {"x": (0, 5000)} - result = delta_channels_to_snapshot(channels, counters) + result = delta_channels_to_snapshot(channels, counters, {"x": 1}) assert "x" in result counters_below: dict[str, tuple[int, int]] = {"x": (0, 4999)} - result2 = delta_channels_to_snapshot(channels, counters_below) + result2 = delta_channels_to_snapshot(channels, counters_below, {"x": 1}) assert "x" not in result2 + assert not delta_channels_to_snapshot(channels, counters, {}), ( + "a channel with no version was never written, so it has nothing to snapshot" + ) + async def test_counter_reset_after_supersteps_snapshot() -> None: """After the supersteps bound triggers a snapshot, the counters for diff --git a/libs/langgraph/tests/test_delta_channel_update_state.py b/libs/langgraph/tests/test_delta_channel_update_state.py index da6db40f6..62475deeb 100644 --- a/libs/langgraph/tests/test_delta_channel_update_state.py +++ b/libs/langgraph/tests/test_delta_channel_update_state.py @@ -113,6 +113,37 @@ def test_fresh_update_state_head_snapshots_delta_channel() -> None: assert "counters_since_delta_snapshot" not in head.metadata +def test_fresh_update_state_stores_nothing_for_a_delta_channel_it_did_not_write() -> ( + None +): + saver = InMemorySaver() + State = TypedDict( # type: ignore[call-overload] # noqa: UP013 + "State", + { + "messages": Annotated[list, DeltaChannel(_messages_delta_reducer)], + "notes": Annotated[list, DeltaChannel(_messages_delta_reducer)], + }, + ) + graph = ( + StateGraph(State) + .add_node("model", lambda state: {}) + .add_edge(START, "model") + .compile(checkpointer=saver) + ) + config = {"configurable": {"thread_id": "fresh-unwritten"}} + + graph.update_state( + config, + {"messages": [HumanMessage(content="hello", id="m1")]}, + as_node="model", + ) + + head = saver.get_tuple(config) + assert head is not None + assert "notes" not in head.checkpoint["channel_versions"] + assert graph.get_state(config).values["notes"] == [] + + # --------------------------------------------------------------------------- # Non-fresh thread: update_state after invoke # --------------------------------------------------------------------------- @@ -338,3 +369,29 @@ def test_state_history_chain_after_fresh_update_state_delta_channel() -> None: assert update_snapshot.metadata["step"] == 0 assert update_snapshot.parent_config is None assert [m.content for m in update_snapshot.values["messages"]] == ["hello"] + + +def test_update_state_that_snapshots_keeps_a_deferred_node_pending() -> None: + channel = DeltaChannel(_messages_delta_reducer, snapshot_frequency=1) + + class State(TypedDict): + messages: Annotated[list, channel] + + builder = StateGraph(State) + builder.add_node("a", lambda state: {"messages": [HumanMessage("a", id="a")]}) + builder.add_node( + "b", lambda state: {"messages": [HumanMessage("b", id="b")]}, defer=True + ) + builder.add_node("c", lambda state: {}) + builder.add_edge(START, "a") + builder.add_edge("a", "b") + builder.add_edge("a", "c") + graph = builder.compile(checkpointer=InMemorySaver(), interrupt_after=["a"]) + config = {"configurable": {"thread_id": "t"}} + graph.invoke({"messages": [HumanMessage("s", id="s")]}, config) + + graph.update_state(config, {"messages": [HumanMessage("u", id="u")]}, as_node="c") + final = graph.invoke(None, config) + + assert [m.content for m in final["messages"]] == ["s", "a", "u", "b"] + assert graph.get_state(config).next == () diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index 2e6661e04..bd76c5ea7 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -9410,6 +9410,192 @@ def test_fork_does_not_apply_pending_writes( assert result == {"value": 121} +def _extend(state: list, writes: list[list]) -> list: + return [*state, *(v for write in writes for v in write)] + + +class _IntVersionSaver(InMemorySaver): + """Integer versions tie exactly where `InMemorySaver`'s break at random.""" + + get_next_version = BaseCheckpointSaver.get_next_version + + +def _build_chain_after_a_delta_channel() -> Pregel: + return Pregel( + nodes={ + "a": NodeBuilder().subscribe_only("inp").do(lambda _: ["a"]).write_to("d"), + "b": NodeBuilder().subscribe_only("d").do(lambda _: "b").write_to("x"), + "c": NodeBuilder().subscribe_only("x").do(lambda _: "c").write_to("out"), + }, + channels={ + "inp": LastValue(str), + "d": DeltaChannel(_extend, snapshot_frequency=1), + "x": LastValue(str), + "out": LastValue(str), + }, + input_channels=["inp"], + output_channels=["out"], + checkpointer=_IntVersionSaver(), + ) + + +def test_update_state_after_an_exit_snapshot_infers_the_last_writer() -> None: + graph = _build_chain_after_a_delta_channel() + config = {"configurable": {"thread_id": "t"}} + graph.invoke({"inp": "go"}, config, durability="exit") + + graph.update_state(config, "u") + + values = graph.get_state(config).values + assert values["out"] == "u", ( + f"the update should apply as c, the last node to write, but state is {values}" + ) + + +async def test_aupdate_state_after_an_exit_snapshot_infers_the_last_writer() -> None: + graph = _build_chain_after_a_delta_channel() + config = {"configurable": {"thread_id": "t"}} + await graph.ainvoke({"inp": "go"}, config, durability="exit") + + await graph.aupdate_state(config, "u") + + values = (await graph.aget_state(config)).values + assert values["out"] == "u", ( + f"the update should apply as c, the last node to write, but state is {values}" + ) + + +def test_update_state_after_a_fork_seal_infers_the_subscriber_that_ran() -> None: + graph = Pregel( + nodes={ + "a": NodeBuilder() + .subscribe_only("inp") + .do(lambda _: ["a"]) + .write_to("d", go="go"), + "c": NodeBuilder().subscribe_only("go").do(lambda _: ["c"]).write_to("d"), + "b": NodeBuilder().subscribe_only("d").do(lambda _: "b").write_to("out"), + }, + channels={ + "inp": LastValue(str), + "go": EphemeralValue(str), + "d": DeltaChannel(_extend), + "out": LastValue(str), + }, + input_channels=["inp"], + output_channels=["out"], + checkpointer=_IntVersionSaver(), + ) + config = {"configurable": {"thread_id": "t"}} + graph.invoke({"inp": "go"}, config) + base = next(s for s in graph.get_state_history(config) if s.metadata["step"] == 0) + graph.invoke(None, graph.update_state(base.config, "u", as_node="b")) + + graph.update_state(config, "w") + + values = graph.get_state(config).values + assert values["out"] == "w", ( + f"the update should apply as b, the last node to run, but state is {values}" + ) + + +def test_update_state_after_a_fork_seal_infers_the_node_whose_read_it_advanced() -> ( + None +): + graph = Pregel( + nodes={ + "a": NodeBuilder().subscribe_only("inp").do(lambda _: ["a"]).write_to("d"), + "b": NodeBuilder().subscribe_only("d").do(lambda _: "b").write_to("out"), + }, + channels={ + "inp": LastValue(str), + "d": DeltaChannel(_extend), + "out": LastValue(str), + }, + input_channels=["inp"], + output_channels=["out"], + checkpointer=_IntVersionSaver(), + ) + config = {"configurable": {"thread_id": "t"}} + graph.invoke({"inp": "1"}, config) + graph.invoke({"inp": "2"}, config) + base = next(s for s in graph.get_state_history(config) if s.next == ("a",)) + fork = graph.update_state(base.config, "u", as_node="b") + + graph.update_state(fork, "w") + + values = graph.get_state(config).values + assert values["out"] == "w", ( + f"the update should apply as b, the last node to read, but state is {values}" + ) + + +def test_update_state_after_a_snapshotting_update_infers_the_last_writer() -> None: + graph = _build_chain_after_a_delta_channel() + config = {"configurable": {"thread_id": "t"}} + graph.update_state(config, ["u"], as_node="a") + graph.invoke(None, config, interrupt_after=["b"]) + + graph.update_state(config, "u") + + values = graph.get_state(config).values + assert values.get("x") == "u", ( + f"the update should apply as b, the last node to write, but state is {values}" + ) + + +async def test_aupdate_state_after_a_snapshotting_update_infers_the_last_writer() -> ( + None +): + graph = _build_chain_after_a_delta_channel() + config = {"configurable": {"thread_id": "t"}} + await graph.aupdate_state(config, ["u"], as_node="a") + await graph.ainvoke(None, config, interrupt_after=["b"]) + + await graph.aupdate_state(config, "u") + + values = (await graph.aget_state(config)).values + assert values.get("x") == "u", ( + f"the update should apply as b, the last node to write, but state is {values}" + ) + + +class _UpdatesOnlyState(TypedDict): + x: Annotated[list[str], operator.add] + + +def _graph_never_run() -> Any: + return ( + StateGraph(_UpdatesOnlyState) + .add_node("a", lambda _: {"x": ["a"]}) + .add_node("b", lambda _: {"x": ["b"]}) + .add_edge(START, "a") + .add_edge("a", "b") + .compile(checkpointer=InMemorySaver()) + ) + + +def test_update_state_on_a_thread_seeded_by_updates_applies_as_input() -> None: + graph = _graph_never_run() + config = {"configurable": {"thread_id": "t"}} + graph.update_state(config, {"x": ["u1"]}) + + graph.update_state(config, {"x": ["u2"]}) + + state = graph.get_state(config) + assert (state.values["x"], state.next) == (["u1", "u2"], ("a",)) + + +async def test_aupdate_state_on_a_thread_seeded_by_updates_applies_as_input() -> None: + graph = _graph_never_run() + config = {"configurable": {"thread_id": "t"}} + await graph.aupdate_state(config, {"x": ["u1"]}) + + await graph.aupdate_state(config, {"x": ["u2"]}) + + state = await graph.aget_state(config) + assert (state.values["x"], state.next) == (["u1", "u2"], ("a",)) + + async def test_delta_channel_end_to_end_inmemory() -> None: """Full graph run: DeltaChannel accumulates correctly across multiple turns."""