Compare commits

..
Author SHA1 Message Date
f1be024f5c perf: cache inspect.signature results in RunnableCallable.__init__
Avoids repeated signature introspection for the same function (e.g. 1000
ChannelWrite instances all inspecting the same _write/_awrite methods).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-27 17:08:39 +00:00
f61e9b45b6 perf: cache UntrackedValue isinstance scan in put_writes
The `put_writes` method was scanning all channels with
`any(isinstance(ch, UntrackedValue) ...)` on every call. For the
sequential_1000 benchmark this produced 1M+ isinstance calls through
the ABC machinery, consuming 40% of total runtime.

Cache the result as `_has_untracked_channels` once in __enter__ and
__aenter__, replacing both scan sites (put_writes and checkpoint
sanitization). This yields a 1.8-2.3x speedup on sequential_1000.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-27 17:08:11 +00:00
158 changed files with 3646 additions and 5885 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
run: run:
working-directory: libs/cli working-directory: libs/cli
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
if: github.event_name != 'workflow_dispatch' if: github.event_name != 'workflow_dispatch'
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
- "3.12" - "3.12"
name: "lint #${{ matrix.python-version }}" name: "lint #${{ matrix.python-version }}"
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
if: github.event_name != 'workflow_dispatch' if: github.event_name != 'workflow_dispatch'
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
env: env:
HAS_LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY != '' }} HAS_LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY != '' }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
@@ -33,7 +33,7 @@ jobs:
working-directory: libs/sdk-py working-directory: libs/sdk-py
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
name: "test #${{ matrix.python-version }}" name: "test #${{ matrix.python-version }}"
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
with: with:
@@ -33,7 +33,7 @@ jobs:
cache-suffix: test-${{ inputs.working-directory }} cache-suffix: test-${{ inputs.working-directory }}
working-directory: ${{ inputs.working-directory }} working-directory: ${{ inputs.working-directory }}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
working-directory: libs/langgraph working-directory: libs/langgraph
name: "test #${{ matrix.python-version }}" name: "test #${{ matrix.python-version }}"
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
with: with:
@@ -31,7 +31,7 @@ jobs:
cache-suffix: "test-langgraph" cache-suffix: "test-langgraph"
working-directory: libs/langgraph working-directory: libs/langgraph
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
+3 -3
View File
@@ -23,7 +23,7 @@ jobs:
version: ${{ steps.check-version.outputs.version }} version: ${{ steps.check-version.outputs.version }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python ${{ env.PYTHON_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }}
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
@@ -74,7 +74,7 @@ jobs:
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with: with:
@@ -82,7 +82,7 @@ jobs:
path: ${{ inputs.working-directory }}/dist/ path: ${{ inputs.working-directory }}/dist/
- name: Publish to test PyPI - name: Publish to test PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
with: with:
packages-dir: ${{ inputs.working-directory }}/dist/ packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true verbose: true
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
run: run:
working-directory: libs/langgraph working-directory: libs/langgraph
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- run: SHA=$(git rev-parse HEAD) && echo "SHA=$SHA" >> $GITHUB_ENV - run: SHA=$(git rev-parse HEAD) && echo "SHA=$SHA" >> $GITHUB_ENV
- name: Set up Python 3.11 - name: Set up Python 3.11
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
run: run:
working-directory: libs/langgraph working-directory: libs/langgraph
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- id: files - id: files
name: Get changed files name: Get changed files
uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0 uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0
+5 -5
View File
@@ -30,8 +30,8 @@ jobs:
deps: ${{ steps.filter.outputs.deps || 'true' }} deps: ${{ steps.filter.outputs.deps || 'true' }}
sdk_py: ${{ steps.filter.outputs.sdk_py || 'true' }} sdk_py: ${{ steps.filter.outputs.sdk_py || 'true' }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4 - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4
if: github.event_name != 'workflow_dispatch' if: github.event_name != 'workflow_dispatch'
id: filter id: filter
with: with:
@@ -112,9 +112,9 @@ jobs:
name: "Check SDK methods matching" name: "Check SDK methods matching"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with: with:
python-version: "3.11" python-version: "3.11"
- name: Run check_sdk_methods script - name: Run check_sdk_methods script
@@ -130,7 +130,7 @@ jobs:
python-version: python-version:
- "3.13" - "3.13"
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
with: with:
+2 -2
View File
@@ -26,10 +26,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with: with:
python-version: '3.11' python-version: '3.11'
+6 -6
View File
@@ -25,7 +25,7 @@ jobs:
tag: ${{ steps.check-version.outputs.tag }} tag: ${{ steps.check-version.outputs.tag }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
@@ -87,7 +87,7 @@ jobs:
outputs: outputs:
release-body: ${{ steps.generate-release-body.outputs.release-body }} release-body: ${{ steps.generate-release-body.outputs.release-body }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
repository: langchain-ai/langgraph repository: langchain-ai/langgraph
path: langgraph path: langgraph
@@ -158,7 +158,7 @@ jobs:
- test-pypi-publish - test-pypi-publish
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# We explicitly *don't* set up caching here. This ensures our tests are # We explicitly *don't* set up caching here. This ensures our tests are
# maximally sensitive to catching breakage. # maximally sensitive to catching breakage.
@@ -262,7 +262,7 @@ jobs:
working-directory: ${{ inputs.working-directory }} working-directory: ${{ inputs.working-directory }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
@@ -278,7 +278,7 @@ jobs:
path: ${{ inputs.working-directory }}/dist/ path: ${{ inputs.working-directory }}/dist/
- name: Publish package distributions to PyPI - name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
with: with:
packages-dir: ${{ inputs.working-directory }}/dist/ packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true verbose: true
@@ -304,7 +304,7 @@ jobs:
working-directory: ${{ inputs.working-directory }} working-directory: ${{ inputs.working-directory }}
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up uv - name: Set up uv
uses: ./.github/actions/uv_setup uses: ./.github/actions/uv_setup
-6
View File
@@ -76,12 +76,7 @@ __pypackages__/
# Environments # Environments
.env .env
.env.*
.envrc .envrc
*.crt
*.key
*.pem
credentials.json
.venv .venv
.venvs .venvs
env/ env/
@@ -103,7 +98,6 @@ dmypy.json
.vercel .vercel
.turbo .turbo
node_modules/
.editorconfig .editorconfig
.scratch .scratch
.worktrees/ .worktrees/
-8
View File
@@ -2,14 +2,6 @@
This repository is a monorepo. Each library lives in a subdirectory under `libs/`. This repository is a monorepo. Each library lives in a subdirectory under `libs/`.
<corridor>
## Corridor security analysis
When Corridor's `analyzePlan` tool is available, create a plan and use the tool to analyze it before generating or modifying code. Apply the resulting security guidance before writing code.
</corridor>
When you modify code in any library, run the following commands in that library's directory before creating a pull request: When you modify code in any library, run the following commands in that library's directory before creating a pull request:
- `make format` run code formatters - `make format` run code formatters
-8
View File
@@ -2,14 +2,6 @@
This repository is a monorepo. Each library lives in a subdirectory under `libs/`. This repository is a monorepo. Each library lives in a subdirectory under `libs/`.
<corridor>
## Corridor security analysis
Before generating or modifying code, create a plan and use Corridor's `analyzePlan` tool to analyze it. Apply the resulting security guidance before writing code.
</corridor>
When you modify code in any library, run the following commands in that library's directory before creating a pull request: When you modify code in any library, run the following commands in that library's directory before creating a pull request:
- `make format` run code formatters - `make format` run code formatters
@@ -6,12 +6,9 @@ import traceback
from collections.abc import Callable from collections.abc import Callable
from uuid import uuid4 from uuid import uuid4
from langgraph.checkpoint.base import BaseCheckpointSaver, Checkpoint from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.serde.types import _DeltaSnapshot
from langgraph.checkpoint.conformance.spec._delta_fixtures import build_delta_chain from langgraph.checkpoint.conformance.spec._delta_fixtures import build_delta_chain
from langgraph.checkpoint.conformance.test_utils import generate_metadata
async def test_history_returns_writes_oldest_first( async def test_history_returns_writes_oldest_first(
@@ -51,6 +48,8 @@ async def test_history_seed_is_nearest_snapshot(
result = await saver.aget_delta_channel_history(config=head, channels=["ch"]) result = await saver.aget_delta_channel_history(config=head, channels=["ch"])
assert "seed" in result["ch"], "Expected seed from snapshot at step 3" assert "seed" in result["ch"], "Expected seed from snapshot at step 3"
seed = result["ch"]["seed"] seed = result["ch"]["seed"]
from langgraph.checkpoint.serde.types import _DeltaSnapshot
actual_value = seed.value if isinstance(seed, _DeltaSnapshot) else seed actual_value = seed.value if isinstance(seed, _DeltaSnapshot) else seed
assert actual_value == 3, f"Expected seed value 3 (step 3), got {actual_value}" assert actual_value == 3, f"Expected seed value 3 (step 3), got {actual_value}"
writes = result["ch"]["writes"] writes = result["ch"]["writes"]
@@ -82,6 +81,11 @@ async def test_history_multi_channel(
tid = str(uuid4()) tid = str(uuid4())
configs: list = [] configs: list = []
parent_cfg = None parent_cfg = None
from langgraph.checkpoint.base import Checkpoint
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.serde.types import _DeltaSnapshot
from langgraph.checkpoint.conformance.test_utils import generate_metadata
for step in range(5): for step in range(5):
config = {"configurable": {"thread_id": tid, "checkpoint_ns": ""}} config = {"configurable": {"thread_id": tid, "checkpoint_ns": ""}}
@@ -157,6 +161,11 @@ async def test_history_migration_plain_value_as_seed(
channel_values[ch] (not a _DeltaSnapshot). The walk should treat it as the channel_values[ch] (not a _DeltaSnapshot). The walk should treat it as the
seed and terminate there. seed and terminate there.
""" """
from langgraph.checkpoint.base import Checkpoint
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.conformance.test_utils import generate_metadata
tid = str(uuid4()) tid = str(uuid4())
configs: list = [] configs: list = []
parent_cfg = None parent_cfg = None
@@ -199,74 +208,6 @@ async def test_history_migration_plain_value_as_seed(
assert values == [2], f"Expected [2], got {values}" assert values == [2], f"Expected [2], got {values}"
async def test_history_seed_ancestor_own_writes_are_replayed(
saver: BaseCheckpointSaver,
) -> None:
"""Writes stored AT the seed ancestor must be included in `writes`.
A stored value is the state ENTERING its checkpoint; the writes stored
under that same checkpoint are what produced its child and are therefore
NOT subsumed by it. Only writes at ancestors OLDER than the seed are
subsumed, and the walk terminates before reaching them.
This holds for plain-value seeds (migration from a pre-delta channel type)
exactly as it does for `_DeltaSnapshot` seeds. Skipping the seed
ancestor's own writes silently drops the first post-migration write.
"""
tid = str(uuid4())
configs: list = []
parent_cfg = None
# Each step's write is labelled by the role it plays, so the assertion
# below reads directly rather than by step index.
writes_by_step = {
0: "older-than-seed", # subsumed by the value stored at step 1
1: "at-seed", # the seed's own write, produced step 2
2: "after-seed", # delta-era write on the path to the head
3: "pending-at-head", # pending for the next step, never replayed
}
# Steps 0 and 1 store a plain value; 1 is the nearest, so it is the seed.
values_by_step = {0: [10], 1: [10, 20]}
for step in range(4):
config = {"configurable": {"thread_id": tid, "checkpoint_ns": ""}}
if parent_cfg:
config["configurable"]["checkpoint_id"] = parent_cfg["configurable"][
"checkpoint_id"
]
cv: dict = {}
cvs: dict = {}
if step in values_by_step:
cv["ch"] = values_by_step[step]
cvs["ch"] = step + 1
cp = Checkpoint(
v=1,
id=str(uuid6(clock_seq=-1)),
ts="",
channel_values=cv,
channel_versions=cvs,
versions_seen={},
updated_channels=None,
)
parent_cfg = await saver.aput(config, cp, generate_metadata(step=step), cvs)
configs.append(parent_cfg)
await saver.aput_writes(
parent_cfg, [("ch", writes_by_step[step])], str(uuid4())
)
head = configs[-1]
result = await saver.aget_delta_channel_history(config=head, channels=["ch"])
assert "seed" in result["ch"], "Expected seed from plain value at step 1"
assert result["ch"]["seed"] == [10, 20], (
f"Expected nearest plain value [10, 20], got {result['ch']['seed']}"
)
values = [w[2] for w in result["ch"]["writes"]]
assert values == ["at-seed", "after-seed"], (
f'Expected ["at-seed", "after-seed"], got {values}'
)
ALL_DELTA_CHANNEL_HISTORY_TESTS = [ ALL_DELTA_CHANNEL_HISTORY_TESTS = [
test_history_returns_writes_oldest_first, test_history_returns_writes_oldest_first,
test_history_seed_is_nearest_snapshot, test_history_seed_is_nearest_snapshot,
@@ -275,7 +216,6 @@ ALL_DELTA_CHANNEL_HISTORY_TESTS = [
test_history_empty_channels_returns_empty, test_history_empty_channels_returns_empty,
test_history_walk_to_root_no_seed, test_history_walk_to_root_no_seed,
test_history_migration_plain_value_as_seed, test_history_migration_plain_value_as_seed,
test_history_seed_ancestor_own_writes_are_replayed,
] ]
@@ -58,14 +58,8 @@ lint.select = [
"UP", # pyupgrade "UP", # pyupgrade
"B", # flake8-bugbear "B", # flake8-bugbear
"I", # isort "I", # isort
"PLC0415", # import-outside-top-level
"RUF100", # unused noqa directive
] ]
lint.ignore = ["E501", "B008"] lint.ignore = ["E501", "B008"]
# PLC0415 (import-outside-top-level) is enforced in tests only. Library code
# still has deferred imports that have not been reviewed, so it stays exempt
# for now.
lint.per-file-ignores = { "langgraph/**" = ["PLC0415"] }
target-version = "py310" target-version = "py310"
[tool.uv.sources] [tool.uv.sources]
+43 -43
View File
@@ -162,7 +162,7 @@ name = "exceptiongroup"
version = "1.3.1" version = "1.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" },
] ]
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" } 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 = [ wheels = [
@@ -247,7 +247,7 @@ wheels = [
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "1.5.2" version = "1.4.8"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jsonpatch" }, { name = "jsonpatch" },
@@ -260,9 +260,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "uuid-utils" }, { 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" } sdist = { url = "https://files.pythonhosted.org/packages/12/e3/bea6d0080acf183332f24dcd74c208aee5857cf8f783c3fb0bd86027d8fb/langchain_core-1.4.8.tar.gz", hash = "sha256:5bf1f8411077c904182ad8f975943d36adcbf579c4e017b3a118b719229ebf9a", size = 957974, upload-time = "2026-06-18T19:39:23.636Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/e4/024e402a65f5ee2eb6ae9667b5ffc5f08776cb4e712f5a11ee1997d56083/langchain_core-1.5.2-py3-none-any.whl", hash = "sha256:a687dd7c3b22c6c1294e1c1eeb61fb6f3a308e6015a1d75b576b3836ad5b5aed", size = 561643, upload-time = "2026-07-28T16:38:36.38Z" }, { url = "https://files.pythonhosted.org/packages/13/d6/bdf6f0481cc57ef300d6b1eb48cf1400c0409be715d6eb3cabadd1142a09/langchain_core-1.4.8-py3-none-any.whl", hash = "sha256:d84c28b05e3ba8d4271d0827aad5b592ccdaaf986e76768c23503f0a2045e8aa", size = 557416, upload-time = "2026-06-18T19:39:21.902Z" },
] ]
[[package]] [[package]]
@@ -279,7 +279,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph-checkpoint" name = "langgraph-checkpoint"
version = "4.2.0" version = "4.1.1"
source = { editable = "../checkpoint" } source = { editable = "../checkpoint" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -809,27 +809,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.16.5" version = "0.15.20"
source = { registry = "https://pypi.org/simple" } 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/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" }
wheels = [ 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/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" },
{ 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/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" },
{ 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/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" },
{ 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/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" },
{ 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/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" },
{ 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/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" },
{ 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/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" },
{ 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/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" },
{ 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/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" },
{ 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/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" },
{ 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/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" },
{ 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/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" },
{ 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/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" },
{ 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/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" },
{ 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/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" },
{ 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/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" },
{ 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/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
] ]
[[package]] [[package]]
@@ -897,27 +897,27 @@ wheels = [
[[package]] [[package]]
name = "ty" name = "ty"
version = "0.0.75" version = "0.0.55"
source = { registry = "https://pypi.org/simple" } 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/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" }
wheels = [ 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/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" },
{ 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/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" },
{ 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/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" },
{ 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/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" },
{ 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/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" },
{ 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/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" },
{ 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/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" },
{ 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/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" },
{ 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/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" },
{ 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/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" },
{ 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/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" },
{ 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/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" },
{ 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/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" },
{ 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/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" },
{ 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/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" },
{ 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/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" },
{ 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/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" },
] ]
[[package]] [[package]]
+32 -8
View File
@@ -67,12 +67,24 @@ with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
"v": 4, "v": 4,
"ts": "2024-07-31T20:14:19.804150+00:00", "ts": "2024-07-31T20:14:19.804150+00:00",
"id": "1ef4f797-8335-6428-8001-8a1503f9b875", "id": "1ef4f797-8335-6428-8001-8a1503f9b875",
"channel_values": {"my_key": "meow", "node": "node"}, "channel_values": {
"channel_versions": {"__start__": 2, "my_key": 3, "start:node": 3, "node": 3}, "my_key": "meow",
"node": "node"
},
"channel_versions": {
"__start__": 2,
"my_key": 3,
"start:node": 3,
"node": 3
},
"versions_seen": { "versions_seen": {
"__input__": {}, "__input__": {},
"__start__": {"__start__": 1}, "__start__": {
"node": {"start:node": 2}, "__start__": 1
},
"node": {
"start:node": 2
}
}, },
} }
@@ -96,12 +108,24 @@ async with AsyncPostgresSaver.from_conn_string(DB_URI) as checkpointer:
"v": 4, "v": 4,
"ts": "2024-07-31T20:14:19.804150+00:00", "ts": "2024-07-31T20:14:19.804150+00:00",
"id": "1ef4f797-8335-6428-8001-8a1503f9b875", "id": "1ef4f797-8335-6428-8001-8a1503f9b875",
"channel_values": {"my_key": "meow", "node": "node"}, "channel_values": {
"channel_versions": {"__start__": 2, "my_key": 3, "start:node": 3, "node": 3}, "my_key": "meow",
"node": "node"
},
"channel_versions": {
"__start__": 2,
"my_key": 3,
"start:node": 3,
"node": 3
},
"versions_seen": { "versions_seen": {
"__input__": {}, "__input__": {},
"__start__": {"__start__": 1}, "__start__": {
"node": {"start:node": 2}, "__start__": 1
},
"node": {
"start:node": 2
}
}, },
} }
@@ -223,7 +223,7 @@ class PostgresSaver(BasePostgresSaver):
>>> checkpoint_tuple = memory.get_tuple(config) >>> checkpoint_tuple = memory.get_tuple(config)
>>> print(checkpoint_tuple) >>> print(checkpoint_tuple)
CheckpointTuple(...) CheckpointTuple(...)
""" """ # noqa
thread_id = config["configurable"]["thread_id"] thread_id = config["configurable"]["thread_id"]
checkpoint_id = get_checkpoint_id(config) checkpoint_id = get_checkpoint_id(config)
checkpoint_ns = config["configurable"].get("checkpoint_ns", "") checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
@@ -478,11 +478,9 @@ class PostgresSaver(BasePostgresSaver):
stage1_sql = _build_delta_stage1_sql(channels, paged=True) stage1_sql = _build_delta_stage1_sql(channels, paged=True)
parent_of: dict[str, str | None] = {} parent_of: dict[str, str | None] = {}
ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels] ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels]
hb_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels] hs_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels]
inline_by_i_by_cid: list[dict[str, Any]] = [{} for _ in channels]
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels} chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels} seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels}
seed_inline_by_ch: dict[str, Any] = {}
walk_cursor_by_ch: dict[str, str | None] = {} walk_cursor_by_ch: dict[str, str | None] = {}
seeded: set[str] = set() seeded: set[str] = set()
cursor: str | None = None cursor: str | None = None
@@ -491,8 +489,7 @@ class PostgresSaver(BasePostgresSaver):
while True: while True:
stage1_params: list[Any] = [] stage1_params: list[Any] = []
for ch in channels: for ch in channels:
# ver_i, blob channel, blob version, inline_i stage1_params.extend([ch, ch])
stage1_params.extend([ch, ch, ch, ch])
stage1_params.extend( stage1_params.extend(
[thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE] [thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE]
) )
@@ -505,19 +502,16 @@ class PostgresSaver(BasePostgresSaver):
channels, channels,
parent_of, parent_of,
ver_by_i_by_cid, ver_by_i_by_cid,
hb_by_i_by_cid, hs_by_i_by_cid,
inline_by_i_by_cid,
) )
self._try_advance_walks( self._try_advance_walks(
checkpoint_id, checkpoint_id,
channels, channels,
parent_of, parent_of,
ver_by_i_by_cid, ver_by_i_by_cid,
hb_by_i_by_cid, hs_by_i_by_cid,
inline_by_i_by_cid,
chain_by_ch, chain_by_ch,
seed_ver_by_ch, seed_ver_by_ch,
seed_inline_by_ch,
walk_cursor_by_ch, walk_cursor_by_ch,
seeded, seeded,
) )
@@ -552,7 +546,6 @@ class PostgresSaver(BasePostgresSaver):
channels=channels, channels=channels,
chain_by_ch=chain_by_ch, chain_by_ch=chain_by_ch,
seed_ver_by_ch=seed_ver_by_ch, seed_ver_by_ch=seed_ver_by_ch,
seed_inline_by_ch=seed_inline_by_ch,
stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows), stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows),
) )
@@ -426,11 +426,9 @@ class AsyncPostgresSaver(BasePostgresSaver):
stage1_sql = _build_delta_stage1_sql(channels, paged=True) stage1_sql = _build_delta_stage1_sql(channels, paged=True)
parent_of: dict[str, str | None] = {} parent_of: dict[str, str | None] = {}
ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels] ver_by_i_by_cid: list[dict[str, str | None]] = [{} for _ in channels]
hb_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels] hs_by_i_by_cid: list[dict[str, bool]] = [{} for _ in channels]
inline_by_i_by_cid: list[dict[str, Any]] = [{} for _ in channels]
chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels} chain_by_ch: dict[str, list[str]] = {ch: [] for ch in channels}
seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels} seed_ver_by_ch: dict[str, str | None] = {ch: None for ch in channels}
seed_inline_by_ch: dict[str, Any] = {}
walk_cursor_by_ch: dict[str, str | None] = {} walk_cursor_by_ch: dict[str, str | None] = {}
seeded: set[str] = set() seeded: set[str] = set()
cursor: str | None = None cursor: str | None = None
@@ -439,8 +437,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
while True: while True:
stage1_params: list[Any] = [] stage1_params: list[Any] = []
for ch in channels: for ch in channels:
# ver_i, blob channel, blob version, inline_i stage1_params.extend([ch, ch])
stage1_params.extend([ch, ch, ch, ch])
stage1_params.extend( stage1_params.extend(
[thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE] [thread_id, checkpoint_ns, cursor, cursor, _DELTA_PAGE_SIZE]
) )
@@ -453,19 +450,16 @@ class AsyncPostgresSaver(BasePostgresSaver):
channels, channels,
parent_of, parent_of,
ver_by_i_by_cid, ver_by_i_by_cid,
hb_by_i_by_cid, hs_by_i_by_cid,
inline_by_i_by_cid,
) )
self._try_advance_walks( self._try_advance_walks(
checkpoint_id, checkpoint_id,
channels, channels,
parent_of, parent_of,
ver_by_i_by_cid, ver_by_i_by_cid,
hb_by_i_by_cid, hs_by_i_by_cid,
inline_by_i_by_cid,
chain_by_ch, chain_by_ch,
seed_ver_by_ch, seed_ver_by_ch,
seed_inline_by_ch,
walk_cursor_by_ch, walk_cursor_by_ch,
seeded, seeded,
) )
@@ -496,7 +490,6 @@ class AsyncPostgresSaver(BasePostgresSaver):
channels=channels, channels=channels,
chain_by_ch=chain_by_ch, chain_by_ch=chain_by_ch,
seed_ver_by_ch=seed_ver_by_ch, seed_ver_by_ch=seed_ver_by_ch,
seed_inline_by_ch=seed_inline_by_ch,
stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows), stage2_rows=cast("list[_DeltaStage2Row]", stage2_rows),
) )
@@ -580,7 +573,7 @@ class AsyncPostgresSaver(BasePostgresSaver):
while True: while True:
try: try:
yield asyncio.run_coroutine_threadsafe( yield asyncio.run_coroutine_threadsafe(
anext(aiter_), # type: ignore[arg-type] anext(aiter_), # type: ignore[arg-type] # noqa: F821
self.loop, self.loop,
).result() ).result()
except StopAsyncIteration: except StopAsyncIteration:
@@ -199,68 +199,27 @@ class _DeltaStage2Row(TypedDict, total=False):
def _build_delta_stage1_sql(channels: Sequence[str], *, paged: bool) -> str: def _build_delta_stage1_sql(channels: Sequence[str], *, paged: bool) -> str:
"""Build stage 1 SQL with K parallel version lookups + seed probes. """Build stage 1 SQL with 2K parallel JSONB key lookups.
For channels=["messages", "files"] (with `paged=True`) the result is:: For channels=["messages", "files"] (with `paged=True`) the result is::
SELECT checkpoint_id, parent_checkpoint_id, SELECT checkpoint_id, parent_checkpoint_id,
checkpoint -> 'channel_versions' ->> %s AS ver_0, checkpoint -> 'channel_versions' ->> %s AS ver_0,
EXISTS (SELECT 1 FROM checkpoint_blobs b0 (checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_0,
WHERE b0.thread_id = checkpoints.thread_id
AND b0.checkpoint_ns = checkpoints.checkpoint_ns
AND b0.channel = %s
AND b0.version = checkpoint -> 'channel_versions' ->> %s
AND b0.type <> 'empty') AS hb_0,
checkpoint -> 'channel_values' -> %s AS inline_0,
checkpoint -> 'channel_versions' ->> %s AS ver_1, checkpoint -> 'channel_versions' ->> %s AS ver_1,
EXISTS (...) AS hb_1, (checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_1
checkpoint -> 'channel_values' -> %s AS inline_1
FROM checkpoints FROM checkpoints
WHERE thread_id = %s AND checkpoint_ns = %s WHERE thread_id = %s AND checkpoint_ns = %s
AND (%s::text IS NULL OR checkpoint_id < %s) AND (%s::text IS NULL OR checkpoint_id < %s)
ORDER BY checkpoint_id DESC ORDER BY checkpoint_id DESC
LIMIT %s LIMIT %s
A stored value for a channel lives in one of two places, because `put`
splits them:
* **blob** — non-primitive values (and `_DeltaSnapshot`) are moved to
`checkpoint_blobs`. `hb_i` ("has blob") probes for one. The probe hits
that table's primary key `(thread_id, checkpoint_ns, channel, version)`
exactly, so it is an index lookup per row per channel.
* **inline** — `None`, `str`, `int`, `float` and `bool` stay in the
checkpoint's own `channel_values` and get no blob row at all. `inline_i`
returns that value.
Testing only for a key in `channel_values` (the previous approach) missed
blob-stored plain values, since `put` leaves an inline marker there for
`_DeltaSnapshot` but not for a plain value — which is what a thread
migrated from a pre-delta channel type leaves behind. Probing only the
blobs table would conversely miss inline primitives. Both are needed, and
the caller treats "either present" as the seed.
`hb_i` also disambiguates the two: for a `_DeltaSnapshot`, `inline_i` is the
literal `true` marker rather than the value, so a blob must win over an
inline reading whenever one exists. That ordering is what makes a genuine
inline `true` (a bool channel) distinguishable from the marker.
The `type <> 'empty'` predicate mirrors the check stage 2 already applies
when resolving the seed blob. `put` does not currently produce `empty` rows
on this path — `blob_versions` is filtered to keys present in
`channel_values`, so `_dump_blobs`' empty branch is unreachable from it —
but without the predicate the two stages could disagree: stage 1 would
terminate the walk on a row stage 2 then discards, yielding no seed *and* a
truncated write chain, which is the failure this function exists to avoid.
Channel names are passed as `%s` parameters (safe from SQL injection). Channel names are passed as `%s` parameters (safe from SQL injection).
Only the column aliases `ver_i` / `hb_i` / `inline_i` and the subquery alias Only the column aliases `ver_i` / `hs_i` are interpolated into the
`b{i}` are interpolated into the SQL string (i is bounded by len(channels) SQL string (i is bounded by len(channels) and uses safe identifiers).
and uses safe identifiers).
Caller must extend params with `[ch_0 x4, ch_1 x4, ..., thread_id, ns, Caller must extend params with `[ch_0, ch_0, ch_1, ch_1, ...,
cursor, cursor, page_size]` when `paged=True` — four per channel: the thread_id, ns, cursor, cursor, page_size]` when `paged=True`.
version lookup, the blob's channel, the version the blob must match, and the
inline lookup.
When `paged=False`, the WHERE has no cursor predicate and there's no When `paged=False`, the WHERE has no cursor predicate and there's no
LIMIT/ORDER BY — kept as a non-public helper for tests/diagnostics. LIMIT/ORDER BY — kept as a non-public helper for tests/diagnostics.
@@ -269,13 +228,7 @@ def _build_delta_stage1_sql(channels: Sequence[str], *, paged: bool) -> str:
for i in range(len(channels)): for i in range(len(channels)):
cols.append( cols.append(
f"checkpoint -> 'channel_versions' ->> %s AS ver_{i}, " f"checkpoint -> 'channel_versions' ->> %s AS ver_{i}, "
f"EXISTS (SELECT 1 FROM checkpoint_blobs b{i} " f"(checkpoint -> 'channel_values' -> %s) IS NOT NULL AS hs_{i}"
f"WHERE b{i}.thread_id = checkpoints.thread_id "
f"AND b{i}.checkpoint_ns = checkpoints.checkpoint_ns "
f"AND b{i}.channel = %s "
f"AND b{i}.version = checkpoint -> 'channel_versions' ->> %s "
f"AND b{i}.type <> 'empty') AS hb_{i}, "
f"checkpoint -> 'channel_values' -> %s AS inline_{i}"
) )
sql = ( sql = (
"SELECT checkpoint_id, parent_checkpoint_id, " "SELECT checkpoint_id, parent_checkpoint_id, "
@@ -389,8 +342,7 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
channels: Sequence[str], channels: Sequence[str],
parent_of: dict[str, str | None], parent_of: dict[str, str | None],
ver_by_i_by_cid: list[dict[str, str | None]], ver_by_i_by_cid: list[dict[str, str | None]],
hb_by_i_by_cid: list[dict[str, bool]], hs_by_i_by_cid: list[dict[str, bool]],
inline_by_i_by_cid: list[dict[str, Any]],
) -> str | None: ) -> str | None:
"""Fold one stage-1 page into the running walk-state mappings. """Fold one stage-1 page into the running walk-state mappings.
@@ -404,8 +356,7 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
parent_of[cid] = cast("str | None", r["parent_checkpoint_id"]) parent_of[cid] = cast("str | None", r["parent_checkpoint_id"])
for i in range(len(channels)): for i in range(len(channels)):
ver_by_i_by_cid[i][cid] = cast("str | None", r.get(f"ver_{i}")) ver_by_i_by_cid[i][cid] = cast("str | None", r.get(f"ver_{i}"))
hb_by_i_by_cid[i][cid] = bool(r.get(f"hb_{i}")) hs_by_i_by_cid[i][cid] = bool(r.get(f"hs_{i}"))
inline_by_i_by_cid[i][cid] = r.get(f"inline_{i}")
# Rows are DESC; the last one is the smallest cid in the page. # Rows are DESC; the last one is the smallest cid in the page.
oldest = cid oldest = cid
return oldest return oldest
@@ -416,11 +367,9 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
channels: Sequence[str], channels: Sequence[str],
parent_of: Mapping[str, str | None], parent_of: Mapping[str, str | None],
ver_by_i_by_cid: Sequence[Mapping[str, str | None]], ver_by_i_by_cid: Sequence[Mapping[str, str | None]],
hb_by_i_by_cid: Sequence[Mapping[str, bool]], hs_by_i_by_cid: Sequence[Mapping[str, bool]],
inline_by_i_by_cid: Sequence[Mapping[str, Any]],
chain_by_ch: dict[str, list[str]], chain_by_ch: dict[str, list[str]],
seed_ver_by_ch: dict[str, str | None], seed_ver_by_ch: dict[str, str | None],
seed_inline_by_ch: dict[str, Any],
walk_cursor_by_ch: dict[str, str | None], walk_cursor_by_ch: dict[str, str | None],
seeded: set[str], seeded: set[str],
) -> None: ) -> None:
@@ -428,15 +377,14 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
Uses the partial `parent_of` map accumulated so far. A walk stops Uses the partial `parent_of` map accumulated so far. A walk stops
either because: either because:
(a) it found a stored value for its channel — a blob or an inline (a) it found a snapshot for its channel (channel becomes seeded),
primitive (channel becomes seeded),
(b) it reached a real root (parent_of[cid] is None — fully (b) it reached a real root (parent_of[cid] is None — fully
materialized at this point), or materialized at this point), or
(c) the next ancestor cid isn't in `parent_of` yet (waiting for (c) the next ancestor cid isn't in `parent_of` yet (waiting for
a later page; the cursor stays put). a later page; the cursor stays put).
Mutates `chain_by_ch`, `seed_ver_by_ch`, `seed_inline_by_ch`, Mutates `chain_by_ch`, `seed_ver_by_ch`, `walk_cursor_by_ch`, and
`walk_cursor_by_ch`, and `seeded` in place. `seeded` in place.
""" """
for i, ch in enumerate(channels): for i, ch in enumerate(channels):
if ch in seeded: if ch in seeded:
@@ -446,22 +394,15 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
walk_cursor_by_ch[ch] = parent_of.get(target_id) walk_cursor_by_ch[ch] = parent_of.get(target_id)
cur_cid = walk_cursor_by_ch[ch] cur_cid = walk_cursor_by_ch[ch]
ch_chain = chain_by_ch[ch] ch_chain = chain_by_ch[ch]
hb_i = hb_by_i_by_cid[i] hs_i = hs_by_i_by_cid[i]
inline_i = inline_by_i_by_cid[i]
ver_i = ver_by_i_by_cid[i] ver_i = ver_by_i_by_cid[i]
while cur_cid is not None: while cur_cid is not None:
if cur_cid not in parent_of: if cur_cid not in parent_of:
# Need more pages to continue this walk. # Need more pages to continue this walk.
break break
ch_chain.append(cur_cid) ch_chain.append(cur_cid)
has_blob = hb_i.get(cur_cid, False) if hs_i.get(cur_cid, False):
inline = inline_i.get(cur_cid)
if has_blob or inline is not None:
# A blob wins: for a `_DeltaSnapshot` the inline reading is
# the `true` marker, not the value.
seed_ver_by_ch[ch] = ver_i.get(cur_cid) seed_ver_by_ch[ch] = ver_i.get(cur_cid)
if not has_blob:
seed_inline_by_ch[ch] = inline
seeded.add(ch) seeded.add(ch)
cur_cid = None cur_cid = None
break break
@@ -474,23 +415,16 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
channels: Sequence[str], channels: Sequence[str],
chain_by_ch: Mapping[str, list[str]], chain_by_ch: Mapping[str, list[str]],
seed_ver_by_ch: Mapping[str, str | None], seed_ver_by_ch: Mapping[str, str | None],
seed_inline_by_ch: Mapping[str, Any],
stage2_rows: Sequence[_DeltaStage2Row], stage2_rows: Sequence[_DeltaStage2Row],
) -> dict[str, DeltaChannelHistory]: ) -> dict[str, DeltaChannelHistory]:
"""Demux stage 2 rows per channel; produce per-channel histories. """Demux stage 2 rows per channel; produce per-channel histories.
stage2_rows carry `channel` on every row. We build per-channel stage2_rows carry `channel` on every row. We build per-channel
`writes_by_cid` and per-channel `seed_blob` dicts, then assemble `writes_by_cid` and per-channel `seed_blob` dicts, then assemble
a `DeltaChannelHistory` per requested channel. a `DeltaChannelHistory` per requested channel. The `seed` key is omitted
when the walk reached root with no snapshot found, or when the
A seed comes from the blobs table when the walk found one there, and seed blob is sentinel "empty" — in both cases the consumer treats
otherwise from `seed_inline_by_ch` — `put` keeps `None`, `str`, `int`, absence as "start empty".
`float` and `bool` values in the checkpoint's own `channel_values` with
no blob row, so those never appear in `stage2_rows`.
The `seed` key is omitted when the walk reached root without finding a
stored value, or when the seed blob is sentinel "empty" — in both cases
the consumer treats absence as "start empty".
""" """
# writes_by_ch_by_cid[channel][cid] = list of (type, blob, task_id, idx) # writes_by_ch_by_cid[channel][cid] = list of (type, blob, task_id, idx)
writes_by_ch_by_cid: dict[str, dict[str, list[tuple[str, bytes, str, int]]]] = { writes_by_ch_by_cid: dict[str, dict[str, list[tuple[str, bytes, str, int]]]] = {
@@ -539,10 +473,6 @@ class BasePostgresSaver(BaseCheckpointSaver[str]):
blob = seed_blob_by_ver.get((ch, seed_version)) blob = seed_blob_by_ver.get((ch, seed_version))
if blob is not None and blob[0] != "empty": if blob is not None and blob[0] != "empty":
entry["seed"] = self.serde.loads_typed(blob) entry["seed"] = self.serde.loads_typed(blob)
elif ch in seed_inline_by_ch:
# Inline primitive: stored in the checkpoint, not the blobs
# table, so stage 2 never returned a row for it.
entry["seed"] = seed_inline_by_ch[ch]
result[ch] = entry result[ch] = entry
return result return result
@@ -334,7 +334,7 @@ class ShallowPostgresSaver(BasePostgresSaver):
>>> checkpoint_tuple = memory.get_tuple(config) >>> checkpoint_tuple = memory.get_tuple(config)
>>> print(checkpoint_tuple) >>> print(checkpoint_tuple)
CheckpointTuple(...) CheckpointTuple(...)
""" """ # noqa
thread_id = config["configurable"]["thread_id"] thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "") checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
args = (thread_id, checkpoint_ns) args = (thread_id, checkpoint_ns)
@@ -885,7 +885,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver):
while True: while True:
try: try:
yield asyncio.run_coroutine_threadsafe( yield asyncio.run_coroutine_threadsafe(
anext(aiter_), # type: ignore[arg-type] anext(aiter_), # type: ignore[arg-type] # noqa: F821
self.loop, self.loop,
).result() ).result()
except StopAsyncIteration: except StopAsyncIteration:
@@ -4,10 +4,9 @@ import asyncio
import concurrent.futures import concurrent.futures
import json import json
import logging import logging
import re
import threading import threading
from collections import defaultdict from collections import defaultdict
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from collections.abc import Callable, Iterable, Iterator, Sequence
from contextlib import contextmanager from contextlib import contextmanager
from datetime import datetime from datetime import datetime
from typing import ( from typing import (
@@ -354,7 +353,7 @@ class BasePostgresStore(Generic[C]):
( (
_namespace_to_text(op.namespace), _namespace_to_text(op.namespace),
op.key, op.key,
Jsonb(dict(cast(Mapping[str, Any], op.value))), Jsonb(cast(dict, op.value)),
) )
) )
if op.ttl is not None: if op.ttl is not None:
@@ -464,9 +463,8 @@ class BasePostgresStore(Generic[C]):
ns_condition = "TRUE" ns_condition = "TRUE"
ns_param: Sequence[str] | None = None ns_param: Sequence[str] | None = None
if op.namespace_prefix: if op.namespace_prefix:
ns_condition, ns_param = _namespace_prefix_condition( ns_condition = "store.prefix LIKE %s"
op.namespace_prefix ns_param = (f"{_namespace_to_text(op.namespace_prefix)}%",)
)
else: else:
ns_param = () ns_param = ()
@@ -619,17 +617,15 @@ class BasePostgresStore(Generic[C]):
conditions.append("(expires_at IS NULL OR expires_at > NOW())") conditions.append("(expires_at IS NULL OR expires_at > NOW())")
if op.match_conditions: if op.match_conditions:
for condition in op.match_conditions: for condition in op.match_conditions:
if condition.match_type in ("prefix", "suffix"): if condition.match_type == "prefix":
if not condition.path: conditions.append("prefix LIKE %s")
# An empty path constrains nothing; skipping keeps it a
# no-op rather than emitting a pattern that matches no
# namespace at all.
continue
conditions.append("prefix ~ %s")
params.append( params.append(
_namespace_match_pattern( f"{_namespace_to_text(condition.path, handle_wildcards=True)}%"
condition.path, condition.match_type
) )
elif condition.match_type == "suffix":
conditions.append("prefix LIKE %s")
params.append(
f"%{_namespace_to_text(condition.path, handle_wildcards=True)}"
) )
else: else:
logger.warning( logger.warning(
@@ -1275,59 +1271,15 @@ def _get_index_params(store: Any) -> tuple[str, dict[str, Any]]:
return kind, sanitized return kind, sanitized
def _namespace_to_text(namespace: tuple[str, ...]) -> str: def _namespace_to_text(
namespace: tuple[str, ...], handle_wildcards: bool = False
) -> str:
"""Convert namespace tuple to text string.""" """Convert namespace tuple to text string."""
if handle_wildcards:
namespace = tuple("%" if val == "*" else val for val in namespace)
return ".".join(namespace) return ".".join(namespace)
def _escape_like_literal(text: str) -> str:
"""Escape LIKE metacharacters so `text` is matched literally.
Namespace labels may contain `_` and `%`, which would otherwise act as
wildcards: `("user_1",)` would match `("userX1",)`. Backslash is escaped
first so it cannot escape the following character. Requires an explicit
`ESCAPE '\\'` clause on the pattern.
"""
return text.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
def _namespace_prefix_condition(namespace_prefix: tuple[str, ...]) -> tuple[str, tuple]:
"""Build the SQL scoping a search to a namespace and its descendants.
Matches the namespace exactly or requires the `.` separator before any
remainder, so a prefix of `("foo",)` does not also match `("foobar",)`.
Both arms stay index-friendly: equality on the `(prefix, key)` primary key,
the anchored LIKE on the `prefix text_pattern_ops` index.
Only the LIKE arm is escaped -- equality does not interpret metacharacters,
so escaping it would stop `("user_1",)` from matching itself.
"""
path = _namespace_to_text(namespace_prefix)
condition = r"(store.prefix = %s OR store.prefix LIKE %s ESCAPE '\')"
return condition, (path, f"{_escape_like_literal(path)}.%")
def _namespace_match_pattern(path: tuple[str, ...], match_type: str) -> str:
"""Build a POSIX regex matching the dot-joined prefix on whole segments.
Needed because `LIKE` cannot express "any character except the separator".
Matches how `InMemoryStore` compares namespaces element-wise.
`*` matches exactly one segment. Prefix matches stay open-ended but must end
on a separator; suffix matches anchor at the end and begin on one.
Examples:
prefix ("uid", "*", "alice") -> ^uid\\.[^.]+\\.alice(\\.|\\Z)
suffix ("alice",) -> (^|\\.)alice\\Z
"""
segments = ("[^.]+" if part == "*" else re.escape(part) for part in path)
body = r"\.".join(segments)
if match_type == "suffix":
return rf"(^|\.){body}\Z"
return rf"^{body}(\.|\Z)"
def _row_to_item( def _row_to_item(
namespace: tuple[str, ...], namespace: tuple[str, ...],
row: Row, row: Row,
+1 -5
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "langgraph-checkpoint-postgres" name = "langgraph-checkpoint-postgres"
version = "3.1.2" version = "3.1.0"
description = "Library with a Postgres implementation of LangGraph checkpoint saver." description = "Library with a Postgres implementation of LangGraph checkpoint saver."
authors = [] authors = []
requires-python = ">=3.10" requires-python = ">=3.10"
@@ -32,7 +32,6 @@ test = [
"pytest-mock", "pytest-mock",
"psycopg[binary]", "psycopg[binary]",
"langgraph-checkpoint", "langgraph-checkpoint",
"langgraph-checkpoint-conformance",
"pytest-watcher", "pytest-watcher",
] ]
lint = [ lint = [
@@ -50,7 +49,6 @@ default-groups = ['dev']
[tool.uv.sources] [tool.uv.sources]
langgraph-checkpoint = { path = "../checkpoint", editable = true } langgraph-checkpoint = { path = "../checkpoint", editable = true }
langgraph-checkpoint-conformance = { path = "../checkpoint-conformance", editable = true }
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
include = ["langgraph"] include = ["langgraph"]
@@ -66,8 +64,6 @@ lint.select = [
"UP", # pyupgrade "UP", # pyupgrade
"B", # flake8-bugbear "B", # flake8-bugbear
"I", # isort "I", # isort
"PLC0415", # import-outside-top-level
"RUF100", # unused noqa directive
"UP", # pyupgrade "UP", # pyupgrade
] ]
lint.ignore = ["E501", "B008"] lint.ignore = ["E501", "B008"]
+6 -8
View File
@@ -380,15 +380,13 @@ async def test_delta_channel_chain_reconstruction(saver_name: str) -> None:
"langgraph.channels.delta", reason="langgraph core not installed" "langgraph.channels.delta", reason="langgraph core not installed"
) )
# Deferred on purpose: langgraph core is not a test dependency of this from typing import Annotated
# package, so these must stay behind the importorskip above.
from typing import Annotated # noqa: PLC0415
from langchain_core.messages import AIMessage, HumanMessage # noqa: PLC0415 from langchain_core.messages import AIMessage, HumanMessage
from langgraph.channels.delta import DeltaChannel # noqa: PLC0415 from langgraph.channels.delta import DeltaChannel
from langgraph.graph import START, StateGraph # noqa: PLC0415 from langgraph.graph import START, StateGraph
from langgraph.graph.message import _messages_delta_reducer # noqa: PLC0415 from langgraph.graph.message import _messages_delta_reducer
from typing_extensions import TypedDict # noqa: PLC0415 from typing_extensions import TypedDict
class State(TypedDict): class State(TypedDict):
messages: Annotated[list, DeltaChannel(_messages_delta_reducer)] messages: Annotated[list, DeltaChannel(_messages_delta_reducer)]
@@ -1,30 +0,0 @@
"""Run delta-channel conformance capabilities against AsyncPostgresSaver."""
from __future__ import annotations
import pytest
from langgraph.checkpoint.conformance import validate
from langgraph.checkpoint.conformance.initializer import checkpointer_test
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from tests.conftest import DEFAULT_URI
@pytest.mark.asyncio
async def test_delta_channel_conformance():
@checkpointer_test(name="AsyncPostgresSaver")
async def postgres_saver():
async with AsyncPostgresSaver.from_conn_string(DEFAULT_URI) as saver:
await saver.setup()
yield saver
report = await validate(
postgres_saver,
capabilities={
"delta_channel_history",
},
)
for cap, result in report.results.items():
if result.passed is False:
details = "\n".join(result.failures or [])
pytest.fail(f"Capability {cap} failed:\n{details}")
@@ -1,204 +0,0 @@
"""Seed detection for `DeltaChannel` histories on Postgres.
`put` splits stored values in two: primitives stay inline in the checkpoint's
`channel_values`, everything else moves to `checkpoint_blobs`. Only
`_DeltaSnapshot` leaves an inline marker behind when it moves, so the stage-1
walk has to check both places a blob probe alone misses inline primitives, and
an inline-key check alone missed blob-stored plain values, which is what a thread
migrated from a pre-delta channel type leaves behind. See #8534.
"""
from __future__ import annotations
from typing import Any
from uuid import uuid4
import pytest
from langgraph.checkpoint.base import Checkpoint, empty_checkpoint
from langgraph.checkpoint.base.id import uuid6
from langgraph.checkpoint.serde.types import _DeltaSnapshot
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from tests.conftest import DEFAULT_URI
CHANNEL = "items"
async def _build_chain(saver: AsyncPostgresSaver, seed_value: Any) -> tuple[str, dict]:
"""Store `seed_value` at step 1, then two steps that store nothing.
Every step carries a write so the walk has something to collect.
Returns `(thread_id, head_config)`.
"""
thread_id = str(uuid4())
parent: dict | None = None
for step in range(4):
config: dict = {"configurable": {"thread_id": thread_id, "checkpoint_ns": ""}}
if parent is not None:
config["configurable"]["checkpoint_id"] = parent["configurable"][
"checkpoint_id"
]
cp: Checkpoint = empty_checkpoint()
cp["id"] = str(uuid6(clock_seq=step))
new_versions: dict[str, Any] = {}
if step == 1:
cp["channel_values"][CHANNEL] = seed_value
cp["channel_versions"][CHANNEL] = "v1"
new_versions[CHANNEL] = "v1"
else:
cp["channel_versions"][CHANNEL] = f"v{step}"
parent = await saver.aput(
config, cp, {"source": "loop", "step": step, "parents": {}}, new_versions
)
await saver.aput_writes(parent, [(CHANNEL, f"w{step}")], str(uuid4()))
assert parent is not None
return thread_id, parent
@pytest.mark.asyncio
async def test_plain_value_seed_is_found() -> None:
"""A pre-delta plain value must be located as the seed.
Before #8534 the walk ran to the root and returned no seed, which happens
to reconstruct correctly for additive reducers while costing an
O(thread length) replay on every read.
"""
async with AsyncPostgresSaver.from_conn_string(DEFAULT_URI) as saver:
await saver.setup()
_, head = await _build_chain(saver, [10, 20])
result = await saver.aget_delta_channel_history(config=head, channels=[CHANNEL])
entry = result[CHANNEL]
assert entry.get("seed") == [10, 20], (
f"expected the plain value as seed, got {entry.get('seed', '<missing>')}"
)
# Only the writes between the seed and the head's parent replay: step 1
# (the seed's own) and step 2. Step 0 is older than the seed, step 3 is
# pending at the head.
assert [w[2] for w in entry["writes"]] == ["w1", "w2"]
@pytest.mark.asyncio
async def test_delta_snapshot_seed_is_found() -> None:
"""The `_DeltaSnapshot` path keeps working, so both seed kinds agree."""
async with AsyncPostgresSaver.from_conn_string(DEFAULT_URI) as saver:
await saver.setup()
_, head = await _build_chain(saver, _DeltaSnapshot([10, 20]))
result = await saver.aget_delta_channel_history(config=head, channels=[CHANNEL])
entry = result[CHANNEL]
seed = entry.get("seed")
assert isinstance(seed, _DeltaSnapshot), f"expected a snapshot, got {seed!r}"
assert seed.value == [10, 20]
assert [w[2] for w in entry["writes"]] == ["w1", "w2"]
@pytest.mark.asyncio
async def test_version_bump_without_a_value_does_not_hide_an_older_seed() -> None:
"""A delta-era step bumps `channel_versions` without storing a value, so no
blob exists for that version. The probe must report no seed there and keep
walking rather than stopping at a version it cannot resolve.
Step 0 holds the real value; step 1 bumps the version with nothing stored.
Walking back from the head has to pass step 1 to reach step 0.
"""
async with AsyncPostgresSaver.from_conn_string(DEFAULT_URI) as saver:
await saver.setup()
thread_id = str(uuid4())
parent: dict | None = None
for step in range(4):
config: dict = {
"configurable": {"thread_id": thread_id, "checkpoint_ns": ""}
}
if parent is not None:
config["configurable"]["checkpoint_id"] = parent["configurable"][
"checkpoint_id"
]
cp: Checkpoint = empty_checkpoint()
cp["id"] = str(uuid6(clock_seq=step))
new_versions: dict[str, Any] = {}
if step == 0:
cp["channel_values"][CHANNEL] = [10, 20]
cp["channel_versions"][CHANNEL] = "v0"
new_versions[CHANNEL] = "v0"
elif step == 1:
# Version bumped, value absent -> no blob row written.
cp["channel_versions"][CHANNEL] = "v1"
new_versions[CHANNEL] = "v1"
else:
cp["channel_versions"][CHANNEL] = "v1"
parent = await saver.aput(
config,
cp,
{"source": "loop", "step": step, "parents": {}},
new_versions,
)
await saver.aput_writes(parent, [(CHANNEL, f"w{step}")], str(uuid4()))
assert parent is not None
result = await saver.aget_delta_channel_history(
config=parent, channels=[CHANNEL]
)
entry = result[CHANNEL]
assert entry.get("seed") == [10, 20], (
"the walk stopped at the empty blob instead of reaching the real "
f"value at step 0; got {entry.get('seed', '<missing>')}"
)
assert [w[2] for w in entry["writes"]] == ["w0", "w1", "w2"]
@pytest.mark.asyncio
async def test_inline_primitive_seed_is_found() -> None:
"""`put` keeps `None`, `str`, `int`, `float` and `bool` in the checkpoint's
own `channel_values` with no blob row, so a blob probe alone cannot see
them. Stage 1 reads the inline value too and uses it when there is no blob.
"""
async with AsyncPostgresSaver.from_conn_string(DEFAULT_URI) as saver:
await saver.setup()
for seed_value in (42, "x", 3.5, None):
_, head = await _build_chain(saver, seed_value)
entry = (
await saver.aget_delta_channel_history(config=head, channels=[CHANNEL])
)[CHANNEL]
if seed_value is None:
# A JSON null is indistinguishable from "no value stored", so
# the walk keeps going; replay from empty is the correct result.
assert "seed" not in entry
else:
assert entry.get("seed") == seed_value, (
f"inline {type(seed_value).__name__} seed not found: "
f"{entry.get('seed', '<missing>')!r}"
)
assert [w[2] for w in entry["writes"]] == ["w1", "w2"]
@pytest.mark.asyncio
async def test_inline_true_is_not_read_as_a_snapshot_marker() -> None:
"""`put` inlines a literal `true` in `channel_values` as the marker for a
`_DeltaSnapshot`, which is also what a genuine `bool` channel holding
`True` looks like. A blob exists only in the snapshot case, so preferring
the blob keeps the two apart.
"""
async with AsyncPostgresSaver.from_conn_string(DEFAULT_URI) as saver:
await saver.setup()
_, head = await _build_chain(saver, True)
entry = (
await saver.aget_delta_channel_history(config=head, channels=[CHANNEL])
)[CHANNEL]
assert entry.get("seed") is True, (
f"a real inline True must survive, got {entry.get('seed', '<missing>')!r}"
)
_, snap_head = await _build_chain(saver, _DeltaSnapshot(True))
snap_entry = (
await saver.aget_delta_channel_history(config=snap_head, channels=[CHANNEL])
)[CHANNEL]
seed = snap_entry.get("seed")
assert isinstance(seed, _DeltaSnapshot), (
f"the marker must resolve to the blob, not inline true; got {seed!r}"
)
assert seed.value is True
@@ -20,10 +20,6 @@ from langgraph.store.base import (
from psycopg import Connection from psycopg import Connection
from langgraph.store.postgres import PostgresStore from langgraph.store.postgres import PostgresStore
from langgraph.store.postgres.base import (
_escape_like_literal,
_namespace_match_pattern,
)
from tests.conftest import ( from tests.conftest import (
DEFAULT_URI, DEFAULT_URI,
VECTOR_TYPES, VECTOR_TYPES,
@@ -330,127 +326,6 @@ def test_list_namespaces(store) -> None:
store.delete(namespace, "dummy") store.delete(namespace, "dummy")
def test_escape_like_literal() -> None:
assert _escape_like_literal("users.alice") == "users.alice"
assert _escape_like_literal("user_1") == r"user\_1"
assert _escape_like_literal("100%") == r"100\%"
assert _escape_like_literal("a\\b") == "a\\\\b"
assert _escape_like_literal("") == ""
def test_namespace_match_pattern() -> None:
assert _namespace_match_pattern(("foo",), "prefix") == r"^foo(\.|\Z)"
assert _namespace_match_pattern(("uid", "users"), "prefix") == r"^uid\.users(\.|\Z)"
assert (
_namespace_match_pattern(("uid", "*", "alice"), "prefix")
== r"^uid\.[^.]+\.alice(\.|\Z)"
)
assert _namespace_match_pattern(("alice",), "suffix") == r"(^|\.)alice\Z"
# Regex metacharacters in a label are quoted, not interpreted.
pattern = _namespace_match_pattern(("a.b+c",), "prefix")
assert re.match(pattern, "a.b+c.child")
assert not re.match(pattern, "axbbbc")
def test_search_namespace_segment_boundary(store) -> None:
"""Prefix scoping must stop at namespace segment boundaries.
Namespaces are stored dot-joined, so matching the raw text also returns
siblings sharing leading characters. Callers isolate tenants by namespace,
so prefix-shaped ids (1 vs 12) would cross-read.
"""
for namespace in [
("foo",),
("foo", "child"),
("foo", "child", "deep"),
("foobar",),
("foobar", "baz"),
("foo2",),
]:
store.put(namespace, "k", {"v": 1})
def _namespaces(prefix: tuple[str, ...]) -> set[tuple[str, ...]]:
return {item.namespace for item in store.search(prefix, limit=100)}
assert _namespaces(("foo",)) == {
("foo",),
("foo", "child"),
("foo", "child", "deep"),
}
# The sibling scope is independent, not merely narrower.
assert _namespaces(("foobar",)) == {("foobar",), ("foobar", "baz")}
assert _namespaces(("foo", "child")) == {("foo", "child"), ("foo", "child", "deep")}
assert _namespaces(("foo2",)) == {("foo2",)}
assert _namespaces(("fo",)) == set()
def test_search_empty_prefix_is_unconstrained(store) -> None:
"""An empty prefix constrains nothing and must return every namespace."""
for namespace in [("a",), ("b", "c"), ("d", "e", "f")]:
store.put(namespace, "k", {"v": 1})
assert {item.namespace for item in store.search((), limit=100)} == {
("a",),
("b", "c"),
("d", "e", "f"),
}
def test_search_namespace_like_metacharacters(store) -> None:
"""`_` and `%` are legal namespace labels, not LIKE wildcards."""
for namespace in [
("user_1",),
("user_1", "child"),
("userX1",),
("a%b",),
("axxb",),
]:
store.put(namespace, "k", {"v": 1})
def _namespaces(prefix: tuple[str, ...]) -> set[tuple[str, ...]]:
return {item.namespace for item in store.search(prefix, limit=100)}
# Also asserts the namespace still matches itself, which catches escaping
# the equality arm by mistake.
assert _namespaces(("user_1",)) == {("user_1",), ("user_1", "child")}
assert _namespaces(("a%b",)) == {("a%b",)}
def test_list_namespaces_segment_boundary(store) -> None:
for namespace in [
("foo",),
("foo", "child"),
("foobar",),
("foobar", "baz"),
("uid", "users", "alice"),
("uid", "users", "malice"),
("uid", "a", "b", "alice"),
]:
store.put(namespace, "k", {"v": 1})
assert set(store.list_namespaces(prefix=["foo"], limit=100)) == {
("foo",),
("foo", "child"),
}
# Suffix must align to a segment: "malice" does not end with the "alice"
# segment.
assert set(store.list_namespaces(suffix=["alice"], limit=100)) == {
("uid", "users", "alice"),
("uid", "a", "b", "alice"),
}
# "*" spans exactly one segment.
assert set(store.list_namespaces(prefix=["uid", "*", "alice"], limit=100)) == {
("uid", "users", "alice"),
}
# Prefix matching stays open-ended across depth.
assert set(store.list_namespaces(prefix=["uid"], limit=100)) == {
("uid", "users", "alice"),
("uid", "users", "malice"),
("uid", "a", "b", "alice"),
}
def test_search(store) -> None: def test_search(store) -> None:
# Create test data # Create test data
test_data = [ test_data = [
@@ -1151,16 +1026,3 @@ def test_non_ascii(
assert result3[0].key == "3" assert result3[0].key == "3"
assert result4[0].key == "4" assert result4[0].key == "4"
assert result5[0].key == "5" assert result5[0].key == "5"
def test_namespace_labels_with_trailing_newline(store) -> None:
"""Labels may contain newlines, and must not match a differently-named label."""
store.put(("users", "alice"), "k", {"v": 1})
store.put(("users", "alice\n"), "k", {"v": 2})
assert set(store.list_namespaces(suffix=["alice"], limit=100)) == {
("users", "alice"),
}
assert set(store.list_namespaces(prefix=["users", "alice"], limit=100)) == {
("users", "alice"),
}
+125 -147
View File
@@ -17,16 +17,16 @@ wheels = [
[[package]] [[package]]
name = "anyio" name = "anyio"
version = "4.14.2" version = "4.14.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "idna" }, { name = "idna" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" },
] ]
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/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" }
wheels = [ 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/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" },
] ]
[[package]] [[package]]
@@ -138,11 +138,11 @@ wheels = [
[[package]] [[package]]
name = "codespell" name = "codespell"
version = "2.4.3" version = "2.4.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/80/19/45e941380f69c042b43423513d201e6592346f992394347f5e7174c31407/codespell-2.4.3.tar.gz", hash = "sha256:cbe085e331227b37bb86ef8bddd08dc768c704ee9a07ca869852c093fa2793e2", size = 352773, upload-time = "2026-07-15T11:51:54.159Z" } sdist = { url = "https://files.pythonhosted.org/packages/2d/9d/1d0903dff693160f893ca6abcabad545088e7a2ee0a6deae7c24e958be69/codespell-2.4.2.tar.gz", hash = "sha256:3c33be9ae34543807f088aeb4832dfad8cb2dae38da61cac0a7045dd376cfdf3", size = 352058, upload-time = "2026-03-05T18:10:42.936Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/bf/bdb951d34eb169140b546f44be9ec4525d1acefb9eb5572071f5492b19fc/codespell-2.4.3-py3-none-any.whl", hash = "sha256:af2505b335e8573dbd2d384d1c4ef498f4006f4ba2d6fceca01e55b91f52628a", size = 340736, upload-time = "2026-07-15T11:51:52.925Z" }, { url = "https://files.pythonhosted.org/packages/42/a1/52fa05533e95fe45bcc09bcf8a503874b1c08f221a4e35608017e0938f55/codespell-2.4.2-py3-none-any.whl", hash = "sha256:97e0c1060cf46bd1d5db89a936c98db8c2b804e1fdd4b5c645e82a1ec6b1f886", size = 353715, upload-time = "2026-03-05T18:10:41.398Z" },
] ]
[[package]] [[package]]
@@ -159,7 +159,7 @@ name = "exceptiongroup"
version = "1.3.1" version = "1.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" },
] ]
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" } 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 = [ wheels = [
@@ -244,7 +244,7 @@ wheels = [
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "1.5.2" version = "1.4.8"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jsonpatch" }, { name = "jsonpatch" },
@@ -257,9 +257,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "uuid-utils" }, { 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" } sdist = { url = "https://files.pythonhosted.org/packages/12/e3/bea6d0080acf183332f24dcd74c208aee5857cf8f783c3fb0bd86027d8fb/langchain_core-1.4.8.tar.gz", hash = "sha256:5bf1f8411077c904182ad8f975943d36adcbf579c4e017b3a118b719229ebf9a", size = 957974, upload-time = "2026-06-18T19:39:23.636Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/e4/024e402a65f5ee2eb6ae9667b5ffc5f08776cb4e712f5a11ee1997d56083/langchain_core-1.5.2-py3-none-any.whl", hash = "sha256:a687dd7c3b22c6c1294e1c1eeb61fb6f3a308e6015a1d75b576b3836ad5b5aed", size = 561643, upload-time = "2026-07-28T16:38:36.38Z" }, { url = "https://files.pythonhosted.org/packages/13/d6/bdf6f0481cc57ef300d6b1eb48cf1400c0409be715d6eb3cabadd1142a09/langchain_core-1.4.8-py3-none-any.whl", hash = "sha256:d84c28b05e3ba8d4271d0827aad5b592ccdaaf986e76768c23503f0a2045e8aa", size = 557416, upload-time = "2026-06-18T19:39:21.902Z" },
] ]
[[package]] [[package]]
@@ -276,7 +276,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph-checkpoint" name = "langgraph-checkpoint"
version = "4.2.0" version = "4.1.1"
source = { editable = "../checkpoint" } source = { editable = "../checkpoint" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -322,36 +322,9 @@ test = [
{ name = "redis" }, { name = "redis" },
] ]
[[package]]
name = "langgraph-checkpoint-conformance"
version = "0.0.2"
source = { editable = "../checkpoint-conformance" }
dependencies = [
{ name = "langgraph-checkpoint" },
]
[package.metadata]
requires-dist = [{ name = "langgraph-checkpoint", editable = "../checkpoint" }]
[package.metadata.requires-dev]
dev = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "ruff" },
{ name = "ty" },
]
lint = [
{ name = "ruff" },
{ name = "ty" },
]
test = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
]
[[package]] [[package]]
name = "langgraph-checkpoint-postgres" name = "langgraph-checkpoint-postgres"
version = "3.1.2" version = "3.1.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint" },
@@ -365,7 +338,6 @@ dev = [
{ name = "anyio" }, { name = "anyio" },
{ name = "codespell" }, { name = "codespell" },
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint" },
{ name = "langgraph-checkpoint-conformance" },
{ name = "psycopg", extra = ["binary"] }, { name = "psycopg", extra = ["binary"] },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
@@ -382,7 +354,6 @@ lint = [
test = [ test = [
{ name = "anyio" }, { name = "anyio" },
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint" },
{ name = "langgraph-checkpoint-conformance" },
{ name = "psycopg", extra = ["binary"] }, { name = "psycopg", extra = ["binary"] },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
@@ -403,7 +374,6 @@ dev = [
{ name = "anyio" }, { name = "anyio" },
{ name = "codespell" }, { name = "codespell" },
{ name = "langgraph-checkpoint", editable = "../checkpoint" }, { name = "langgraph-checkpoint", editable = "../checkpoint" },
{ name = "langgraph-checkpoint-conformance", editable = "../checkpoint-conformance" },
{ name = "psycopg", extras = ["binary"] }, { name = "psycopg", extras = ["binary"] },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
@@ -420,7 +390,6 @@ lint = [
test = [ test = [
{ name = "anyio" }, { name = "anyio" },
{ name = "langgraph-checkpoint", editable = "../checkpoint" }, { name = "langgraph-checkpoint", editable = "../checkpoint" },
{ name = "langgraph-checkpoint-conformance", editable = "../checkpoint-conformance" },
{ name = "psycopg", extras = ["binary"] }, { name = "psycopg", extras = ["binary"] },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
@@ -451,74 +420,83 @@ wheels = [
[[package]] [[package]]
name = "orjson" name = "orjson"
version = "3.12.0" version = "3.11.9"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/742fb1f62b825f2c010697eaf4e828004bc2a81e7e806666989c132c7c42/orjson-3.12.0.tar.gz", hash = "sha256:d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5", size = 4142915, upload-time = "2026-08-14T16:13:30.607Z" } sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/35/819eeb4fa8ee676d38fdbb8213a76fd496f7dbbfdfafa89d34e02b22dfac/orjson-3.12.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:747843254519dd43b93eee3153a19e5a509334320c4d2f823ec879232db5c796", size = 224133, upload-time = "2026-08-14T16:12:00.607Z" }, { url = "https://files.pythonhosted.org/packages/10/5d/b95ca542a001135cc250a49370f282f578c8f4e46cc8617d73775297eea8/orjson-3.11.9-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:135869ef917b8704ea0a94e01620e0c05021c15c52036e4663baffe75e72f8ce", size = 228986, upload-time = "2026-05-06T15:09:14.765Z" },
{ url = "https://files.pythonhosted.org/packages/58/ab/d9221d4a2b085b073fcddc91728d490f20b9cf010c62c2f42371ab997695/orjson-3.12.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:7c2ad193c8004254f34b499f3bd2c80f043d10754aff2b38f93da574f4883f98", size = 113669, upload-time = "2026-08-14T16:12:02.126Z" }, { url = "https://files.pythonhosted.org/packages/80/01/be33fbff646e22f93398429ea645f20d2097aea1a6cdc1e6628e70125f83/orjson-3.11.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115ab5f5f4a0f203cc2a5f0fb09aee503a3f771aa08392949ab5ca230c4fbdbd", size = 132558, upload-time = "2026-05-06T15:09:17.431Z" },
{ url = "https://files.pythonhosted.org/packages/15/12/644cbbcabb26df61d9ef0c66e6f2bf8b687cc7b66137597f2858951f1952/orjson-3.12.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:bc7a872f03522d90e0429e6c0c5cd23084f767bedcb4c58048eec19294613344", size = 130410, upload-time = "2026-08-14T16:12:03.503Z" }, { url = "https://files.pythonhosted.org/packages/4e/61/73d49333bba660a075daccca10970dc6409ce1cf42ae4046646a19468aad/orjson-3.11.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4da3c38a2083ca4aaf9c2a36776cce3e9328e6647b10d118948f3cfb4913ffe4", size = 128213, upload-time = "2026-05-06T15:09:18.719Z" },
{ url = "https://files.pythonhosted.org/packages/14/6d/e3a8c34d687895aecd8b267a01c46106eb98d8424a83bfa7bacb723854f6/orjson-3.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18a87929f31d94a77f7dc93cf527e91f39ce7fe7813d588a4de2507efd32a387", size = 131101, upload-time = "2026-08-14T16:12:04.918Z" }, { url = "https://files.pythonhosted.org/packages/1f/7d/30e844b3dac3f74aed66b1f984daf9db3c98c0328c03d965a9e8dc06449e/orjson-3.11.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53b50b0e14084b8f7e29c5ce84c5af0f1160169b30d8a6914231d97d2fe297d4", size = 135430, upload-time = "2026-05-06T15:09:20.257Z" },
{ url = "https://files.pythonhosted.org/packages/75/20/930824c07685c22af23f26818ed3853b0270488a412b6ab757904b7f787b/orjson-3.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9683ee9ea0659da64f36574ef675b8a86330c34c19ea75db1fb93c3ff99e0ef", size = 131479, upload-time = "2026-08-14T16:12:06.11Z" }, { url = "https://files.pythonhosted.org/packages/16/64/bd815f5c610b3facc204f26ba94e87a9eb49b0d83de3d5fc1eee2402d91b/orjson-3.11.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:231742b4a11dad8d5380a435962c57e91b7c37b79be858f4ef1c0df1a259897e", size = 146178, upload-time = "2026-05-06T15:09:21.616Z" },
{ url = "https://files.pythonhosted.org/packages/4c/a6/22e863bbbe8917aa292e33e0db597000f9a07eb5e6f52efed623fa16bae1/orjson-3.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:103b5db66aa53c1f9e88c2524be4f383e831ba7dfd5f9f5af6336a177c622f11", size = 135865, upload-time = "2026-08-14T16:12:07.392Z" }, { url = "https://files.pythonhosted.org/packages/c7/35/e744fd36c79b339d27beb06068b5a08a8882ef5418804d0ce545a31f718d/orjson-3.11.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34fd2317602587321faab75ab76c623a0117e80841a6413654f04e47f339a8fb", size = 133068, upload-time = "2026-05-06T15:09:23.228Z" },
{ url = "https://files.pythonhosted.org/packages/50/a0/ceb5008914a65e9a19a46a09d94bc67a74d120209fdfa772750023ceb377/orjson-3.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd57d79aefa3f84eec851d6de7a366795b9345cfaf17f82b4820430a7a5fa241", size = 127843, upload-time = "2026-08-14T16:12:08.607Z" }, { url = "https://files.pythonhosted.org/packages/2a/56/d54152b67b63a0b3e556cfc549d6ce84f74d7f425ddeadc6c8a74d913da7/orjson-3.11.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f3db16e69b667b132e0f305a833d5497da302d801508cbb051ed9a9819da47", size = 134217, upload-time = "2026-05-06T15:09:24.847Z" },
{ url = "https://files.pythonhosted.org/packages/12/3d/61c6b3b84c250cb09cb7229701ff77e4d763773ad7f577d0b6abf2892664/orjson-3.12.0-cp310-cp310-win32.whl", hash = "sha256:3dbce9b6b3074b31a5d5dd322a9c4e5b16f206091ece4194c2e36952847a105e", size = 128293, upload-time = "2026-08-14T16:12:09.819Z" }, { url = "https://files.pythonhosted.org/packages/0b/ee/66154baf69f71c7164a268a5e888908aec5a0819d13c81d5e2755a257758/orjson-3.11.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0b34789fa0da61cf7bef0546b09c738fb195331e017e477096d129e9105ab03d", size = 141917, upload-time = "2026-05-06T15:09:26.647Z" },
{ url = "https://files.pythonhosted.org/packages/ce/0e/ea0f4a563253b6363195a4f704123c6bfbf156641bd3be5a75de81c5e917/orjson-3.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:3bb17a06f9bd15237b3216c044209fe92597379124018cfc196fbb846cde64df", size = 122216, upload-time = "2026-08-14T16:12:11.261Z" }, { url = "https://files.pythonhosted.org/packages/09/d3/c5824260ca8b9d7ba82648d042a3f8f4815d18c15bb98a1f30edd1bb2d83/orjson-3.11.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:87e4d4ab280b0c87424d47695bec2182caf8cfc17879ea78dab76680194abc13", size = 415356, upload-time = "2026-05-06T15:09:28.252Z" },
{ url = "https://files.pythonhosted.org/packages/75/1a/a7075a8e8b0d3f5097d17ac3099017104b6b7b42012041147995d5b2da05/orjson-3.12.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a94f0f0c6fcbb2b5bd9734c57a489c7584a732bbdf04a39e8c83b861e9d03e92", size = 223409, upload-time = "2026-08-14T16:12:12.654Z" }, { url = "https://files.pythonhosted.org/packages/64/cb/509c2e816fe4df641d93dc92f6a89adc8df3ada8ebdee2bd44aba3264c3c/orjson-3.11.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ace6c58523302d3b97b6ac5c38a5298a54b473762b6be82726b4265c41029f92", size = 148112, upload-time = "2026-05-06T15:09:29.783Z" },
{ url = "https://files.pythonhosted.org/packages/05/34/c2eb3b2900e5597db7841a4c6416ac2d90081bd956b02d4dd1833fa2b96b/orjson-3.12.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:a696529ec96a90d9a5f9570207efe403c8b08f8e4aa2783ee3403511e2fdfa10", size = 124015, upload-time = "2026-08-14T16:12:14.025Z" }, { url = "https://files.pythonhosted.org/packages/db/b5/3ceae56d2e4962979eedb023ba6a46a4bb65f333960379be0ca470686220/orjson-3.11.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:97d0d932803c1b164fde11cb542a9efcb1e0f63b184537cca65887147906ff48", size = 137112, upload-time = "2026-05-06T15:09:31.432Z" },
{ url = "https://files.pythonhosted.org/packages/1c/df/b49081766a75b6a37b3d33bdc0a39e492abab8441dd25e3e1998e7b83fcb/orjson-3.12.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:e4ac5059baab4b3acbd99485de019ff8cda0fdf34b61fa74f7197a53db78bfe8", size = 113471, upload-time = "2026-08-14T16:12:15.81Z" }, { url = "https://files.pythonhosted.org/packages/d7/7a/81fa3f2c7bef79b04cf2ab7838e5ac74b1f12511ceab979759b0275d6bb4/orjson-3.11.9-cp310-cp310-win32.whl", hash = "sha256:b3afcf569c15577a9fe64627292daa3e6b3a70f4fb77a5df246a87ec21681b94", size = 131706, upload-time = "2026-05-06T15:09:32.707Z" },
{ url = "https://files.pythonhosted.org/packages/48/d4/58ea28eeef95c2a27358ed927380a621162cf20bd740bbccf9c3f09a200a/orjson-3.12.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8e29957429c35bbb5a185a119c523aa2428b7bbf1a293724c7b9375ed8f892a3", size = 129998, upload-time = "2026-08-14T16:12:17.503Z" }, { url = "https://files.pythonhosted.org/packages/ae/d8/b64600f9083c7f151ad39717a5877fccbeb0ef6d7efcb55f971ce00b6bee/orjson-3.11.9-cp310-cp310-win_amd64.whl", hash = "sha256:8697ab6a080a5c46edaad50e2bc5bd8c7ca5c66442d24104fa44ec74910a8244", size = 127282, upload-time = "2026-05-06T15:09:33.955Z" },
{ url = "https://files.pythonhosted.org/packages/e2/f4/1e82aa2efc9916422d804697876ce433c907a1abd7c7e5c6d3d48565e5f9/orjson-3.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dce0166feb0a737ab84f598c9a338cbc0b764a036617aa686194f53c7eba0c3e", size = 130891, upload-time = "2026-08-14T16:12:18.762Z" }, { url = "https://files.pythonhosted.org/packages/1e/51/3fb9e65ae76ee97bd611869a503fa3fc0a6e81dd8b737cf3003f682df7ff/orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f", size = 228522, upload-time = "2026-05-06T15:09:35.362Z" },
{ url = "https://files.pythonhosted.org/packages/5b/e1/15169e9d22b59a406264f99d6db387c0b0b12b6357a8a0169917c2a713eb/orjson-3.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9caf3d09f47c3c70c4451ada20ef9bc4a4cdffa26f49862cf0a253b329aae2d5", size = 131285, upload-time = "2026-08-14T16:12:20.251Z" }, { url = "https://files.pythonhosted.org/packages/16/fa/9d54b07cb3f3b0bfd57841478e42d7a0ece4a9f49f9907eecf5a45461687/orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c", size = 128463, upload-time = "2026-05-06T15:09:37.063Z" },
{ url = "https://files.pythonhosted.org/packages/a4/3a/763dbd426290d044ec3e615a05e70adb6d8b6f95bf17dc355c0081a5e8b6/orjson-3.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9dca132b1fda5565088e65a6b6e742285e0aeceb6fae549fa8863e16c7d3998", size = 135707, upload-time = "2026-08-14T16:12:21.652Z" }, { url = "https://files.pythonhosted.org/packages/88/b1/6ceafc2eefd0a553e3be77ce6c49d107e772485d9568629376171c50e634/orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5", size = 132306, upload-time = "2026-05-06T15:09:38.299Z" },
{ url = "https://files.pythonhosted.org/packages/04/d1/3b2038ed168d22e14182ed715d6963f9c073a83a2ba43cfe918a4fc43c64/orjson-3.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a791f793b287bbc135b8e87c34e35c8bfc693e2a8a620fab1ae682b925f9a32e", size = 127669, upload-time = "2026-08-14T16:12:22.926Z" }, { url = "https://files.pythonhosted.org/packages/ea/76/f11311285324a40aab1e3031385c50b635a7cd0734fdaf60c7e89a696f60/orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c", size = 127988, upload-time = "2026-05-06T15:09:39.597Z" },
{ url = "https://files.pythonhosted.org/packages/88/ae/b84b3d3e65f5629ada0edcb1d2bccc55d7c5f89d8b981537ecdc3d6f31ec/orjson-3.12.0-cp311-cp311-win32.whl", hash = "sha256:31ed278a36304390adc3eec5d7f6fd593a7c3e99e5a06cd07866396c4b1b4710", size = 128043, upload-time = "2026-08-14T16:12:24.367Z" }, { url = "https://files.pythonhosted.org/packages/9e/85/0ef63bcf1337f44031ce9b91b1919563f62a37527b3ea4368bb15a22e5d7/orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd", size = 135188, upload-time = "2026-05-06T15:09:40.957Z" },
{ url = "https://files.pythonhosted.org/packages/35/24/2ed0e6f51ea3d0af45d807233a851175af75bec83ef5fd0d6a2601904ec0/orjson-3.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb2539159dfe8d371914f354360fa50e4a577cc89222a3828b9650a5e5040252", size = 122084, upload-time = "2026-08-14T16:12:25.813Z" }, { url = "https://files.pythonhosted.org/packages/05/94/b0d27090ea8a2095db3c2bd1b1c96f96f19bbb494d7fef33130e846e613d/orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62", size = 145937, upload-time = "2026-05-06T15:09:42.249Z" },
{ url = "https://files.pythonhosted.org/packages/21/dd/95d25fcfbc9471799ef6bb01c552d64ee5cde93ee40ba2f423dd3442c708/orjson-3.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:61318b6de893c7a9d9f3e5ecbadccbfc26a7eb417ccc7bbf0771de3b4d72f868", size = 127035, upload-time = "2026-08-14T16:12:27.201Z" }, { url = "https://files.pythonhosted.org/packages/09/eb/75d50c29c05b8054013e221e598820a365c8e64065312e75e202ed880709/orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877", size = 132758, upload-time = "2026-05-06T15:09:43.945Z" },
{ url = "https://files.pythonhosted.org/packages/be/4a/295da39c651c2faac8bd351a2a346f0fdedd9d50b847ee9dfc27d2207ef6/orjson-3.12.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:aa3e43a6846e91d7bde3d5a9c66090fcd8744f569a9b6cffc5e1ca38f6a461c0", size = 223427, upload-time = "2026-08-14T16:12:28.525Z" }, { url = "https://files.pythonhosted.org/packages/49/bd/360686f39348aa88827cb6fbf7dc606fd41c831a35235e1abf1db8e3a9e6/orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1", size = 133971, upload-time = "2026-05-06T15:09:45.239Z" },
{ url = "https://files.pythonhosted.org/packages/29/98/758cf90fbeaaafb7f8141bfac75a432099959f3a2f5db93a412e876415d8/orjson-3.12.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:11edb4660a6680abee9788a3a9072208a2c96538cc1322bd79542065229d8e54", size = 123725, upload-time = "2026-08-14T16:12:30.013Z" }, { url = "https://files.pythonhosted.org/packages/0e/30/3178eb16f3221aeef068b6f1f1ebe05f656ea5c6dffe9f6c917329fe17a3/orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd", size = 141685, upload-time = "2026-05-06T15:09:46.858Z" },
{ url = "https://files.pythonhosted.org/packages/32/b5/5b934d251f8651f7e41df180ad0c57a6e1cabe15c7bd331638413a50ebc9/orjson-3.12.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:2d3a9da945a4d96ae758fdaaca56742e6b73b6fd554c5d8876f252a6dad70b83", size = 113375, upload-time = "2026-08-14T16:12:31.209Z" }, { url = "https://files.pythonhosted.org/packages/5f/f1/ff2f19ed0225f9680fafa42febca3570dd59444ebf190980738d376214c2/orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff", size = 415167, upload-time = "2026-05-06T15:09:48.312Z" },
{ url = "https://files.pythonhosted.org/packages/cd/d2/37efb5b12a176ce3ced29f4144f20da57d02757f78ce549637dc1b4e1fc8/orjson-3.12.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:92ffc09e07233a6ab6d4e067f7841edcbcc134cb4812155cf171ea5255a421d7", size = 129983, upload-time = "2026-08-14T16:12:32.721Z" }, { url = "https://files.pythonhosted.org/packages/9b/61/863bddf0da6e9e586765414debd54b4e58db05f560902b6d00658cb88636/orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980", size = 147913, upload-time = "2026-05-06T15:09:49.733Z" },
{ url = "https://files.pythonhosted.org/packages/50/22/0644b87c73f13e0092df8f35a1fe280d991e5e90072087411e0dd7e44e0c/orjson-3.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf44e374aadde77b1f6109f1030be51433eb61984379852766b6f4e187db7b1e", size = 130629, upload-time = "2026-08-14T16:12:34.084Z" }, { url = "https://files.pythonhosted.org/packages/b6/8a/4081492586d75b073d60c5271a8d0f05a0955cabf1e34c8473f6fcd84235/orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2", size = 136959, upload-time = "2026-05-06T15:09:51.311Z" },
{ url = "https://files.pythonhosted.org/packages/8c/57/80b986ebfecd9c6a177ddf1c2319717f0cd8feffb2b78946595a18a2fc88/orjson-3.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b", size = 131245, upload-time = "2026-08-14T16:12:35.713Z" }, { url = "https://files.pythonhosted.org/packages/0d/bd/70b6ab193594d7abb875320c0a7c8335e846f28968c432c31042409c3c8d/orjson-3.11.9-cp311-cp311-win32.whl", hash = "sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180", size = 131533, upload-time = "2026-05-06T15:09:52.637Z" },
{ url = "https://files.pythonhosted.org/packages/80/3d/75c5ac5a69161f44492a68fbdde66f4cc4ce48cd5e1fb05918e46f0c8848/orjson-3.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:53c0c474a9d9aff9aebfc0c88de1f28f843d940e6e3a80729abdf6a20274356f", size = 135397, upload-time = "2026-08-14T16:12:37.128Z" }, { url = "https://files.pythonhosted.org/packages/3f/17/1a1a228183d62d1b77e2c30d210f47dd4768b310ebe1607c63e3c0e3a71e/orjson-3.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02", size = 127106, upload-time = "2026-05-06T15:09:54.204Z" },
{ url = "https://files.pythonhosted.org/packages/71/93/4d71f2df314a97ff0d27a4559bf5888fc8406e3c6dec90e92291e3511215/orjson-3.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873", size = 127693, upload-time = "2026-08-14T16:12:38.627Z" }, { url = "https://files.pythonhosted.org/packages/b8/95/285de5fa296d09681ee9c546cd4a8aeb773b701cf343dc125994f4d52953/orjson-3.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697", size = 126848, upload-time = "2026-05-06T15:09:55.551Z" },
{ url = "https://files.pythonhosted.org/packages/bc/1d/0dbc6be5adfd1730491072fb60beb6bcdf5d7b2596ee41b7fc2e298bfc09/orjson-3.12.0-cp312-cp312-win32.whl", hash = "sha256:a6cf4b18e7de173f209f2084ffbd736dd72389a396326ee80a7022168be232e5", size = 128000, upload-time = "2026-08-14T16:12:39.954Z" }, { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" },
{ url = "https://files.pythonhosted.org/packages/2d/c9/97b1ce0112ebf5e949c775ed5b1755e562233179f3584579673cc24d6378/orjson-3.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:010811c1b69773450a01cef97727a67b223242f350b77d4ca000e59a9ef2155a", size = 122106, upload-time = "2026-08-14T16:12:41.324Z" }, { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" },
{ url = "https://files.pythonhosted.org/packages/a8/6a/facd8b312e4a0d3a7fa978c7e15821f74a336adf1d65529faec33b48e18b/orjson-3.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:ad29eece0c601737f2a60edc2752a84e7a0785df3efb62e3012834700a5afe0d", size = 126869, upload-time = "2026-08-14T16:12:42.651Z" }, { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" },
{ url = "https://files.pythonhosted.org/packages/54/cb/d7b78218a987eb8a8ce4eeae0286b1bb679333eb631ea0eeaf6371680bfc/orjson-3.12.0-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a36ec60f1796f9a3f13e3b98390295e17a1c7c10155b448d264098bf9ee5900", size = 223397, upload-time = "2026-08-14T16:12:44.003Z" }, { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" },
{ url = "https://files.pythonhosted.org/packages/f8/4a/bc87c45e7ec639d35ebefd62618e01939531ac8e171426606a01bda05914/orjson-3.12.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ad0422b92d5195443a39f80c3bcf731cc2e00f153bd32063a47b73b057bd0f03", size = 123662, upload-time = "2026-08-14T16:12:45.433Z" }, { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" },
{ url = "https://files.pythonhosted.org/packages/94/ee/c9a4ff3f2dbedbbe9e635d0fa72c8866adede09b6335ef9644f53752f0d8/orjson-3.12.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:5a0fdbc216388f653d3752ff310e710f59253bd4ed6a2bfb3f4f06b84714bbd8", size = 113374, upload-time = "2026-08-14T16:12:46.755Z" }, { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" },
{ url = "https://files.pythonhosted.org/packages/75/09/3f330a026a796c8b4c97a6f429652a5e912e7065039bf96ed25e42aa7b25/orjson-3.12.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2eb5c56e534127b2b8fa38d2363c8b1b8190367ee0d1d16c041517d880843b94", size = 130029, upload-time = "2026-08-14T16:12:48.06Z" }, { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" },
{ url = "https://files.pythonhosted.org/packages/7d/40/094cc53126a3d22f76cdf83b6ea67338bed01d774037621a785aa8e6e5ea/orjson-3.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:784106539f4b9d4b930e0b4eb8d45168507dae001945e71b4675a367f1e5e806", size = 130528, upload-time = "2026-08-14T16:12:49.362Z" }, { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" },
{ url = "https://files.pythonhosted.org/packages/bc/74/89bb236deb9565f99434b13052bb40ddfcce4adf3afbfa3132ee7e421468/orjson-3.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c680706fc8396d95e7c4c1f9482563f552137aef91b57237a3ad5aaf64629df", size = 131075, upload-time = "2026-08-14T16:12:50.692Z" }, { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" },
{ url = "https://files.pythonhosted.org/packages/0c/ac/1176360d762c01b5bd34acd56fc098e936c491363d8b6b397ad4aa475547/orjson-3.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:83445adc40cba26d6d621185a45128ce455b766af368cad2ab64b970603a7978", size = 135321, upload-time = "2026-08-14T16:12:52.114Z" }, { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" },
{ url = "https://files.pythonhosted.org/packages/7a/02/bbd881c8b9276d50b998de38b4e97de8ace1aac940b0ee545aedbf65ed00/orjson-3.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:644d005bc82f917337a95ce270c9f6f92f9834c2bed7b1477572f8db00784222", size = 127472, upload-time = "2026-08-14T16:12:53.517Z" }, { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" },
{ url = "https://files.pythonhosted.org/packages/8e/02/a0934d7503e6dcbedd6afac3e7f3f8597fd09389949ad94d0f7540e9dbca/orjson-3.12.0-cp313-cp313-win32.whl", hash = "sha256:d8e78d3d93705e3d27cc17cdb209e44d7a8ea203010cac6ce9c7ffc1ae1996f1", size = 128000, upload-time = "2026-08-14T16:12:55.14Z" }, { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" },
{ url = "https://files.pythonhosted.org/packages/52/87/69f98f8d40faff103a965a5fbb83f08241b01beaf92badb5413fbc9358cc/orjson-3.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b85931be5b6763c31283805c9bdaae1ca03ad9f6f12a15f1cbf6745b907932c2", size = 121841, upload-time = "2026-08-14T16:12:56.507Z" }, { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" },
{ url = "https://files.pythonhosted.org/packages/e6/07/b83046a4e3cadcc0987d0f160696107c4af706a619b56e4ad01940cadadf/orjson-3.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:6a31348d7dfa64cd9c78bd1f510ff44c48fe64d71094e6b90e364dba3b55949e", size = 126765, upload-time = "2026-08-14T16:12:57.806Z" }, { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" },
{ url = "https://files.pythonhosted.org/packages/12/9d/3931253e6f3148abf2cbe14830367042a4806b362ea520df2303db188fb9/orjson-3.12.0-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9e6fee342a48760e854d743e7a81534d8e2925a6f46e09f750cf56b50fd1de5d", size = 223391, upload-time = "2026-08-14T16:12:59.184Z" }, { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" },
{ url = "https://files.pythonhosted.org/packages/8a/0e/b4a4f1e305367245877b967a0bad70fcf001d77c54ac4339a120b66fdae4/orjson-3.12.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:8c3bb86dd10f39b3fbf434b7d5dc7cac77d6fc8ac572ae30a10731ede2c4b647", size = 123659, upload-time = "2026-08-14T16:13:00.548Z" }, { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" },
{ url = "https://files.pythonhosted.org/packages/96/f3/6782c6fa85e2702bc66be183c3b421486167dcf266ee4dc1403fe3824870/orjson-3.12.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:2bb3ce43203936072dd8b4917b01d3aecfc02329bfb42510cb7cfb24708adc9c", size = 113337, upload-time = "2026-08-14T16:13:02.009Z" }, { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" },
{ url = "https://files.pythonhosted.org/packages/bf/79/b32ab64bacda9d0fa4942ef483bd03cabf0eaf2be819ca9fb7ff610c559d/orjson-3.12.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:6a2a79c89984dc719817d388c8709e0efc2a2795a934eaa746b4882eb6045adc", size = 130112, upload-time = "2026-08-14T16:13:03.404Z" }, { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" },
{ url = "https://files.pythonhosted.org/packages/ee/49/6e6142999ca01509219be5e5a9c338a3e5ea011f63e91ff473fbbf3734ed/orjson-3.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f06dd838d1e07d9b1de0932ec0485ec92c4d5f5d1ad4817a656268c3e88be1e1", size = 130520, upload-time = "2026-08-14T16:13:04.798Z" }, { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" },
{ url = "https://files.pythonhosted.org/packages/49/d0/3745af0a4cc9867784f29722929cec4d10bd1c877cd754b01ba6d96eb21a/orjson-3.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6b11be792c3d2c6a4be2af4ebf97a68d0bf5f580aca6e86a418a354f6cc846a", size = 131053, upload-time = "2026-08-14T16:13:06.14Z" }, { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" },
{ url = "https://files.pythonhosted.org/packages/c3/f4/6fe5a22fa478fffb190e65c338c84df5c311ef597b363150a17cc57063c0/orjson-3.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:477ecaf6b9f88f873341b91fcc736119ca81b5e002a9f7f308ff5b4f2ce2a70e", size = 135321, upload-time = "2026-08-14T16:13:07.544Z" }, { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" },
{ url = "https://files.pythonhosted.org/packages/ff/41/b1b0ec30289646a81a76e2dbaae2686b96fcccb7cb0323dc1dd78cbc7875/orjson-3.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f3c0683136acdc29afdf88a5bc2f7d3d0e34087788d1d63c0144b805a87a196f", size = 127485, upload-time = "2026-08-14T16:13:08.88Z" }, { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" },
{ url = "https://files.pythonhosted.org/packages/bf/2b/277404bdcc21c93b112b963655b76443ebfe828f8a3ff1de7d90f8850eb3/orjson-3.12.0-cp314-cp314-win32.whl", hash = "sha256:d39f3f5c3927e2dc0913fe5bbc1a2f6b1b9d1bba1de6358340d0ad0d0c00ca92", size = 128048, upload-time = "2026-08-14T16:13:10.305Z" }, { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" },
{ url = "https://files.pythonhosted.org/packages/41/2b/395b36fa2b4ce7af70b651d715e88f80d884b2c2b14a6b53e84d554fb5f0/orjson-3.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:0b1ac5bf6609b2716c7954011c5fef6254922df029f45d032ee4ebf5d363cbed", size = 121858, upload-time = "2026-08-14T16:13:11.634Z" }, { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" },
{ url = "https://files.pythonhosted.org/packages/ea/a3/833e895ff452859eebe75093d26691fe9108f1a7a6a08435d7a5780ea652/orjson-3.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:50fae885cb073eac7556353ff3df93312b0d5137b0a5056b2bb63f97ed9a93c7", size = 126749, upload-time = "2026-08-14T16:13:13.117Z" }, { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" },
{ url = "https://files.pythonhosted.org/packages/58/64/99c8947ece10c17176af9aae85c4948f1d109da77440ec14d87239efaf73/orjson-3.12.0-cp315-cp315-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:01efac2074fffb4cb1ea3fab7861e9d0f2a26913854a972f5ac760525dbdaf6e", size = 223398, upload-time = "2026-08-14T16:13:14.694Z" }, { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" },
{ url = "https://files.pythonhosted.org/packages/3e/30/cf983fe09f2731420fda097a9f7ef4343f47fa216c228961ad8f6da44f3d/orjson-3.12.0-cp315-cp315-macosx_15_0_arm64.whl", hash = "sha256:ed4ca42bd55955aa34deedcfdfd0e0c31abf51143aae158ae2bc3520b626e517", size = 123655, upload-time = "2026-08-14T16:13:16.221Z" }, { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" },
{ url = "https://files.pythonhosted.org/packages/11/50/9cb8ae73fa4749dbbc20f617004213b5ff01c20aaeec34c3f31124f2c1d8/orjson-3.12.0-cp315-cp315-manylinux_2_39_aarch64.whl", hash = "sha256:40f92192227505acca4e2533ce565f8e6b9535f7d0d09b0968452f18b7376b38", size = 130515, upload-time = "2026-08-14T16:13:17.601Z" }, { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" },
{ url = "https://files.pythonhosted.org/packages/9f/0a/adb6ce1a5b5fbf9cb1790f9961bb668a0dd5429aadaf6cee044724681795/orjson-3.12.0-cp315-cp315-manylinux_2_39_armv7l.whl", hash = "sha256:33efefcf5d88eaf400b47e2eba02f91f319bb9951be61ca500b7d536d3f2079d", size = 113327, upload-time = "2026-08-14T16:13:18.927Z" }, { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" },
{ url = "https://files.pythonhosted.org/packages/51/5c/d17f61581d8dbdde7048f87a330fa24915edec38db4d72b381fec14fbb56/orjson-3.12.0-cp315-cp315-manylinux_2_39_i686.whl", hash = "sha256:8e386b0bc0ddd7cd2056f884b5a0af33592bd01ac66a7ca4b42a65a7e7774a13", size = 130105, upload-time = "2026-08-14T16:13:20.317Z" }, { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" },
{ url = "https://files.pythonhosted.org/packages/9f/b7/938befcf33bee4704a92ecec6a2731224c539d939bf9429fd39396d28931/orjson-3.12.0-cp315-cp315-manylinux_2_39_x86_64.whl", hash = "sha256:58c58e1de0006ffb580368d6793c36c7b0b021db066479cf281bf5061e732328", size = 131049, upload-time = "2026-08-14T16:13:21.719Z" }, { url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" },
{ url = "https://files.pythonhosted.org/packages/b0/15/cfa2021d64d5aa8bb5c9f604ef375e00ec8b657651b5dd650b1b7ad13df1/orjson-3.12.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:08231552159be266a7269555bd9f7c016aee7d9ad6dab06eb58796c5ccb7101c", size = 135320, upload-time = "2026-08-14T16:13:23.415Z" }, { url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" },
{ url = "https://files.pythonhosted.org/packages/1a/50/3e75dfe357c1e8f9e287c7a5740260ef15bd23a5299eae8d0835dcad5375/orjson-3.12.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:a15f9a891bce5f5cc5d210e3ad8614d4d1b489a56448c099d6d2a7168b2d954a", size = 127488, upload-time = "2026-08-14T16:13:24.791Z" }, { url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" },
{ url = "https://files.pythonhosted.org/packages/11/a6/79aed402eb3ab284dc5b4791a7ad62c5875127de01b8e3f04bd92d551298/orjson-3.12.0-cp315-cp315-win32.whl", hash = "sha256:03091c8a64db4be38746597ceea68f33c238e27acd9bfe99fb59420224ae7a55", size = 128048, upload-time = "2026-08-14T16:13:26.217Z" }, { url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" },
{ url = "https://files.pythonhosted.org/packages/64/f7/2723e264aab7248c1ed6ecaad8e5d0cb866c0cffde75442102ffa7491aba/orjson-3.12.0-cp315-cp315-win_amd64.whl", hash = "sha256:2b7bcefb9f40fa242fa6b06377232c048e655747790829609168c01162f60578", size = 121860, upload-time = "2026-08-14T16:13:27.577Z" }, { url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" },
{ url = "https://files.pythonhosted.org/packages/82/56/630c9113ec8996778f1f0304b364b091b9a9db5fef5fdc17cca622f5ea24/orjson-3.12.0-cp315-cp315-win_arm64.whl", hash = "sha256:859fc4196855890150bb08e649b30d2c93b249b3e3edd0d3bb2231abf8aa8adc", size = 126754, upload-time = "2026-08-14T16:13:28.962Z" }, { url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" },
{ url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" },
{ url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" },
{ url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" },
{ url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" },
{ url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" },
{ url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" },
{ url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" },
{ url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" },
{ url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" },
] ]
[[package]] [[package]]
@@ -979,27 +957,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.16.5" version = "0.15.20"
source = { registry = "https://pypi.org/simple" } 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/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" }
wheels = [ 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/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" },
{ 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/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" },
{ 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/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" },
{ 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/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" },
{ 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/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" },
{ 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/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" },
{ 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/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" },
{ 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/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" },
{ 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/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" },
{ 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/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" },
{ 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/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" },
{ 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/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" },
{ 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/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" },
{ 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/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" },
{ 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/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" },
{ 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/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" },
{ 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/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
] ]
[[package]] [[package]]
@@ -1067,27 +1045,27 @@ wheels = [
[[package]] [[package]]
name = "ty" name = "ty"
version = "0.0.75" version = "0.0.55"
source = { registry = "https://pypi.org/simple" } 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/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" }
wheels = [ 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/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" },
{ 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/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" },
{ 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/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" },
{ 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/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" },
{ 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/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" },
{ 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/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" },
{ 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/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" },
{ 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/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" },
{ 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/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" },
{ 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/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" },
{ 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/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" },
{ 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/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" },
{ 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/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" },
{ 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/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" },
{ 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/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" },
{ 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/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" },
{ 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/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" },
] ]
[[package]] [[package]]
@@ -77,7 +77,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
>>> result = graph.invoke(3, config) >>> result = graph.invoke(3, config)
>>> graph.get_state(config) >>> graph.get_state(config)
StateSnapshot(values=4, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '0c62ca34-ac19-445d-bbb0-5b4984975b2a'}}, parent_config=None) StateSnapshot(values=4, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '0c62ca34-ac19-445d-bbb0-5b4984975b2a'}}, parent_config=None)
""" """ # noqa
conn: sqlite3.Connection conn: sqlite3.Connection
is_setup: bool is_setup: bool
@@ -222,7 +222,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
>>> checkpoint_tuple = memory.get_tuple(config) >>> checkpoint_tuple = memory.get_tuple(config)
>>> print(checkpoint_tuple) >>> print(checkpoint_tuple)
CheckpointTuple(...) CheckpointTuple(...)
""" """ # noqa
checkpoint_ns = config["configurable"].get("checkpoint_ns", "") checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
with self.cursor(transaction=False) as cur: with self.cursor(transaction=False) as cur:
# find the latest checkpoint for the thread_id # find the latest checkpoint for the thread_id
@@ -212,7 +212,7 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
while True: while True:
try: try:
yield asyncio.run_coroutine_threadsafe( yield asyncio.run_coroutine_threadsafe(
anext(aiter_), # type: ignore[arg-type] anext(aiter_), # type: ignore[arg-type] # noqa: F821
self.loop, self.loop,
).result() ).result()
except StopAsyncIteration: except StopAsyncIteration:
@@ -24,13 +24,11 @@ from langgraph.store.base.batch import AsyncBatchedBaseStore
from langgraph.store.sqlite.base import ( from langgraph.store.sqlite.base import (
_PLACEHOLDER, _PLACEHOLDER,
NS_MATCH_FUNCTION,
BaseSqliteStore, BaseSqliteStore,
SqliteIndexConfig, SqliteIndexConfig,
_decode_ns_text, _decode_ns_text,
_ensure_index_config, _ensure_index_config,
_group_ops, _group_ops,
_namespace_match,
_row_to_item, _row_to_item,
_row_to_search_item, _row_to_search_item,
) )
@@ -152,13 +150,6 @@ class AsyncSqliteStore(AsyncBatchedBaseStore, BaseSqliteStore):
if self.is_setup: if self.is_setup:
return return
# list_namespaces needs segment-aware matching, which SQLite cannot
# express in LIKE or GLOB. Registered here rather than in __init__
# because aiosqlite's create_function is a coroutine.
await self.conn.create_function(
NS_MATCH_FUNCTION, 2, _namespace_match, deterministic=True
)
# Create migrations table if it doesn't exist # Create migrations table if it doesn't exist
await self.conn.execute( await self.conn.execute(
""" """
@@ -7,7 +7,7 @@ import re
import sqlite3 import sqlite3
import threading import threading
from collections import defaultdict from collections import defaultdict
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from collections.abc import Callable, Iterable, Iterator, Sequence
from contextlib import contextmanager from contextlib import contextmanager
from typing import Any, Literal, NamedTuple, cast from typing import Any, Literal, NamedTuple, cast
@@ -93,74 +93,12 @@ class SqliteIndexConfig(IndexConfig):
pass pass
NS_MATCH_FUNCTION = "_langgraph_namespace_match" def _namespace_to_text(
"""SQLite user function backing segment-aware namespace matching. namespace: tuple[str, ...], handle_wildcards: bool = False
) -> str:
Registered under a private name rather than overriding `REGEXP`, so a caller's
own `REGEXP` is left untouched.
"""
def _namespace_match(prefix: str | None, pattern: str) -> int:
"""Backing implementation of `NS_MATCH_FUNCTION`."""
if prefix is None:
return 0
return 1 if re.search(pattern, prefix) else 0
def _escape_glob_literal(text: str) -> str:
"""Escape GLOB metacharacters so `text` is matched literally.
GLOB has no `ESCAPE` clause, so metacharacters are wrapped in a character
class instead. `]` is literal outside a class and needs no escaping.
"""
return text.replace("[", "[[]").replace("*", "[*]").replace("?", "[?]")
def _namespace_prefix_condition(
namespace_prefix: tuple[str, ...], column: str = "prefix"
) -> tuple[str, tuple[str, ...]]:
"""Build the SQL scoping a search to a namespace and its descendants.
Matches the namespace exactly or requires the `.` separator before any
remainder, so a prefix of `("foo",)` does not also match `("foobar",)`.
Uses GLOB rather than LIKE because SQLite's LIKE is case-insensitive for
ASCII, which would match `("FOO",)` for a prefix of `("foo",)` even though
`get`/`put`/`delete` compare with `=` and treat those as distinct.
An empty prefix is unconstrained and matches every namespace.
"""
if not namespace_prefix:
return "TRUE", ()
path = _namespace_to_text(namespace_prefix)
condition = f"({column} = ? OR {column} GLOB ?)"
return condition, (path, f"{_escape_glob_literal(path)}.*")
def _namespace_match_pattern(path: tuple[str, ...], match_type: str) -> str:
"""Build a regex matching the dot-joined prefix on whole namespace segments.
Needed because neither LIKE nor GLOB can express "any character except the
separator": GLOB has character classes but no quantifier, so `[^.]*` still
crosses `.`. Matches how `InMemoryStore` compares namespaces element-wise.
`*` matches exactly one segment. Prefix matches stay open-ended but must end
on a separator; suffix matches anchor at the end and begin on one.
Examples:
prefix ("uid", "*", "alice") -> ^uid\\.[^.]+\\.alice(\\.|\\Z)
suffix ("alice",) -> (^|\\.)alice\\Z
"""
segments = ("[^.]+" if part == "*" else re.escape(part) for part in path)
body = r"\.".join(segments)
if match_type == "suffix":
return rf"(^|\.){body}\Z"
return rf"^{body}(\.|\Z)"
def _namespace_to_text(namespace: tuple[str, ...]) -> str:
"""Convert namespace tuple to text string.""" """Convert namespace tuple to text string."""
if handle_wildcards:
namespace = tuple("%" if val == "*" else val for val in namespace)
return ".".join(namespace) return ".".join(namespace)
@@ -387,7 +325,7 @@ class BaseSqliteStore:
[ [
_namespace_to_text(op.namespace), _namespace_to_text(op.namespace),
op.key, op.key,
orjson.dumps(dict(cast(Mapping[str, Any], op.value))), orjson.dumps(cast(dict, op.value)),
expires_at, expires_at,
op.ttl, op.ttl,
] ]
@@ -523,11 +461,8 @@ class BaseSqliteStore:
else " AND " + " AND ".join(filter_conditions) else " AND " + " AND ".join(filter_conditions)
) )
if op.namespace_prefix: if op.namespace_prefix:
ns_condition, ns_args_tuple = _namespace_prefix_condition( prefix_filter_str = f"WHERE s.prefix LIKE ? {filter_str} "
op.namespace_prefix, column="s.prefix" ns_args: Sequence = (f"{_namespace_to_text(op.namespace_prefix)}%",)
)
prefix_filter_str = f"WHERE {ns_condition} {filter_str} "
ns_args: Sequence = ns_args_tuple
else: else:
ns_args = () ns_args = ()
if filter_str: if filter_str:
@@ -568,15 +503,12 @@ class BaseSqliteStore:
] ]
# Regular search branch (no vector search) # Regular search branch (no vector search)
else: else:
ns_condition, ns_args_tuple = _namespace_prefix_condition( base_query = """
op.namespace_prefix
)
base_query = f"""
SELECT prefix, key, value, created_at, updated_at, expires_at, ttl_minutes, NULL as score SELECT prefix, key, value, created_at, updated_at, expires_at, ttl_minutes, NULL as score
FROM store FROM store
WHERE {ns_condition} WHERE prefix LIKE ?
""" """
params = list(ns_args_tuple) params = [f"{_namespace_to_text(op.namespace_prefix)}%"]
if filter_conditions: if filter_conditions:
params.extend(filter_params) params.extend(filter_params)
@@ -617,27 +549,15 @@ class BaseSqliteStore:
if op.match_conditions: if op.match_conditions:
for cond in op.match_conditions: for cond in op.match_conditions:
if cond.match_type in ("prefix", "suffix"): if cond.match_type == "prefix":
if not cond.path: where_clauses.append("prefix LIKE ?")
# An empty path constrains nothing; skipping keeps it a
# no-op rather than emitting a pattern that matches no
# namespace at all.
continue
if cond.match_type == "prefix" and "*" not in cond.path:
# Equivalent to the anchored pattern, but SQLite can
# satisfy `=` and a trailing-wildcard GLOB from
# store_prefix_idx. The user function is opaque to the
# planner, so it would scan every row and call back
# into Python for each one.
condition, args = _namespace_prefix_condition(
tuple(cond.path)
)
where_clauses.append(condition)
params.extend(args)
else:
where_clauses.append(f"{NS_MATCH_FUNCTION}(prefix, ?) = 1")
params.append( params.append(
_namespace_match_pattern(cond.path, cond.match_type) f"{_namespace_to_text(cond.path, handle_wildcards=True)}%"
)
elif cond.match_type == "suffix":
where_clauses.append("prefix LIKE ?")
params.append(
f"%{_namespace_to_text(cond.path, handle_wildcards=True)}"
) )
else: else:
logger.warning( logger.warning(
@@ -865,9 +785,6 @@ class SqliteStore(BaseSqliteStore, BaseStore):
super().__init__() super().__init__()
self._deserializer = deserializer self._deserializer = deserializer
self.conn = conn self.conn = conn
# Registered here rather than in from_conn_string so a caller-supplied
# connection also gets it.
conn.create_function(NS_MATCH_FUNCTION, 2, _namespace_match, deterministic=True)
self.lock = threading.Lock() self.lock = threading.Lock()
self.is_setup = False self.is_setup = False
self.index_config = index self.index_config = index
+1 -5
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "langgraph-checkpoint-sqlite" name = "langgraph-checkpoint-sqlite"
version = "3.1.1" version = "3.1.0"
description = "Library with a SQLite implementation of LangGraph checkpoint saver." description = "Library with a SQLite implementation of LangGraph checkpoint saver."
authors = [] authors = []
requires-python = ">=3.10" requires-python = ">=3.10"
@@ -30,7 +30,6 @@ test = [
"pytest-mock", "pytest-mock",
"pytest-watcher", "pytest-watcher",
"langgraph-checkpoint", "langgraph-checkpoint",
"langgraph-checkpoint-conformance",
"pytest-retry>=1.7.0", "pytest-retry>=1.7.0",
] ]
lint = [ lint = [
@@ -48,7 +47,6 @@ default-groups = ['dev']
[tool.uv.sources] [tool.uv.sources]
langgraph-checkpoint = { path = "../checkpoint", editable = true } langgraph-checkpoint = { path = "../checkpoint", editable = true }
langgraph-checkpoint-conformance = { path = "../checkpoint-conformance", editable = true }
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
include = ["langgraph"] include = ["langgraph"]
@@ -64,8 +62,6 @@ lint.select = [
"UP", # pyupgrade "UP", # pyupgrade
"B", # flake8-bugbear "B", # flake8-bugbear
"I", # isort "I", # isort
"PLC0415", # import-outside-top-level
"RUF100", # unused noqa directive
"UP", # pyupgrade "UP", # pyupgrade
] ]
lint.ignore = ["E501", "B008"] lint.ignore = ["E501", "B008"]
@@ -716,32 +716,3 @@ async def test_search_items(
for ns in test_namespaces: for ns in test_namespaces:
key = f"item_{ns[-1]}" key = f"item_{ns[-1]}"
await store.adelete(ns, key) await store.adelete(ns, key)
async def test_async_namespace_segment_boundary(store: AsyncSqliteStore) -> None:
"""Segment-aware scoping on the async path.
Also covers that the namespace-match SQLite function is registered on the
async connection -- aiosqlite's create_function is a coroutine, so it is
registered in setup() rather than __init__.
"""
for namespace in [
("foo",),
("foo", "child"),
("foobar",),
("uid", "users", "alice"),
("uid", "users", "malice"),
("user_1",),
("userX1",),
]:
await store.aput(namespace, "k", {"v": 1})
found = {item.namespace for item in await store.asearch(("foo",), limit=100)}
assert found == {("foo",), ("foo", "child")}
found = {item.namespace for item in await store.asearch(("user_1",), limit=100)}
assert found == {("user_1",)}
assert set(await store.alist_namespaces(suffix=["alice"], limit=100)) == {
("uid", "users", "alice"),
}
@@ -3,14 +3,21 @@
from __future__ import annotations from __future__ import annotations
import pytest import pytest
pytest.importorskip(
"langgraph.checkpoint.conformance",
reason="langgraph-checkpoint-conformance not installed",
)
pytest.importorskip("aiosqlite", reason="aiosqlite not installed")
@pytest.mark.asyncio
async def test_delta_channel_conformance():
from langgraph.checkpoint.conformance import validate from langgraph.checkpoint.conformance import validate
from langgraph.checkpoint.conformance.initializer import checkpointer_test from langgraph.checkpoint.conformance.initializer import checkpointer_test
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
@pytest.mark.asyncio
async def test_delta_channel_conformance():
@checkpointer_test(name="AsyncSqliteSaver") @checkpointer_test(name="AsyncSqliteSaver")
async def sqlite_saver(): async def sqlite_saver():
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver: async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
@@ -29,13 +29,13 @@ pytest.importorskip("langgraph.channels.delta", reason="langgraph core not insta
pytest.importorskip("langgraph.channels.binop", reason="langgraph core not installed") pytest.importorskip("langgraph.channels.binop", reason="langgraph core not installed")
pytest.importorskip("langgraph.graph", reason="langgraph core not installed") pytest.importorskip("langgraph.graph", reason="langgraph core not installed")
from langgraph.channels.binop import BinaryOperatorAggregate # type: ignore[import-untyped] # noqa: I001 from langgraph.channels.binop import BinaryOperatorAggregate # type: ignore[import-untyped] # noqa: E402,I001
from langgraph.channels.delta import DeltaChannel # type: ignore[import-untyped] from langgraph.channels.delta import DeltaChannel # type: ignore[import-untyped] # noqa: E402
from langgraph.graph import END, START, StateGraph # type: ignore[import-untyped] from langgraph.graph import END, START, StateGraph # type: ignore[import-untyped] # noqa: E402
from typing_extensions import TypedDict from typing_extensions import TypedDict # noqa: E402
from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.checkpoint.sqlite import SqliteSaver # noqa: E402
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver # noqa: E402
pytestmark = pytest.mark.anyio pytestmark = pytest.mark.anyio
@@ -32,13 +32,13 @@ from langchain_core.runnables import RunnableConfig
pytest.importorskip("langgraph.channels.delta", reason="langgraph core not installed") pytest.importorskip("langgraph.channels.delta", reason="langgraph core not installed")
pytest.importorskip("langgraph.graph", reason="langgraph core not installed") pytest.importorskip("langgraph.graph", reason="langgraph core not installed")
from langgraph.channels.delta import DeltaChannel # type: ignore[import-untyped] # noqa: I001 from langgraph.channels.delta import DeltaChannel # type: ignore[import-untyped] # noqa: E402,I001
from langgraph.checkpoint.serde.types import _DeltaSnapshot from langgraph.checkpoint.serde.types import _DeltaSnapshot # noqa: E402
from langgraph.graph import END, START, StateGraph # type: ignore[import-untyped] from langgraph.graph import END, START, StateGraph # type: ignore[import-untyped] # noqa: E402
from typing_extensions import TypedDict from typing_extensions import TypedDict # noqa: E402
from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.checkpoint.sqlite import SqliteSaver # noqa: E402
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver # noqa: E402
pytestmark = pytest.mark.anyio pytestmark = pytest.mark.anyio
+10 -216
View File
@@ -1,11 +1,7 @@
import math
import os import os
import random
import re import re
import tempfile import tempfile
import time
import uuid import uuid
from collections import Counter, defaultdict
from collections.abc import Generator, Iterable from collections.abc import Generator, Iterable
from contextlib import contextmanager from contextlib import contextmanager
from typing import Any, Literal, cast from typing import Any, Literal, cast
@@ -22,13 +18,7 @@ from langgraph.store.base import (
) )
from langgraph.store.sqlite import SqliteStore from langgraph.store.sqlite import SqliteStore
from langgraph.store.sqlite.base import ( from langgraph.store.sqlite.base import SqliteIndexConfig
NS_MATCH_FUNCTION,
BaseSqliteStore,
SqliteIndexConfig,
_escape_glob_literal,
_namespace_match_pattern,
)
# Local embeddings implementation for testing vector search # Local embeddings implementation for testing vector search
@@ -37,6 +27,10 @@ class CharacterEmbeddings(Embeddings):
def __init__(self, dims: int = 50, seed: int = 42): def __init__(self, dims: int = 50, seed: int = 42):
"""Initialize with embedding dimensions and random seed.""" """Initialize with embedding dimensions and random seed."""
import math
import random
from collections import defaultdict
self._rng = random.Random(seed) self._rng = random.Random(seed)
self.dims = dims self.dims = dims
# Create projection vector for each character lazily # Create projection vector for each character lazily
@@ -48,6 +42,9 @@ class CharacterEmbeddings(Embeddings):
def _embed_one(self, text: str) -> list[float]: def _embed_one(self, text: str) -> list[float]:
"""Embed a single text.""" """Embed a single text."""
import math
from collections import Counter
counts = Counter(text) counts = Counter(text)
total = sum(counts.values()) total = sum(counts.values())
@@ -335,6 +332,8 @@ class TestSqliteStore:
# Test update # Test update
# Small delay to ensure the updated timestamp is different # Small delay to ensure the updated timestamp is different
import time
time.sleep(0.01) time.sleep(0.01)
updated_value = {"title": "Updated Document", "content": "Hello, Updated!"} updated_value = {"title": "Updated Document", "content": "Hello, Updated!"}
@@ -1230,208 +1229,3 @@ def test_non_ascii(
assert result3[0].key == "3" assert result3[0].key == "3"
assert result4[0].key == "4" assert result4[0].key == "4"
assert result5[0].key == "5" assert result5[0].key == "5"
def test_escape_glob_literal() -> None:
assert _escape_glob_literal("users.alice") == "users.alice"
# "_" and "%" are LIKE wildcards but literal in GLOB, so they are left alone.
assert _escape_glob_literal("user_1") == "user_1"
assert _escape_glob_literal("100%") == "100%"
assert _escape_glob_literal("a*b") == "a[*]b"
assert _escape_glob_literal("a?b") == "a[?]b"
assert _escape_glob_literal("a[b") == "a[[]b"
def test_namespace_match_pattern() -> None:
assert _namespace_match_pattern(("foo",), "prefix") == r"^foo(\.|\Z)"
assert (
_namespace_match_pattern(("uid", "*", "alice"), "prefix")
== r"^uid\.[^.]+\.alice(\.|\Z)"
)
assert _namespace_match_pattern(("alice",), "suffix") == r"(^|\.)alice\Z"
def test_search_namespace_segment_boundary(store: SqliteStore) -> None:
"""Prefix scoping must stop at namespace segment boundaries.
Namespaces are stored dot-joined, so matching the raw text also returns
siblings sharing leading characters.
"""
for namespace in [
("foo",),
("foo", "child"),
("foo", "child", "deep"),
("foobar",),
("foobar", "baz"),
("foo2",),
]:
store.put(namespace, "k", {"v": 1})
def _namespaces(prefix: tuple[str, ...]) -> set[tuple[str, ...]]:
return {item.namespace for item in store.search(prefix, limit=100)}
assert _namespaces(("foo",)) == {
("foo",),
("foo", "child"),
("foo", "child", "deep"),
}
# The sibling scope is independent, not merely narrower.
assert _namespaces(("foobar",)) == {("foobar",), ("foobar", "baz")}
assert _namespaces(("foo2",)) == {("foo2",)}
assert _namespaces(("fo",)) == set()
def test_search_namespace_wildcard_chars_are_literal(store: SqliteStore) -> None:
"""LIKE and GLOB metacharacters in labels must be matched literally."""
for namespace in [
("user_1",),
("user_1", "child"),
("userX1",),
("a%b",),
("axxb",),
("star*",),
("starX",),
]:
store.put(namespace, "k", {"v": 1})
def _namespaces(prefix: tuple[str, ...]) -> set[tuple[str, ...]]:
return {item.namespace for item in store.search(prefix, limit=100)}
# Also asserts each namespace still matches itself, which catches escaping
# the equality arm by mistake.
assert _namespaces(("user_1",)) == {("user_1",), ("user_1", "child")}
assert _namespaces(("a%b",)) == {("a%b",)}
assert _namespaces(("star*",)) == {("star*",)}
def test_search_namespace_is_case_sensitive(store: SqliteStore) -> None:
"""Search must agree with get/put, which compare namespaces with `=`.
SQLite's LIKE is case-insensitive for ASCII, so matching with it conflated
namespaces that every other operation treats as distinct.
"""
store.put(("Foo",), "k", {"v": "upper"})
store.put(("foo",), "k", {"v": "lower"})
assert {item.namespace for item in store.search(("foo",), limit=100)} == {("foo",)}
assert {item.namespace for item in store.search(("Foo",), limit=100)} == {("Foo",)}
def test_list_namespaces_segment_boundary(store: SqliteStore) -> None:
for namespace in [
("foo",),
("foo", "child"),
("foobar",),
("foobar", "baz"),
("uid", "users", "alice"),
("uid", "users", "malice"),
("uid", "a", "b", "alice"),
]:
store.put(namespace, "k", {"v": 1})
assert set(store.list_namespaces(prefix=["foo"], limit=100)) == {
("foo",),
("foo", "child"),
}
# Suffix must align to a segment: "malice" does not end with the "alice"
# segment.
assert set(store.list_namespaces(suffix=["alice"], limit=100)) == {
("uid", "users", "alice"),
("uid", "a", "b", "alice"),
}
# "*" spans exactly one segment.
assert set(store.list_namespaces(prefix=["uid", "*", "alice"], limit=100)) == {
("uid", "users", "alice"),
}
# Prefix matching stays open-ended across depth.
assert set(store.list_namespaces(prefix=["uid"], limit=100)) == {
("uid", "users", "alice"),
("uid", "users", "malice"),
("uid", "a", "b", "alice"),
}
def test_search_empty_prefix_is_unconstrained(store: SqliteStore) -> None:
"""An empty prefix constrains nothing and must return every namespace."""
for namespace in [("a",), ("b", "c"), ("d", "e", "f")]:
store.put(namespace, "k", {"v": 1})
assert {item.namespace for item in store.search((), limit=100)} == {
("a",),
("b", "c"),
("d", "e", "f"),
}
def test_namespace_labels_with_trailing_newline(store: SqliteStore) -> None:
"""Labels may contain newlines, and must not match a differently-named label.
Python's `$` also matches just before a trailing newline, so the patterns use
`\\Z` to anchor at the true end of the string.
"""
store.put(("users", "alice"), "k", {"v": 1})
store.put(("users", "alice\n"), "k", {"v": 2})
assert set(store.list_namespaces(suffix=["alice"], limit=100)) == {
("users", "alice"),
}
assert set(store.list_namespaces(prefix=["users", "alice"], limit=100)) == {
("users", "alice"),
}
def test_list_namespaces_prefix_uses_indexable_condition() -> None:
"""Plain prefixes must use the indexable condition, not the match function.
A user function is opaque to the query planner, so it scans every row and
calls back into Python for each one. Only suffix and wildcard paths, which
no SQLite operator can express, need it.
"""
store = BaseSqliteStore()
def where(match_type: str, path: tuple[str, ...]) -> str:
op = ListNamespacesOp(
match_conditions=(MatchCondition(match_type=match_type, path=path),),
max_depth=None,
limit=10,
offset=0,
)
query, _ = store._get_batch_list_namespaces_queries([(0, op)])[0]
return " ".join(query.split())
assert "GLOB" in where("prefix", ("uid", "users"))
assert NS_MATCH_FUNCTION not in where("prefix", ("uid", "users"))
# A label that merely contains "*" is not the wildcard.
assert "GLOB" in where("prefix", ("star*",))
# Wildcard and suffix cannot be expressed by GLOB, so they keep the function.
assert NS_MATCH_FUNCTION in where("prefix", ("uid", "*", "alice"))
assert NS_MATCH_FUNCTION in where("suffix", ("alice",))
def test_list_namespaces_metacharacter_labels(store: SqliteStore) -> None:
"""Metacharacters in labels are literal on both matching paths.
Plain prefixes take the `= OR GLOB` condition and wildcard/suffix paths take
the regex function, so escaping has to hold in two different syntaxes.
"""
pairs = [
("star*", "starX"),
("q?m", "qXm"),
("br[ack]et", "brXacXket"),
("user_1", "userX1"),
("a%b", "axxb"),
("plus+", "plusX"),
]
for label, decoy in pairs:
store.put((label,), "k", {"v": 1})
store.put((decoy,), "k", {"v": 1})
store.put((label, "child"), "k", {"v": 1})
for label, decoy in pairs:
found = set(store.list_namespaces(prefix=[label], limit=100))
assert found == {(label,), (label, "child")}
# The decoy differs only where the metacharacter would have matched.
assert (decoy,) not in found
assert set(store.list_namespaces(prefix=[label, "child"], limit=100)) == {
(label, "child"),
}
+47 -78
View File
@@ -147,11 +147,11 @@ wheels = [
[[package]] [[package]]
name = "codespell" name = "codespell"
version = "2.4.3" version = "2.4.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/80/19/45e941380f69c042b43423513d201e6592346f992394347f5e7174c31407/codespell-2.4.3.tar.gz", hash = "sha256:cbe085e331227b37bb86ef8bddd08dc768c704ee9a07ca869852c093fa2793e2", size = 352773, upload-time = "2026-07-15T11:51:54.159Z" } sdist = { url = "https://files.pythonhosted.org/packages/2d/9d/1d0903dff693160f893ca6abcabad545088e7a2ee0a6deae7c24e958be69/codespell-2.4.2.tar.gz", hash = "sha256:3c33be9ae34543807f088aeb4832dfad8cb2dae38da61cac0a7045dd376cfdf3", size = 352058, upload-time = "2026-03-05T18:10:42.936Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/bf/bdb951d34eb169140b546f44be9ec4525d1acefb9eb5572071f5492b19fc/codespell-2.4.3-py3-none-any.whl", hash = "sha256:af2505b335e8573dbd2d384d1c4ef498f4006f4ba2d6fceca01e55b91f52628a", size = 340736, upload-time = "2026-07-15T11:51:52.925Z" }, { url = "https://files.pythonhosted.org/packages/42/a1/52fa05533e95fe45bcc09bcf8a503874b1c08f221a4e35608017e0938f55/codespell-2.4.2-py3-none-any.whl", hash = "sha256:97e0c1060cf46bd1d5db89a936c98db8c2b804e1fdd4b5c645e82a1ec6b1f886", size = 353715, upload-time = "2026-03-05T18:10:41.398Z" },
] ]
[[package]] [[package]]
@@ -168,7 +168,7 @@ name = "exceptiongroup"
version = "1.3.1" version = "1.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" },
] ]
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" } 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 = [ wheels = [
@@ -253,7 +253,7 @@ wheels = [
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "1.5.2" version = "1.4.8"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jsonpatch" }, { name = "jsonpatch" },
@@ -266,9 +266,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "uuid-utils" }, { 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" } sdist = { url = "https://files.pythonhosted.org/packages/12/e3/bea6d0080acf183332f24dcd74c208aee5857cf8f783c3fb0bd86027d8fb/langchain_core-1.4.8.tar.gz", hash = "sha256:5bf1f8411077c904182ad8f975943d36adcbf579c4e017b3a118b719229ebf9a", size = 957974, upload-time = "2026-06-18T19:39:23.636Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/e4/024e402a65f5ee2eb6ae9667b5ffc5f08776cb4e712f5a11ee1997d56083/langchain_core-1.5.2-py3-none-any.whl", hash = "sha256:a687dd7c3b22c6c1294e1c1eeb61fb6f3a308e6015a1d75b576b3836ad5b5aed", size = 561643, upload-time = "2026-07-28T16:38:36.38Z" }, { url = "https://files.pythonhosted.org/packages/13/d6/bdf6f0481cc57ef300d6b1eb48cf1400c0409be715d6eb3cabadd1142a09/langchain_core-1.4.8-py3-none-any.whl", hash = "sha256:d84c28b05e3ba8d4271d0827aad5b592ccdaaf986e76768c23503f0a2045e8aa", size = 557416, upload-time = "2026-06-18T19:39:21.902Z" },
] ]
[[package]] [[package]]
@@ -285,7 +285,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph-checkpoint" name = "langgraph-checkpoint"
version = "4.2.0" version = "4.1.1"
source = { editable = "../checkpoint" } source = { editable = "../checkpoint" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -331,36 +331,9 @@ test = [
{ name = "redis" }, { name = "redis" },
] ]
[[package]]
name = "langgraph-checkpoint-conformance"
version = "0.0.2"
source = { editable = "../checkpoint-conformance" }
dependencies = [
{ name = "langgraph-checkpoint" },
]
[package.metadata]
requires-dist = [{ name = "langgraph-checkpoint", editable = "../checkpoint" }]
[package.metadata.requires-dev]
dev = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "ruff" },
{ name = "ty" },
]
lint = [
{ name = "ruff" },
{ name = "ty" },
]
test = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
]
[[package]] [[package]]
name = "langgraph-checkpoint-sqlite" name = "langgraph-checkpoint-sqlite"
version = "3.1.1" version = "3.1.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiosqlite" }, { name = "aiosqlite" },
@@ -372,7 +345,6 @@ dependencies = [
dev = [ dev = [
{ name = "codespell" }, { name = "codespell" },
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint" },
{ name = "langgraph-checkpoint-conformance" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
{ name = "pytest-mock" }, { name = "pytest-mock" },
@@ -388,7 +360,6 @@ lint = [
] ]
test = [ test = [
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint" },
{ name = "langgraph-checkpoint-conformance" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
{ name = "pytest-mock" }, { name = "pytest-mock" },
@@ -407,7 +378,6 @@ requires-dist = [
dev = [ dev = [
{ name = "codespell" }, { name = "codespell" },
{ name = "langgraph-checkpoint", editable = "../checkpoint" }, { name = "langgraph-checkpoint", editable = "../checkpoint" },
{ name = "langgraph-checkpoint-conformance", editable = "../checkpoint-conformance" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
{ name = "pytest-mock" }, { name = "pytest-mock" },
@@ -423,7 +393,6 @@ lint = [
] ]
test = [ test = [
{ name = "langgraph-checkpoint", editable = "../checkpoint" }, { name = "langgraph-checkpoint", editable = "../checkpoint" },
{ name = "langgraph-checkpoint-conformance", editable = "../checkpoint-conformance" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
{ name = "pytest-mock" }, { name = "pytest-mock" },
@@ -911,27 +880,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.16.5" version = "0.15.20"
source = { registry = "https://pypi.org/simple" } 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/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" }
wheels = [ 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/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" },
{ 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/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" },
{ 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/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" },
{ 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/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" },
{ 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/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" },
{ 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/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" },
{ 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/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" },
{ 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/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" },
{ 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/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" },
{ 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/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" },
{ 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/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" },
{ 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/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" },
{ 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/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" },
{ 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/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" },
{ 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/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" },
{ 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/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" },
{ 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/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
] ]
[[package]] [[package]]
@@ -1011,27 +980,27 @@ wheels = [
[[package]] [[package]]
name = "ty" name = "ty"
version = "0.0.75" version = "0.0.55"
source = { registry = "https://pypi.org/simple" } 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/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" }
wheels = [ 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/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" },
{ 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/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" },
{ 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/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" },
{ 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/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" },
{ 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/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" },
{ 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/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" },
{ 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/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" },
{ 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/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" },
{ 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/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" },
{ 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/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" },
{ 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/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" },
{ 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/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" },
{ 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/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" },
{ 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/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" },
{ 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/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" },
{ 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/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" },
{ 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/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" },
] ]
[[package]] [[package]]
+17 -10
View File
@@ -39,12 +39,7 @@ You must pass these when invoking the graph as part of the configurable part of
```python ```python
{"configurable": {"thread_id": "1"}} # valid config {"configurable": {"thread_id": "1"}} # valid config
{ {"configurable": {"thread_id": "1", "checkpoint_id": "0c62ca34-ac19-445d-bbb0-5b4984975b2a"}} # also valid config
"configurable": {
"thread_id": "1",
"checkpoint_id": "0c62ca34-ac19-445d-bbb0-5b4984975b2a",
}
} # also valid config
``` ```
### Serde ### Serde
@@ -84,12 +79,24 @@ checkpoint = {
"v": 4, "v": 4,
"ts": "2024-07-31T20:14:19.804150+00:00", "ts": "2024-07-31T20:14:19.804150+00:00",
"id": "1ef4f797-8335-6428-8001-8a1503f9b875", "id": "1ef4f797-8335-6428-8001-8a1503f9b875",
"channel_values": {"my_key": "meow", "node": "node"}, "channel_values": {
"channel_versions": {"__start__": 2, "my_key": 3, "start:node": 3, "node": 3}, "my_key": "meow",
"node": "node"
},
"channel_versions": {
"__start__": 2,
"my_key": 3,
"start:node": 3,
"node": 3
},
"versions_seen": { "versions_seen": {
"__input__": {}, "__input__": {},
"__start__": {"__start__": 1}, "__start__": {
"node": {"start:node": 2}, "__start__": 1
},
"node": {
"start:node": 2
}
}, },
} }
@@ -10,7 +10,6 @@ from typing import (
NamedTuple, NamedTuple,
TypedDict, TypedDict,
TypeVar, TypeVar,
cast,
) )
from langchain_core.runnables import RunnableConfig from langchain_core.runnables import RunnableConfig
@@ -782,7 +781,7 @@ def get_serializable_checkpoint_metadata(
"""Get checkpoint metadata in a backwards-compatible manner.""" """Get checkpoint metadata in a backwards-compatible manner."""
checkpoint_metadata = get_checkpoint_metadata(config, metadata) checkpoint_metadata = get_checkpoint_metadata(config, metadata)
if "writes" in checkpoint_metadata: if "writes" in checkpoint_metadata:
cast(dict[str, Any], checkpoint_metadata).pop("writes") checkpoint_metadata.pop("writes")
return checkpoint_metadata return checkpoint_metadata
@@ -148,16 +148,17 @@ class InMemorySaver(
whose stored blob is non-empty. Other channels keep walking until whose stored blob is non-empty. Other channels keep walking until
they find their own terminator or hit the root. they find their own terminator or hit the root.
A blob is the value AT its ancestor, prior to the writes stored Pre-delta plain-value blobs subsume their ancestor's pending
under that same ancestor (those writes produce its child, which writes (the value already includes them); `_DeltaSnapshot` blobs
is on the path to the target). This holds for `_DeltaSnapshot` do not (snapshot is the value AT that ancestor, prior to its own
blobs and for pre-delta plain values alike, so the seed pending writes that produce the child).
ancestor's own writes are always collected. Writes at ancestors
older than the seed are subsumed by the seed value and are never
reached the walk terminates there.
""" """
if not channels: if not channels:
return {} return {}
# Imported lazily to avoid a hard checkpoint→serde-types coupling at
# module import; only this override needs the runtime check.
from langgraph.checkpoint.serde.types import _DeltaSnapshot
thread_id = config["configurable"]["thread_id"] thread_id = config["configurable"]["thread_id"]
checkpoint_ns = config["configurable"].get("checkpoint_ns", "") checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
checkpoint_id = config["configurable"].get("checkpoint_id", "") checkpoint_id = config["configurable"].get("checkpoint_id", "")
@@ -204,6 +205,11 @@ class InMemorySaver(
): ):
if ch not in remaining: if ch not in remaining:
continue continue
blob_value = blob_value_by_ch.get(ch)
if blob_value is not None and not isinstance(
blob_value, _DeltaSnapshot
):
continue
collected_by_ch[ch].append( collected_by_ch[ch].append(
(tid, ch, self.serde.loads_typed(serialized)) (tid, ch, self.serde.loads_typed(serialized))
) )
@@ -12,7 +12,7 @@ Core types:
from __future__ import annotations from __future__ import annotations
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections.abc import Iterable, Mapping from collections.abc import Iterable
from datetime import datetime from datetime import datetime
from typing import ( from typing import (
Any, Any,
@@ -473,10 +473,10 @@ class PutOp(NamedTuple):
the full path would effectively be `"documents/user123/report1"` the full path would effectively be `"documents/user123/report1"`
""" """
value: Mapping[str, Any] | None value: dict[str, Any] | None
"""The data to store, or `None` to mark the item for deletion. """The data to store, or `None` to mark the item for deletion.
The value must be a mapping with string keys and JSON-serializable values. The value must be a dictionary with string keys and JSON-serializable values.
Setting this to `None` signals that the item should be deleted. Setting this to `None` signals that the item should be deleted.
Example: Example:
@@ -857,7 +857,7 @@ class BaseStore(ABC):
self, self,
namespace: tuple[str, ...], namespace: tuple[str, ...],
key: str, key: str,
value: Mapping[str, Any], value: dict[str, Any],
index: Literal[False] | list[str] | None = None, index: Literal[False] | list[str] | None = None,
*, *,
ttl: float | None | NotProvided = NOT_PROVIDED, ttl: float | None | NotProvided = NOT_PROVIDED,
@@ -869,7 +869,7 @@ class BaseStore(ABC):
Example: `("documents", "user123")` Example: `("documents", "user123")`
key: Unique identifier within the namespace. Together with namespace forms key: Unique identifier within the namespace. Together with namespace forms
the complete path to the item. the complete path to the item.
value: Mapping containing the item's data. Must contain string keys value: Dictionary containing the item's data. Must contain string keys
and JSON-serializable values. and JSON-serializable values.
index: Controls how the item's fields are indexed for search: index: Controls how the item's fields are indexed for search:
@@ -1110,7 +1110,7 @@ class BaseStore(ABC):
self, self,
namespace: tuple[str, ...], namespace: tuple[str, ...],
key: str, key: str,
value: Mapping[str, Any], value: dict[str, Any],
index: Literal[False] | list[str] | None = None, index: Literal[False] | list[str] | None = None,
*, *,
ttl: float | None | NotProvided = NOT_PROVIDED, ttl: float | None | NotProvided = NOT_PROVIDED,
@@ -1122,7 +1122,7 @@ class BaseStore(ABC):
Example: `("documents", "user123")` Example: `("documents", "user123")`
key: Unique identifier within the namespace. Together with namespace forms key: Unique identifier within the namespace. Together with namespace forms
the complete path to the item. the complete path to the item.
value: Mapping containing the item's data. Must contain string keys value: Dictionary containing the item's data. Must contain string keys
and JSON-serializable values. and JSON-serializable values.
index: Controls how the item's fields are indexed for search: index: Controls how the item's fields are indexed for search:
@@ -5,7 +5,7 @@ from __future__ import annotations
import asyncio import asyncio
import functools import functools
import weakref import weakref
from collections.abc import Callable, Iterable, Mapping from collections.abc import Callable, Iterable
from typing import Any, Literal, TypeVar from typing import Any, Literal, TypeVar
from langgraph.store.base import ( from langgraph.store.base import (
@@ -132,7 +132,7 @@ class AsyncBatchedBaseStore(BaseStore):
self, self,
namespace: tuple[str, ...], namespace: tuple[str, ...],
key: str, key: str,
value: Mapping[str, Any], value: dict[str, Any],
index: Literal[False] | list[str] | None = None, index: Literal[False] | list[str] | None = None,
*, *,
ttl: float | None | NotProvided = NOT_PROVIDED, ttl: float | None | NotProvided = NOT_PROVIDED,
@@ -231,7 +231,7 @@ class AsyncBatchedBaseStore(BaseStore):
self, self,
namespace: tuple[str, ...], namespace: tuple[str, ...],
key: str, key: str,
value: Mapping[str, Any], value: dict[str, Any],
index: Literal[False] | list[str] | None = None, index: Literal[False] | list[str] | None = None,
*, *,
ttl: float | None | NotProvided = NOT_PROVIDED, ttl: float | None | NotProvided = NOT_PROVIDED,
@@ -11,7 +11,7 @@ from __future__ import annotations
import asyncio import asyncio
import functools import functools
import json import json
from collections.abc import Awaitable, Callable, Mapping, Sequence from collections.abc import Awaitable, Callable, Sequence
from typing import Any from typing import Any
from langchain_core.embeddings import Embeddings from langchain_core.embeddings import Embeddings
@@ -244,9 +244,6 @@ def get_text_at_path(obj: Any, path: str | list[str]) -> list[str]:
- Multi-field selection: "{field1,field2}" - Multi-field selection: "{field1,field2}"
- Nested paths in multi-field: "{field1,nested.field2}" - Nested paths in multi-field: "{field1,nested.field2}"
""" """
if isinstance(obj, Mapping) and not isinstance(obj, dict):
obj = dict(obj)
if not path or path == "$": if not path or path == "$":
return [json.dumps(obj, sort_keys=True, ensure_ascii=False)] return [json.dumps(obj, sort_keys=True, ensure_ascii=False)]
@@ -408,7 +408,7 @@ class InMemoryStore(BaseStore):
self._vectors[namespace].pop(key, None) self._vectors[namespace].pop(key, None)
else: else:
self._data[namespace][key] = Item( self._data[namespace][key] = Item(
value=dict(op.value), value=op.value,
key=key, key=key,
namespace=namespace, namespace=namespace,
created_at=datetime.now(timezone.utc), created_at=datetime.now(timezone.utc),
+1 -7
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "langgraph-checkpoint" name = "langgraph-checkpoint"
version = "4.2.0" version = "4.1.1"
description = "Library with base interfaces for LangGraph checkpoint savers." description = "Library with base interfaces for LangGraph checkpoint savers."
authors = [] authors = []
requires-python = ">=3.10" requires-python = ">=3.10"
@@ -59,15 +59,9 @@ lint.select = [
"UP", # pyupgrade "UP", # pyupgrade
"B", # flake8-bugbear "B", # flake8-bugbear
"I", # isort "I", # isort
"PLC0415", # import-outside-top-level
"RUF100", # unused noqa directive
"UP", # pyupgrade "UP", # pyupgrade
] ]
lint.ignore = ["E501", "B008"] lint.ignore = ["E501", "B008"]
# PLC0415 (import-outside-top-level) is enforced in tests only. Library code
# still has deferred imports that have not been reviewed, so it stays exempt
# for now.
lint.per-file-ignores = { "langgraph/**" = ["PLC0415"] }
target-version = "py310" target-version = "py310"
[tool.ty.rules] [tool.ty.rules]
@@ -12,14 +12,10 @@ conformance = pytest.importorskip(
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_delta_channel_conformance(): async def test_delta_channel_conformance():
# Imported inside the test: the module-level importorskip above is what from langgraph.checkpoint.conformance import validate
# makes these safe, so they cannot move to the top of the file. from langgraph.checkpoint.conformance.initializer import checkpointer_test
from langgraph.checkpoint.conformance import validate # noqa: PLC0415
from langgraph.checkpoint.conformance.initializer import ( # noqa: PLC0415
checkpointer_test,
)
from langgraph.checkpoint.memory import InMemorySaver # noqa: PLC0415 from langgraph.checkpoint.memory import InMemorySaver
@checkpointer_test(name="InMemorySaver") @checkpointer_test(name="InMemorySaver")
async def mem_saver(): async def mem_saver():
+2
View File
@@ -307,6 +307,8 @@ class TestWithMsgpackAllowlistEncrypted:
def loads_typed(self, data: tuple[str, bytes]) -> None: def loads_typed(self, data: tuple[str, bytes]) -> None:
return None return None
from langgraph.checkpoint.serde.base import CipherProtocol
class DummyCipher(CipherProtocol): class DummyCipher(CipherProtocol):
def encrypt(self, plaintext: bytes) -> tuple[str, bytes]: def encrypt(self, plaintext: bytes) -> tuple[str, bytes]:
return "dummy", plaintext return "dummy", plaintext
+9 -4
View File
@@ -1,12 +1,9 @@
import dataclasses import dataclasses
import json import json
import logging import logging
import os
import pathlib import pathlib
import pickle
import re import re
import sys import sys
import tempfile
import uuid import uuid
from collections import deque from collections import deque
from datetime import date, datetime, time, timezone from datetime import date, datetime, time, timezone
@@ -21,7 +18,7 @@ import ormsgpack
import pandas as pd import pandas as pd
import pytest import pytest
from langchain_core.documents.base import Document from langchain_core.documents.base import Document
from langchain_core.messages import AIMessage, HumanMessage from langchain_core.messages import HumanMessage
from pydantic import BaseModel, SecretStr from pydantic import BaseModel, SecretStr
from pydantic.v1 import BaseModel as BaseModelV1 from pydantic.v1 import BaseModel as BaseModelV1
from pydantic.v1 import SecretStr as SecretStrV1 from pydantic.v1 import SecretStr as SecretStrV1
@@ -344,6 +341,7 @@ def test_lc2_json_safe_type_revives_without_allowlist() -> None:
constructor dicts. Resuming those threads must reconstruct proper BaseMessage objects constructor dicts. Resuming those threads must reconstruct proper BaseMessage objects
rather than returning raw dicts that cause MESSAGE_COERCION_FAILURE in add_messages. rather than returning raw dicts that cause MESSAGE_COERCION_FAILURE in add_messages.
""" """
from langchain_core.messages import AIMessage
serde = JsonPlusSerializer() # default: _allowed_json_modules=None serde = JsonPlusSerializer() # default: _allowed_json_modules=None
@@ -412,6 +410,7 @@ def test_lc2_json_method_field_is_ignored() -> None:
to that method: the result is whatever ``AIMessage(*args, **kwargs)`` would to that method: the result is whatever ``AIMessage(*args, **kwargs)`` would
produce, which proves the default constructor ran instead of ``parse_raw``. produce, which proves the default constructor ran instead of ``parse_raw``.
""" """
from langchain_core.messages import AIMessage
serde = JsonPlusSerializer() serde = JsonPlusSerializer()
load = { load = {
@@ -437,6 +436,7 @@ def test_lc2_json_method_field_is_ignored_for_allowlisted_types() -> None:
method dispatch as a side effect. Revival is restricted to the default method dispatch as a side effect. Revival is restricted to the default
constructor regardless of how the class reached the revival path. constructor regardless of how the class reached the revival path.
""" """
from langchain_core.messages import AIMessage
serde = JsonPlusSerializer( serde = JsonPlusSerializer(
allowed_json_modules=[("langchain_core.messages.ai", "AIMessage")] allowed_json_modules=[("langchain_core.messages.ai", "AIMessage")]
@@ -455,6 +455,7 @@ def test_lc2_json_method_field_is_ignored_for_allowlisted_types() -> None:
def test_lc2_json_safe_type_init_still_works() -> None: def test_lc2_json_safe_type_init_still_works() -> None:
"""SAFE-type lc=2 revival without a `method` field still constructs the class.""" """SAFE-type lc=2 revival without a `method` field still constructs the class."""
from langchain_core.messages import AIMessage
serde = JsonPlusSerializer() serde = JsonPlusSerializer()
load = { load = {
@@ -478,6 +479,7 @@ def test_lc2_json_legacy_pydantic_method_list_falls_back_to_default() -> None:
this shape continue to revive correctly as long as the default constructor this shape continue to revive correctly as long as the default constructor
accepts the serialized kwargs. accepts the serialized kwargs.
""" """
from langchain_core.messages import AIMessage
serde = JsonPlusSerializer() serde = JsonPlusSerializer()
load = { load = {
@@ -549,6 +551,9 @@ def test_lc2_json_safe_type_pickle_payload_does_not_execute() -> None:
With method dispatch removed from `_revive_lc2`, the gadget bytes are never With method dispatch removed from `_revive_lc2`, the gadget bytes are never
passed to `parse_raw` and therefore never reach `pickle.loads`. passed to `parse_raw` and therefore never reach `pickle.loads`.
""" """
import os
import pickle
import tempfile
marker = tempfile.NamedTemporaryFile( marker = tempfile.NamedTemporaryFile(
prefix="lc2_block_proof_", suffix=".out", delete=False prefix="lc2_block_proof_", suffix=".out", delete=False
+19 -48
View File
@@ -1,4 +1,3 @@
import asyncio
import logging import logging
from typing import Any from typing import Any
@@ -524,6 +523,7 @@ class TestBaseFallbackGetChannelWrites:
`threading.local()` guard would let whichever task set it first `threading.local()` guard would let whichever task set it first
short-circuit the other to `writes=[]`. short-circuit the other to `writes=[]`.
""" """
import asyncio
saver, thread_id, ns = self._build_saver_with_chain() saver, thread_id, ns = self._build_saver_with_chain()
@@ -577,19 +577,9 @@ class TestPreDeltaBlobTerminator:
""" """
def _build_mixed_thread(self) -> tuple[InMemorySaver, str, str, str, str]: def _build_mixed_thread(self) -> tuple[InMemorySaver, str, str, str, str]:
"""Four-checkpoint chain spanning the migration boundary: """Three-checkpoint chain: cp1 (pre-delta, blob=[A]), cp2 (delta,
write=B), cp3 (delta, write=C). Reconstructing at cp3 must yield
* `cp0` pre-delta ancestor OLDER than the seed. Its write seed=[A] + writes=[B, C].
(`OLDER-WRITE`) is already folded into `cp1`'s stored value, so the
walk must terminate at `cp1` and never reach it.
* `cp1` pre-delta, blob `["A"]`. That value is the state ENTERING
`cp1`; the write stored under `cp1` (`PRE-DELTA-WRITE`) is what
produced `cp2` and is NOT subsumed by the blob.
* `cp2` delta-era, no stored value, write `B`.
* `cp3` target, delta-era, write `PENDING-AT-TARGET`.
Reconstructing at `cp3` must yield seed `["A"]` plus writes
`["PRE-DELTA-WRITE", "B"]`.
Returns `(saver, thread_id, ns, channel, cp3_id)`. Returns `(saver, thread_id, ns, channel, cp3_id)`.
""" """
@@ -597,21 +587,16 @@ class TestPreDeltaBlobTerminator:
serde = JsonPlusSerializer() serde = JsonPlusSerializer()
thread_id, ns, channel = "t1", "", "messages" thread_id, ns, channel = "t1", "", "messages"
v0 = "00000000000000000000000000000000.0"
v1 = "00000000000000000000000000000001.0" v1 = "00000000000000000000000000000001.0"
v2 = "00000000000000000000000000000002.0" v2 = "00000000000000000000000000000002.0"
v3 = "00000000000000000000000000000003.0" v3 = "00000000000000000000000000000003.0"
# Pre-delta: cp0 and cp1 stored real blobs for the channel. # Pre-delta: cp1 stored a real blob for the channel.
saver.blobs[(thread_id, ns, channel, v0)] = serde.dumps_typed([])
saver.blobs[(thread_id, ns, channel, v1)] = serde.dumps_typed(["A"]) saver.blobs[(thread_id, ns, channel, v1)] = serde.dumps_typed(["A"])
# Delta-era: cp2 and cp3 store "empty"; real writes in checkpoint_writes. # Delta-era: cp2 and cp3 store "empty"; real writes in checkpoint_writes.
saver.blobs[(thread_id, ns, channel, v2)] = ("empty", b"") saver.blobs[(thread_id, ns, channel, v2)] = ("empty", b"")
saver.blobs[(thread_id, ns, channel, v3)] = ("empty", b"") saver.blobs[(thread_id, ns, channel, v3)] = ("empty", b"")
cp0 = empty_checkpoint()
cp0["id"] = "cp0"
cp0["channel_versions"][channel] = v0
cp1 = empty_checkpoint() cp1 = empty_checkpoint()
cp1["id"] = "cp1" cp1["id"] = "cp1"
cp1["channel_versions"][channel] = v1 cp1["channel_versions"][channel] = v1
@@ -623,24 +608,16 @@ class TestPreDeltaBlobTerminator:
cp3["channel_versions"][channel] = v3 cp3["channel_versions"][channel] = v3
saver.storage[thread_id][ns] = { saver.storage[thread_id][ns] = {
"cp0": (serde.dumps_typed(cp0), serde.dumps_typed({}), None), "cp1": (serde.dumps_typed(cp1), serde.dumps_typed({}), None),
"cp1": (serde.dumps_typed(cp1), serde.dumps_typed({}), "cp0"),
"cp2": (serde.dumps_typed(cp2), serde.dumps_typed({}), "cp1"), "cp2": (serde.dumps_typed(cp2), serde.dumps_typed({}), "cp1"),
"cp3": (serde.dumps_typed(cp3), serde.dumps_typed({}), "cp2"), "cp3": (serde.dumps_typed(cp3), serde.dumps_typed({}), "cp2"),
} }
# Write under cp0 is older than the seed — cp1's blob already folded # Write under cp1 would be from the pre-delta era and MUST be ignored
# it in, and the terminator must stop before reaching it. # (the blob already captures it). We add one and assert it is not
saver.writes[(thread_id, ns, "cp0")][("task0", 0)] = ( # folded into the reconstructed result.
saver.writes[(thread_id, ns, "cp1")][("task0", 0)] = (
"task0", "task0",
channel, channel,
serde.dumps_typed("OLDER-WRITE"),
"",
)
# Write under cp1 postdates cp1's blob (it is what produced cp2, which
# stores no value of its own) and MUST be replayed.
saver.writes[(thread_id, ns, "cp1")][("task1", 0)] = (
"task1",
channel,
serde.dumps_typed("PRE-DELTA-WRITE"), serde.dumps_typed("PRE-DELTA-WRITE"),
"", "",
) )
@@ -674,19 +651,15 @@ class TestPreDeltaBlobTerminator:
# Seed came from the pre-delta blob at cp1. # Seed came from the pre-delta blob at cp1.
assert result["seed"] == ["A"] assert result["seed"] == ["A"]
# The seed ancestor's own write and the delta-era write from cp2 both # Delta-era writes from cp2 replay through the reducer on top of seed.
# replay through the reducer on top of the seed, oldest first. cp3 is # cp3 is the target — its own write is pending for the NEXT step and
# the target — its own write is pending for the NEXT step and must be # must be excluded.
# excluded.
values = [v for _, _, v in result["writes"]] values = [v for _, _, v in result["writes"]]
assert values == ["PRE-DELTA-WRITE", "B"] assert values == ["B"]
def test_seed_bounds_walk_without_dropping_its_own_writes(self) -> None: def test_pre_delta_blob_terminates_walk_before_older_writes(self) -> None:
"""The seed terminator bounds the walk: writes at ancestors OLDER than """Writes stored at the pre-delta ancestor itself must not be replayed
the seed are already folded into the seed value and must not be (the blob subsumes them)."""
replayed. The seed ancestor's own write is not one of them — it
postdates the stored value and produced the next checkpoint.
"""
saver, thread_id, ns, channel, target = self._build_mixed_thread() saver, thread_id, ns, channel, target = self._build_mixed_thread()
config: RunnableConfig = { config: RunnableConfig = {
"configurable": { "configurable": {
@@ -701,9 +674,7 @@ class TestPreDeltaBlobTerminator:
] ]
values = [v for _, _, v in result["writes"]] values = [v for _, _, v in result["writes"]]
# Older than the seed — subsumed by cp1's blob, so the walk stops first. # The pre-delta write under cp1 must not appear (the blob subsumes it).
assert "OLDER-WRITE" not in values assert "PRE-DELTA-WRITE" not in values
# Stored AT the seed ancestor — not subsumed, so it must be replayed.
assert "PRE-DELTA-WRITE" in values
# And the pending write at the target is never folded in. # And the pending write at the target is never folded in.
assert "PENDING-AT-TARGET" not in values assert "PENDING-AT-TARGET" not in values
+1 -15
View File
@@ -1,9 +1,7 @@
import asyncio import asyncio
import json import json
from collections import UserDict from collections.abc import Iterable
from collections.abc import Iterable, Mapping
from datetime import datetime from datetime import datetime
from types import MappingProxyType
from typing import Any from typing import Any
import pytest import pytest
@@ -139,18 +137,6 @@ def test_get_text_at_path() -> None:
assert get_text_at_path(nested_data, "nested[{invalid}]") == [] assert get_text_at_path(nested_data, "nested[{invalid}]") == []
@pytest.mark.parametrize(
"mapping",
[
UserDict({"text": "searchable"}),
MappingProxyType({"text": "searchable"}),
],
)
def test_get_text_at_path_with_non_dict_mapping(mapping: Mapping[str, str]) -> None:
assert get_text_at_path(mapping, "$") == ['{"text": "searchable"}']
assert get_text_at_path(mapping, "text") == ["searchable"]
async def test_async_batch_store(mocker: MockerFixture) -> None: async def test_async_batch_store(mocker: MockerFixture) -> None:
abatch = mocker.stub() abatch = mocker.stub()
+49 -50
View File
@@ -149,11 +149,11 @@ wheels = [
[[package]] [[package]]
name = "codespell" name = "codespell"
version = "2.4.3" version = "2.4.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/80/19/45e941380f69c042b43423513d201e6592346f992394347f5e7174c31407/codespell-2.4.3.tar.gz", hash = "sha256:cbe085e331227b37bb86ef8bddd08dc768c704ee9a07ca869852c093fa2793e2", size = 352773, upload-time = "2026-07-15T11:51:54.159Z" } sdist = { url = "https://files.pythonhosted.org/packages/2d/9d/1d0903dff693160f893ca6abcabad545088e7a2ee0a6deae7c24e958be69/codespell-2.4.2.tar.gz", hash = "sha256:3c33be9ae34543807f088aeb4832dfad8cb2dae38da61cac0a7045dd376cfdf3", size = 352058, upload-time = "2026-03-05T18:10:42.936Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/bf/bdb951d34eb169140b546f44be9ec4525d1acefb9eb5572071f5492b19fc/codespell-2.4.3-py3-none-any.whl", hash = "sha256:af2505b335e8573dbd2d384d1c4ef498f4006f4ba2d6fceca01e55b91f52628a", size = 340736, upload-time = "2026-07-15T11:51:52.925Z" }, { url = "https://files.pythonhosted.org/packages/42/a1/52fa05533e95fe45bcc09bcf8a503874b1c08f221a4e35608017e0938f55/codespell-2.4.2-py3-none-any.whl", hash = "sha256:97e0c1060cf46bd1d5db89a936c98db8c2b804e1fdd4b5c645e82a1ec6b1f886", size = 353715, upload-time = "2026-03-05T18:10:41.398Z" },
] ]
[[package]] [[package]]
@@ -183,7 +183,7 @@ name = "exceptiongroup"
version = "1.3.1" version = "1.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
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" } 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 = [ wheels = [
@@ -268,10 +268,9 @@ wheels = [
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "1.6.1" version = "1.4.8"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "httpx" },
{ name = "jsonpatch" }, { name = "jsonpatch" },
{ name = "langchain-protocol" }, { name = "langchain-protocol" },
{ name = "langsmith" }, { name = "langsmith" },
@@ -282,9 +281,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "uuid-utils" }, { 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/12/e3/bea6d0080acf183332f24dcd74c208aee5857cf8f783c3fb0bd86027d8fb/langchain_core-1.4.8.tar.gz", hash = "sha256:5bf1f8411077c904182ad8f975943d36adcbf579c4e017b3a118b719229ebf9a", size = 957974, upload-time = "2026-06-18T19:39:23.636Z" }
wheels = [ 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/13/d6/bdf6f0481cc57ef300d6b1eb48cf1400c0409be715d6eb3cabadd1142a09/langchain_core-1.4.8-py3-none-any.whl", hash = "sha256:d84c28b05e3ba8d4271d0827aad5b592ccdaaf986e76768c23503f0a2045e8aa", size = 557416, upload-time = "2026-06-18T19:39:21.902Z" },
] ]
[[package]] [[package]]
@@ -301,7 +300,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph-checkpoint" name = "langgraph-checkpoint"
version = "4.2.0" version = "4.1.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -1125,14 +1124,14 @@ wheels = [
[[package]] [[package]]
name = "redis" name = "redis"
version = "8.1.0" version = "8.0.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "async-timeout", marker = "python_full_version < '3.11.3'" }, { name = "async-timeout", marker = "python_full_version < '3.11.3'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/a8/99/604f0b666d4c616d891cf77ebb9db6bb21601344c051aebf1b72b9ff915f/redis-8.1.0.tar.gz", hash = "sha256:6e1a19beef9225c83efd689c7e6b7da2d5215b1f42cd13b7fc3714d0a09c7b25", size = 5254356, upload-time = "2026-07-30T08:51:00.269Z" } sdist = { url = "https://files.pythonhosted.org/packages/cc/c3/928b290c2c0ca99ab96eea5b4ff8f30be8112b075301a7d3ba214a3c8c12/redis-8.0.1.tar.gz", hash = "sha256:afc5a7a2f5a084f5b1880dec548dd45be17db7e43c82a30d84f952aefb05cfb0", size = 5114170, upload-time = "2026-06-23T14:52:37.728Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/66/9d/c5731f6e3608663d4d3656fd8d3aecee8b509c3082818f5a13eae925baea/redis-8.1.0-py3-none-any.whl", hash = "sha256:a4fe1aac3d3b3cc791d4b3d5931c5a956045dc951ee74d1c913ee3ac4d2ee9fb", size = 560618, upload-time = "2026-07-30T08:50:58.497Z" }, { url = "https://files.pythonhosted.org/packages/fd/0a/c2345ebf1ebe70840ce3f6c6ee612f8fa749cfbd1b03069c53bf0c62aaad/redis-8.0.1-py3-none-any.whl", hash = "sha256:47daa35a058c23468d6437f17a8c76882cb316b838ef763036af99b96cedd743", size = 502406, upload-time = "2026-06-23T14:52:36.137Z" },
] ]
[[package]] [[package]]
@@ -1164,27 +1163,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.16.5" version = "0.15.20"
source = { registry = "https://pypi.org/simple" } 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/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" }
wheels = [ 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/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" },
{ 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/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" },
{ 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/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" },
{ 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/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" },
{ 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/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" },
{ 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/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" },
{ 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/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" },
{ 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/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" },
{ 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/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" },
{ 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/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" },
{ 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/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" },
{ 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/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" },
{ 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/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" },
{ 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/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" },
{ 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/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" },
{ 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/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" },
{ 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/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
] ]
[[package]] [[package]]
@@ -1261,27 +1260,27 @@ wheels = [
[[package]] [[package]]
name = "ty" name = "ty"
version = "0.0.75" version = "0.0.55"
source = { registry = "https://pypi.org/simple" } 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/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" }
wheels = [ 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/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" },
{ 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/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" },
{ 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/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" },
{ 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/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" },
{ 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/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" },
{ 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/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" },
{ 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/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" },
{ 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/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" },
{ 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/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" },
{ 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/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" },
{ 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/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" },
{ 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/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" },
{ 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/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" },
{ 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/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" },
{ 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/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" },
{ 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/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" },
{ 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/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" },
] ]
[[package]] [[package]]
+10 -10
View File
@@ -21,28 +21,28 @@
"test:all": "yarn test && yarn test:int && yarn lint:langgraph" "test:all": "yarn test && yarn test:int && yarn lint:langgraph"
}, },
"dependencies": { "dependencies": {
"@langchain/core": "^1.2.9", "@langchain/core": "^1.2.1",
"@langchain/langgraph": "^1.4.13" "@langchain/langgraph": "^1.4.7"
}, },
"resolutions": { "resolutions": {
"@langchain/langgraph-checkpoint": "1.0.4" "@langchain/langgraph-checkpoint": "1.0.4"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.3.6", "@eslint/eslintrc": "^3.3.5",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@tsconfig/recommended": "^1.0.13", "@tsconfig/recommended": "^1.0.13",
"@types/jest": "^30.0.0", "@types/jest": "^30.0.0",
"@typescript-eslint/eslint-plugin": "^8.68.0", "@typescript-eslint/eslint-plugin": "^8.62.1",
"@typescript-eslint/parser": "^8.68.0", "@typescript-eslint/parser": "^8.62.1",
"dotenv": "^17.4.2", "dotenv": "^17.4.2",
"eslint": "^10.9.1", "eslint": "^10.6.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-no-instanceof": "^1.0.1", "eslint-plugin-no-instanceof": "^1.0.1",
"eslint-plugin-prettier": "^5.5.6", "eslint-plugin-prettier": "^5.5.6",
"jest": "^30.5.0", "jest": "^30.4.2",
"prettier": "^3.9.6", "prettier": "^3.9.4",
"ts-jest": "^29.4.12", "ts-jest": "^29.4.11",
"typescript": "^7.0.2" "typescript": "^6.0.3"
} }
} }
File diff suppressed because it is too large Load Diff
@@ -9,10 +9,10 @@
}, },
"dependencies": { "dependencies": {
"@js-monorepo-example/shared": "*", "@js-monorepo-example/shared": "*",
"@langchain/core": "^1.2.9", "@langchain/core": "^1.2.1",
"@langchain/langgraph": "^1.4.13" "@langchain/langgraph": "^1.4.7"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^7.0.2" "typescript": "^6.0.3"
} }
} }
@@ -9,6 +9,6 @@
"clean": "rm -rf dist" "clean": "rm -rf dist"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^7.0.2" "typescript": "^6.0.3"
} }
} }
+7 -7
View File
@@ -17,18 +17,18 @@
"lint": "eslint 'apps/**/*.ts' 'libs/**/*.ts'" "lint": "eslint 'apps/**/*.ts' 'libs/**/*.ts'"
}, },
"devDependencies": { "devDependencies": {
"turbo": "^2.10.12", "turbo": "^2.10.2",
"typescript": "^7.0.2", "typescript": "^6.0.3",
"@tsconfig/recommended": "^1.0.13", "@tsconfig/recommended": "^1.0.13",
"@eslint/eslintrc": "^3.3.6", "@eslint/eslintrc": "^3.3.5",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"eslint": "^10.9.1", "eslint": "^10.6.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-no-instanceof": "^1.0.1", "eslint-plugin-no-instanceof": "^1.0.1",
"eslint-plugin-prettier": "^5.5.6", "eslint-plugin-prettier": "^5.5.6",
"@typescript-eslint/eslint-plugin": "^8.68.0", "@typescript-eslint/eslint-plugin": "^8.62.1",
"@typescript-eslint/parser": "^8.68.0", "@typescript-eslint/parser": "^8.62.1",
"prettier": "^3.9.6" "prettier": "^3.9.4"
} }
} }
+152 -288
View File
@@ -28,10 +28,10 @@
debug "^4.3.1" debug "^4.3.1"
minimatch "^10.2.4" minimatch "^10.2.4"
"@eslint/config-helpers@^0.7.0": "@eslint/config-helpers@^0.6.0":
version "0.7.0" version "0.6.0"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.7.0.tgz#09ee4aa07b73f059ec2d4c74bf4b2ff02b322377" resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.6.0.tgz#ef9a36881d39dfd5dbeac22b0da997fabfb08b03"
integrity sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw== integrity sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==
dependencies: dependencies:
"@eslint/core" "^1.2.1" "@eslint/core" "^1.2.1"
@@ -42,10 +42,10 @@
dependencies: dependencies:
"@types/json-schema" "^7.0.15" "@types/json-schema" "^7.0.15"
"@eslint/eslintrc@^3.3.6": "@eslint/eslintrc@^3.3.5":
version "3.3.6" version "3.3.5"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.6.tgz#d22bfd6b3a7d8e1f2c0b2f2e6de111b53ec6e13e" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.5.tgz#c131793cfc1a7b96f24a83e0a8bbd4b881558c60"
integrity sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA== integrity sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==
dependencies: dependencies:
ajv "^6.14.0" ajv "^6.14.0"
debug "^4.3.2" debug "^4.3.2"
@@ -53,7 +53,7 @@
globals "^14.0.0" globals "^14.0.0"
ignore "^5.2.0" ignore "^5.2.0"
import-fresh "^3.2.1" import-fresh "^3.2.1"
js-yaml "^4.3.0" js-yaml "^4.1.1"
minimatch "^3.1.5" minimatch "^3.1.5"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
@@ -103,10 +103,10 @@
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-9.0.0.tgz#4d0a3f127058043bf2e7ee169eaf30ed901302f3" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-9.0.0.tgz#4d0a3f127058043bf2e7ee169eaf30ed901302f3"
integrity sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg== integrity sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==
"@langchain/core@^1.2.9": "@langchain/core@^1.2.1":
version "1.2.9" version "1.2.1"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.2.9.tgz#4f5fb27ba07c51ce4fb8e1dc32eb36945737afa1" resolved "https://registry.yarnpkg.com/@langchain/core/-/core-1.2.1.tgz#46751258ee56a6ae47ae578eba7928d3995d11ed"
integrity sha512-conzSEj9Zu1AyXJLXsSbgrtxtxinmI1yGqQ5CIJZSoV5rvv+yvQE/vgBnoySpBQ/bl3YPgj2FL/gbDjWykLSfg== integrity sha512-NNG/cC5FGuHDOAP56h0ddp8Rfk8p+othWzEK5RV9JIG6RvnF5vGa5r0AEGtKfQieed7s1kC42GuIzVOBvMBL/g==
dependencies: dependencies:
"@cfworker/json-schema" "^4.0.2" "@cfworker/json-schema" "^4.0.2"
"@standard-schema/spec" "^1.1.0" "@standard-schema/spec" "^1.1.0"
@@ -116,28 +116,28 @@
p-queue "^6.6.2" p-queue "^6.6.2"
zod "^3.25.76 || ^4" zod "^3.25.76 || ^4"
"@langchain/langgraph-checkpoint@^1.1.5": "@langchain/langgraph-checkpoint@^1.1.3":
version "1.1.5" version "1.1.3"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.1.5.tgz#c793177f9afcce31a1e9922f317f88fec1254282" resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.1.3.tgz#891f2f9a2e96cf2ab0920f9240f9ad1e0a86a4ab"
integrity sha512-BwDwl5VeTOh6CVuiIPgsUgfK51vTJDMSbFcSCUfjJWsl8/DPdK/mbv+ejxJstkSk/BlSPMP4JfXWcN6jD2ea2Q== integrity sha512-wgzdQNeEsdw1e+4lvlj0tdq/RYR/k1vPin10g0ymGoehZDDgd9nvIllGXSXN4TFgF9sf5qQP/KTkOcLfeseIhA==
"@langchain/langgraph-sdk@~1.10.0": "@langchain/langgraph-sdk@~1.9.25":
version "1.10.0" version "1.9.25"
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.10.0.tgz#6ec1364a97caf615983161ae1f00b196897b9cd6" resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.9.25.tgz#034baf46bb626313761f1006cb554e30d797e310"
integrity sha512-cPPkh+hMNgeOaGtJRrqs1AjZde45cG2+Ma9Sc10wz2RyvT8SKToCKS+VvkS18SsLajnmq6/FKVmthq6rnUVYOw== integrity sha512-mRKW8zyQUaHox+HirRFMRrPqOvNbQI3xeXDt6kkk4PbBg77V92bsO1WzUVNrmJ81zCkvxyOrWSK8D6ioCj0a8A==
dependencies: dependencies:
"@langchain/protocol" "^0.0.19" "@langchain/protocol" "^0.0.18"
"@types/json-schema" "^7.0.15" "@types/json-schema" "^7.0.15"
p-queue "^9.0.1" p-queue "^9.0.1"
p-retry "^7.1.1" p-retry "^7.1.1"
"@langchain/langgraph@^1.4.13": "@langchain/langgraph@^1.4.7":
version "1.4.13" version "1.4.7"
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.4.13.tgz#168ab4f05c212fd5ab4b2816bcad2e963b345101" resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-1.4.7.tgz#fed2157855ece936df27d812e6c5b21c515f7294"
integrity sha512-LO1ak6jNQ9jR13tm7Ay4Yh2/otrH7LNVUwWTAI7WJigVdW5Fb6LuYSZUzVn4S7sVSiyVFfnrUcDTd8c7eAzPrQ== integrity sha512-2tcyf3QGC7v89kqSxMCtRvzg/3L/4yHtOaWC49A8KieCciWJs7LGaxHoPB6QRxXyUgyR+Zg9Q1ss/XJIE+JuSQ==
dependencies: dependencies:
"@langchain/langgraph-checkpoint" "^1.1.5" "@langchain/langgraph-checkpoint" "^1.1.3"
"@langchain/langgraph-sdk" "~1.10.0" "@langchain/langgraph-sdk" "~1.9.25"
"@langchain/protocol" "^0.0.18" "@langchain/protocol" "^0.0.18"
"@standard-schema/spec" "1.1.0" "@standard-schema/spec" "1.1.0"
@@ -146,11 +146,6 @@
resolved "https://registry.yarnpkg.com/@langchain/protocol/-/protocol-0.0.18.tgz#6d96155e7263c958fbce6d4b7241b4725b72fbad" resolved "https://registry.yarnpkg.com/@langchain/protocol/-/protocol-0.0.18.tgz#6d96155e7263c958fbce6d4b7241b4725b72fbad"
integrity sha512-XW1egQtPfsGI41w2AMZNFZrUIwFSQHTjVMZs0OaTpCAvht/QLoaPN8FQcsysMVypOhupG28J29yOorrc70otBQ== integrity sha512-XW1egQtPfsGI41w2AMZNFZrUIwFSQHTjVMZs0OaTpCAvht/QLoaPN8FQcsysMVypOhupG28J29yOorrc70otBQ==
"@langchain/protocol@^0.0.19":
version "0.0.19"
resolved "https://registry.yarnpkg.com/@langchain/protocol/-/protocol-0.0.19.tgz#7fe43fed115dc34b3b4c246e8d5283fbcbfab7b0"
integrity sha512-9hKcRrH7cBX6gfutdfXPoft1OCchHe4FEpALoDJMl5Qu+n/YG5ynZmyu8+8cxORlPwHBoKTxggvXz+76M1yX1Q==
"@pkgr/core@^0.3.6": "@pkgr/core@^0.3.6":
version "0.3.6" version "0.3.6"
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.3.6.tgz#3569708bd4be4d8870ba32bf1c456dac81600d97" resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.3.6.tgz#3569708bd4be4d8870ba32bf1c456dac81600d97"
@@ -171,35 +166,35 @@
resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.13.tgz#269fce3ad04ca70b93269ff44cca81b950f542da" resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.13.tgz#269fce3ad04ca70b93269ff44cca81b950f542da"
integrity sha512-sySRuBfMKyKO/j2ZAhR8kSembhjuPEV4Ra3AHtmWLq51+iGaudr45crPSzNC5b7/Ctrh9dfUpBuTlYrH6rM58Q== integrity sha512-sySRuBfMKyKO/j2ZAhR8kSembhjuPEV4Ra3AHtmWLq51+iGaudr45crPSzNC5b7/Ctrh9dfUpBuTlYrH6rM58Q==
"@turbo/darwin-64@2.10.12": "@turbo/darwin-64@2.10.2":
version "2.10.12" version "2.10.2"
resolved "https://registry.yarnpkg.com/@turbo/darwin-64/-/darwin-64-2.10.12.tgz#719ac12ae47caf7b4be855d672582cdbfde177ea" resolved "https://registry.yarnpkg.com/@turbo/darwin-64/-/darwin-64-2.10.2.tgz#224d47639e882a23985664f69278c61be37a2602"
integrity sha512-9nKgKoF6ZOUsM+or0OtNf+TTJSfGvDNP7ZFv/ZGWVwOSCkumyctQiTeHwB4UNljHTnC41AqylgbunLDHoccNrA== integrity sha512-wBM3ObqOWnKUDmg7QfUFDkDHPFUAJmrYlYqmEM8jMPAPA/I6wRJIbWimeQUqhOiQ8xPKhzyWM+xaiUP0wz8FEQ==
"@turbo/darwin-arm64@2.10.12": "@turbo/darwin-arm64@2.10.2":
version "2.10.12" version "2.10.2"
resolved "https://registry.yarnpkg.com/@turbo/darwin-arm64/-/darwin-arm64-2.10.12.tgz#ac2d3dde3a2407f8359ca2e3152690280cff2714" resolved "https://registry.yarnpkg.com/@turbo/darwin-arm64/-/darwin-arm64-2.10.2.tgz#c6f075855a10e87ee0f5db7582432906cf05b484"
integrity sha512-H4Elb1jqTZVeIC9bbcNwjSzemZ6RegoTOVHeuV5Osirt2Z8UguTyisMEkvZjPVZgMeN9J4ERZBFad40tFnkb7w== integrity sha512-/Cq0joWnuMjDPfhjbFP4sv+C/7gkQ415zlaO4XUzD5EZxbtrKgXKvuuydMvogG8GeUnN1aDltW71RlmEfpjbyw==
"@turbo/linux-64@2.10.12": "@turbo/linux-64@2.10.2":
version "2.10.12" version "2.10.2"
resolved "https://registry.yarnpkg.com/@turbo/linux-64/-/linux-64-2.10.12.tgz#264a0ec88a1f69f93cf57dab20be1b4c50bf226c" resolved "https://registry.yarnpkg.com/@turbo/linux-64/-/linux-64-2.10.2.tgz#d4251e876fda269a3a9b1c02ee668dbb3187b2a5"
integrity sha512-lr7KIotukvjZwEXiFSYAeOH3BWzjFVBbSzTbv0fuGFsNukYyH0+g1hB5ecqnJkgkYU+KHEMG1edOhnjiKON1wQ== integrity sha512-mMsf5IIhiKuceEXNstd25IbadjBXZ0amxzFOqliEzJX6HyeeHdBQPVSY583PWqYDyqM/FB8d5ZjkthfBSeuH3Q==
"@turbo/linux-arm64@2.10.12": "@turbo/linux-arm64@2.10.2":
version "2.10.12" version "2.10.2"
resolved "https://registry.yarnpkg.com/@turbo/linux-arm64/-/linux-arm64-2.10.12.tgz#3ee53d1f930d2bb65708e904971be0e765381e37" resolved "https://registry.yarnpkg.com/@turbo/linux-arm64/-/linux-arm64-2.10.2.tgz#2a68be00512a6874fe972dcf028a8f6d7b0b7620"
integrity sha512-f0pZDTtvzB5SuNwuXBaKbZHUCMCukgc8nMlHEuvLmj91Fzec+MEbr3cAvGNor5htEDqZnO6Lxt9N/GPI/77oGA== integrity sha512-Wcng1i2kaKmXutmwxT9MUoYZvdaIekXAdlGr4+0TpgbhGLw7nDuEcRBFrxb5BbRoX1d1q8SpdRxLc45TvDZIdQ==
"@turbo/windows-64@2.10.12": "@turbo/windows-64@2.10.2":
version "2.10.12" version "2.10.2"
resolved "https://registry.yarnpkg.com/@turbo/windows-64/-/windows-64-2.10.12.tgz#ac0a0b7794f9a541e68e930383f48747f7e648ab" resolved "https://registry.yarnpkg.com/@turbo/windows-64/-/windows-64-2.10.2.tgz#f2233aec870c45d12b3dd8f1dfc767a042498151"
integrity sha512-SDOueJRjS/QcykWf2KCRtTLmIl5YMKsLbXkXQGhDwcTXvKXZiS5ih5lBl/gkwZIpYFjqA/rAlfMzlAFcVHNe0g== integrity sha512-SsNhM7Ho7EpAdwtrJKBOic9Hso23vu6Dp0gAfLOvUFjPzurr/sGQlXZEvr6z89ne4RDOypTwz5CBDrixpMKtXw==
"@turbo/windows-arm64@2.10.12": "@turbo/windows-arm64@2.10.2":
version "2.10.12" version "2.10.2"
resolved "https://registry.yarnpkg.com/@turbo/windows-arm64/-/windows-arm64-2.10.12.tgz#50eb6c20a20d0aab216d2b6aa13cc3100ea44106" resolved "https://registry.yarnpkg.com/@turbo/windows-arm64/-/windows-arm64-2.10.2.tgz#c7b8c5b7c30b1fb3b7dac33dda9cb161fe09f461"
integrity sha512-0i0mVUa4kKk+/B3RwEwPMf9CB+T7ul56hn5FFHNA4VUNTOoLBEd6aNf3FaKfCatDNZ6cicCEf6if9QUTVyzzcA== integrity sha512-Gf+S7ICAdimT/n02bOuVWKvhHnct/HYjZg3oBNIz5hZ9ZyWHbQim9J3P5Qip8WpX0ksxF7eaBVziJCuLnjhqDg==
"@types/esrecurse@^4.3.1": "@types/esrecurse@^4.3.1":
version "4.3.1" version "4.3.1"
@@ -221,212 +216,102 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@typescript-eslint/eslint-plugin@^8.68.0": "@typescript-eslint/eslint-plugin@^8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.68.0.tgz#a8fbdb1cf49aafaf16071b646daad890151bd149" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz#1736dcdca6cae3359d818456a47d18b674761f7f"
integrity sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q== integrity sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==
dependencies: dependencies:
"@eslint-community/regexpp" "^4.12.2" "@eslint-community/regexpp" "^4.12.2"
"@typescript-eslint/scope-manager" "8.68.0" "@typescript-eslint/scope-manager" "8.62.1"
"@typescript-eslint/type-utils" "8.68.0" "@typescript-eslint/type-utils" "8.62.1"
"@typescript-eslint/utils" "8.68.0" "@typescript-eslint/utils" "8.62.1"
"@typescript-eslint/visitor-keys" "8.68.0" "@typescript-eslint/visitor-keys" "8.62.1"
ignore "^7.0.5" ignore "^7.0.5"
natural-compare "^1.4.0" natural-compare "^1.4.0"
ts-api-utils "^2.5.0" ts-api-utils "^2.5.0"
"@typescript-eslint/parser@^8.68.0": "@typescript-eslint/parser@^8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.68.0.tgz#61de31481354c50457bc9621a7ed746779f09ee7" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.62.1.tgz#d3f7ba18f1bf78bfb7256fea021d1927b48e7080"
integrity sha512-fHq2VC1kpyYfvEcbiMjOpySY4WS7voEp89yAThrHRX5sm9j2lzYppCb2umFMEed4fWcyeLjHxrz0mpjNBaBxMQ== integrity sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "8.68.0" "@typescript-eslint/scope-manager" "8.62.1"
"@typescript-eslint/types" "8.68.0" "@typescript-eslint/types" "8.62.1"
"@typescript-eslint/typescript-estree" "8.68.0" "@typescript-eslint/typescript-estree" "8.62.1"
"@typescript-eslint/visitor-keys" "8.68.0" "@typescript-eslint/visitor-keys" "8.62.1"
debug "^4.4.3" debug "^4.4.3"
"@typescript-eslint/project-service@8.68.0": "@typescript-eslint/project-service@8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.68.0.tgz#ea4b2869f59165c420cd7a4bbebc38039794e8cc" resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.62.1.tgz#78d880eb1cf6859b5ec263d04f95403e9f90ae47"
integrity sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw== integrity sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==
dependencies: dependencies:
"@typescript-eslint/tsconfig-utils" "^8.68.0" "@typescript-eslint/tsconfig-utils" "^8.62.1"
"@typescript-eslint/types" "^8.68.0" "@typescript-eslint/types" "^8.62.1"
debug "^4.4.3" debug "^4.4.3"
"@typescript-eslint/scope-manager@8.68.0": "@typescript-eslint/scope-manager@8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.68.0.tgz#e5a13a1159497faeab4e48279bf07576045b1499" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz#7ee65e9a6eb3ccdc4816593a4ff38840306de88a"
integrity sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA== integrity sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==
dependencies: dependencies:
"@typescript-eslint/types" "8.68.0" "@typescript-eslint/types" "8.62.1"
"@typescript-eslint/visitor-keys" "8.68.0" "@typescript-eslint/visitor-keys" "8.62.1"
"@typescript-eslint/tsconfig-utils@8.68.0": "@typescript-eslint/tsconfig-utils@8.62.1", "@typescript-eslint/tsconfig-utils@^8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.68.0.tgz#594d7a3c5952055b3c431fc563ca7fd1defcce18" resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz#e2b5f24fe721044189cb7e81117c96d75979d627"
integrity sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw== integrity sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==
"@typescript-eslint/tsconfig-utils@^8.68.0": "@typescript-eslint/type-utils@8.62.1":
version "8.69.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz#d3b0ccc781ab252a90a0b3989b9d1eb85ab59469" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz#ebd30b13bacb13070917259a23309cf644121f9a"
integrity sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ== integrity sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==
"@typescript-eslint/type-utils@8.68.0":
version "8.68.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.68.0.tgz#8f3e838dbd740909db27053857468cd037b00220"
integrity sha512-X77zqoY1EjeWGs/0JNxeaMfp5C5lIz4Tw8y66F1Ne8Faq6g424sBNYM6xBAqElfGZPLpWS+CZAp0DXyKDzWiHg==
dependencies: dependencies:
"@typescript-eslint/types" "8.68.0" "@typescript-eslint/types" "8.62.1"
"@typescript-eslint/typescript-estree" "8.68.0" "@typescript-eslint/typescript-estree" "8.62.1"
"@typescript-eslint/utils" "8.68.0" "@typescript-eslint/utils" "8.62.1"
debug "^4.4.3" debug "^4.4.3"
ts-api-utils "^2.5.0" ts-api-utils "^2.5.0"
"@typescript-eslint/types@8.68.0": "@typescript-eslint/types@8.62.1", "@typescript-eslint/types@^8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.68.0.tgz#3f9d4e62fbe5728f09403cdc7b4d58af842ac1af" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.62.1.tgz#c58be954e483b2fc98275374d5bcb40b99842dc1"
integrity sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg== integrity sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==
"@typescript-eslint/types@^8.68.0": "@typescript-eslint/typescript-estree@8.62.1":
version "8.69.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.69.0.tgz#5d9ad3f707c2e4f70a2db540031104df3e63bcf5" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz#98c1bb17635d5b026b24193a8d29188ac64380ff"
integrity sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA== integrity sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==
"@typescript-eslint/typescript-estree@8.68.0":
version "8.68.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.68.0.tgz#bf4165029825138ac27231a3ff02923ecd977f38"
integrity sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==
dependencies: dependencies:
"@typescript-eslint/project-service" "8.68.0" "@typescript-eslint/project-service" "8.62.1"
"@typescript-eslint/tsconfig-utils" "8.68.0" "@typescript-eslint/tsconfig-utils" "8.62.1"
"@typescript-eslint/types" "8.68.0" "@typescript-eslint/types" "8.62.1"
"@typescript-eslint/visitor-keys" "8.68.0" "@typescript-eslint/visitor-keys" "8.62.1"
debug "^4.4.3" debug "^4.4.3"
minimatch "^10.2.2" minimatch "^10.2.2"
semver "^7.7.3" semver "^7.7.3"
tinyglobby "^0.2.15" tinyglobby "^0.2.15"
ts-api-utils "^2.5.0" ts-api-utils "^2.5.0"
"@typescript-eslint/utils@8.68.0": "@typescript-eslint/utils@8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.68.0.tgz#00547f2c8de8aca2a3c21752a9711f73206fd36d" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.62.1.tgz#1622b75c7e6df308181dd0b44855dc4228da0457"
integrity sha512-PB5gJMMOg0Q5P1tsgWtEAqQacJXq0qEqRHDX/YJ4FaTMLfZPpHB3gjl2EJuiZyPABxmj4ZQYiY9m1bdAJ5y7tQ== integrity sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.9.1" "@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.68.0" "@typescript-eslint/scope-manager" "8.62.1"
"@typescript-eslint/types" "8.68.0" "@typescript-eslint/types" "8.62.1"
"@typescript-eslint/typescript-estree" "8.68.0" "@typescript-eslint/typescript-estree" "8.62.1"
"@typescript-eslint/visitor-keys@8.68.0": "@typescript-eslint/visitor-keys@8.62.1":
version "8.68.0" version "8.62.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.68.0.tgz#78db3c9bb258a0309d9e2b1b617127c3a8fb1f54" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz#499657d77ffafb8a99eb1d6c97847ca430234722"
integrity sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A== integrity sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==
dependencies: dependencies:
"@typescript-eslint/types" "8.68.0" "@typescript-eslint/types" "8.62.1"
eslint-visitor-keys "^5.0.0" eslint-visitor-keys "^5.0.0"
"@typescript/typescript-aix-ppc64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz#cdc7ce81d60f1e09034960ddfb1fb880d7a776b6"
integrity sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==
"@typescript/typescript-darwin-arm64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz#a55fdfcfa58df58d27db2237cde6a5c1e35a7235"
integrity sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==
"@typescript/typescript-darwin-x64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz#38d1c9172800a91d707bec64d2a370a016634db4"
integrity sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==
"@typescript/typescript-freebsd-arm64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz#f1ff8810030b35d2b5be0db6a2dc650460ea94fa"
integrity sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==
"@typescript/typescript-freebsd-x64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz#3d86b03f353c5b1ba95162eb6ce35533bfc294bd"
integrity sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==
"@typescript/typescript-linux-arm64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz#d9334d96d6dac6ff85da9c865588948de939e91f"
integrity sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==
"@typescript/typescript-linux-arm@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz#ad94b41e1aee2a4dcc6a298c7b67c43345fde32e"
integrity sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==
"@typescript/typescript-linux-loong64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz#2965aee4fc873360139d893daafe6397a29138ad"
integrity sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==
"@typescript/typescript-linux-mips64el@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz#1a887a311bed3a833f80bfd4a9ed37c271936cf0"
integrity sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==
"@typescript/typescript-linux-ppc64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz#8b63c9b2f445b393eb4e43ec21da225dade3577d"
integrity sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==
"@typescript/typescript-linux-riscv64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz#b6e8a35c289b3ea97a92a41d461aaeed0d3b36e1"
integrity sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==
"@typescript/typescript-linux-s390x@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz#2ef96693be4861f6d17965427e5b009cbbed1a3e"
integrity sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==
"@typescript/typescript-linux-x64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz#73269cb0baba50aea0ca060445a6b88e583f1ce2"
integrity sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==
"@typescript/typescript-netbsd-arm64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz#3a3649f97fafa210b4e6e3798c15e06605c8a901"
integrity sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==
"@typescript/typescript-netbsd-x64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz#47ec59491a40c470d2807dc4d2b825528fd979ab"
integrity sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==
"@typescript/typescript-openbsd-arm64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz#796be8da0bd989d8a3fb96f2801e38a8365b4baf"
integrity sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==
"@typescript/typescript-openbsd-x64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz#d37fe2a729eb942c076c454ee7f1815faf7d560f"
integrity sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==
"@typescript/typescript-sunos-x64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz#aba8d3464c3565a7044789baba96916bd4ab2c88"
integrity sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==
"@typescript/typescript-win32-arm64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz#b9de50a17196383f62620b5f9d0a2f34ad3b60d7"
integrity sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==
"@typescript/typescript-win32-x64@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz#cf3b7b0d6ce5635daca4c8e01c189cdcde47ec3c"
integrity sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==
acorn-jsx@^5.3.2: acorn-jsx@^5.3.2:
version "5.3.2" version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
@@ -562,10 +447,10 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^5.0.8: brace-expansion@^5.0.2:
version "5.0.9" version "5.0.2"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.9.tgz#7c72438809b5fa5babf54199a1f1c281a6984fcf" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.2.tgz#b6c16d0791087af6c2bc463f52a8142046c06b6f"
integrity sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg== integrity sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==
dependencies: dependencies:
balanced-match "^4.0.2" balanced-match "^4.0.2"
@@ -886,15 +771,15 @@ eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be"
integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==
eslint@^10.9.1: eslint@^10.6.0:
version "10.9.1" version "10.6.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.9.1.tgz#409da5c41a5536d5a849f8555a18ca7ef1eb963b" resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.6.0.tgz#e1b4059c582be950c7088c9b55f984738b243c27"
integrity sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A== integrity sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/eslint-utils" "^4.8.0"
"@eslint-community/regexpp" "^4.12.2" "@eslint-community/regexpp" "^4.12.2"
"@eslint/config-array" "^0.23.5" "@eslint/config-array" "^0.23.5"
"@eslint/config-helpers" "^0.7.0" "@eslint/config-helpers" "^0.6.0"
"@eslint/core" "^1.2.1" "@eslint/core" "^1.2.1"
"@eslint/plugin-kit" "^0.7.2" "@eslint/plugin-kit" "^0.7.2"
"@humanfs/node" "^0.16.6" "@humanfs/node" "^0.16.6"
@@ -918,7 +803,7 @@ eslint@^10.9.1:
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
is-glob "^4.0.0" is-glob "^4.0.0"
json-stable-stringify-without-jsonify "^1.0.1" json-stable-stringify-without-jsonify "^1.0.1"
minimatch "^10.2.5" minimatch "^10.2.4"
natural-compare "^1.4.0" natural-compare "^1.4.0"
optionator "^0.9.3" optionator "^0.9.3"
@@ -1390,10 +1275,10 @@ js-tiktoken@^1.0.12:
dependencies: dependencies:
base64-js "^1.5.1" base64-js "^1.5.1"
js-yaml@^4.3.0: js-yaml@^4.1.1:
version "4.3.1" version "4.3.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.3.1.tgz#01216c001d67f48e2cd560d708c7af21090a3848" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.3.0.tgz#d1900572a7f7cf0b5f540c83673e60bad3436592"
integrity sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ== integrity sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
@@ -1453,12 +1338,12 @@ math-intrinsics@^1.1.0:
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
minimatch@^10.2.2, minimatch@^10.2.4, minimatch@^10.2.5: minimatch@^10.2.2, minimatch@^10.2.4:
version "10.2.6" version "10.2.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.6.tgz#fd956bbe0b77241e9f15ac5dccb1c638060968ef" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde"
integrity sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A== integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==
dependencies: dependencies:
brace-expansion "^5.0.8" brace-expansion "^5.0.2"
minimatch@^3.1.2, minimatch@^3.1.5: minimatch@^3.1.2, minimatch@^3.1.5:
version "3.1.5" version "3.1.5"
@@ -1657,10 +1542,10 @@ prettier-linter-helpers@^1.0.1:
dependencies: dependencies:
fast-diff "^1.1.2" fast-diff "^1.1.2"
prettier@^3.9.6: prettier@^3.9.4:
version "3.9.6" version "3.9.4"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.6.tgz#b3ea5146515d40fc53f18aa63f74dfab1e10dbf6" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.4.tgz#a9c477cf1614376bd1f6bbc593d8c0d414bcec87"
integrity sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g== integrity sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==
punycode@^2.1.0: punycode@^2.1.0:
version "2.3.1" version "2.3.1"
@@ -1913,17 +1798,17 @@ tsconfig-paths@^3.15.0:
minimist "^1.2.6" minimist "^1.2.6"
strip-bom "^3.0.0" strip-bom "^3.0.0"
turbo@^2.10.12: turbo@^2.10.2:
version "2.10.12" version "2.10.2"
resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.10.12.tgz#22f552bd88182d58365960a02e5f45e628fb965b" resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.10.2.tgz#7b8993242d550e3adf7f3245b231f663e5300ebf"
integrity sha512-AswgMPnpOoaVZHrrSBejETzEbuIA69OVGwfkHwfrY0A23VjWXBANzgq9+OymWOHAIArB7D1+1z498WY8fGg1Jw== integrity sha512-wTExrNrRjB8qzIcg+ZLm0A3GFNLDsWNwdS/RBXB0FPrBDyzk3i96Yx+TxWZC7a0k1SIreFB8ciUbxjmEqTH8IQ==
optionalDependencies: optionalDependencies:
"@turbo/darwin-64" "2.10.12" "@turbo/darwin-64" "2.10.2"
"@turbo/darwin-arm64" "2.10.12" "@turbo/darwin-arm64" "2.10.2"
"@turbo/linux-64" "2.10.12" "@turbo/linux-64" "2.10.2"
"@turbo/linux-arm64" "2.10.12" "@turbo/linux-arm64" "2.10.2"
"@turbo/windows-64" "2.10.12" "@turbo/windows-64" "2.10.2"
"@turbo/windows-arm64" "2.10.12" "@turbo/windows-arm64" "2.10.2"
type-check@^0.4.0, type-check@~0.4.0: type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0" version "0.4.0"
@@ -1977,31 +1862,10 @@ typed-array-length@^1.0.7:
possible-typed-array-names "^1.0.0" possible-typed-array-names "^1.0.0"
reflect.getprototypeof "^1.0.6" reflect.getprototypeof "^1.0.6"
typescript@^7.0.2: typescript@^6.0.3:
version "7.0.2" version "6.0.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-7.0.2.tgz#9ec773d7954a8c182c17cc5bbd575aa28bc51582" resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21"
integrity sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA== integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==
optionalDependencies:
"@typescript/typescript-aix-ppc64" "7.0.2"
"@typescript/typescript-darwin-arm64" "7.0.2"
"@typescript/typescript-darwin-x64" "7.0.2"
"@typescript/typescript-freebsd-arm64" "7.0.2"
"@typescript/typescript-freebsd-x64" "7.0.2"
"@typescript/typescript-linux-arm" "7.0.2"
"@typescript/typescript-linux-arm64" "7.0.2"
"@typescript/typescript-linux-loong64" "7.0.2"
"@typescript/typescript-linux-mips64el" "7.0.2"
"@typescript/typescript-linux-ppc64" "7.0.2"
"@typescript/typescript-linux-riscv64" "7.0.2"
"@typescript/typescript-linux-s390x" "7.0.2"
"@typescript/typescript-linux-x64" "7.0.2"
"@typescript/typescript-netbsd-arm64" "7.0.2"
"@typescript/typescript-netbsd-x64" "7.0.2"
"@typescript/typescript-openbsd-arm64" "7.0.2"
"@typescript/typescript-openbsd-x64" "7.0.2"
"@typescript/typescript-sunos-x64" "7.0.2"
"@typescript/typescript-win32-arm64" "7.0.2"
"@typescript/typescript-win32-x64" "7.0.2"
unbox-primitive@^1.1.0: unbox-primitive@^1.1.0:
version "1.1.0" version "1.1.0"
-10
View File
@@ -1603,16 +1603,6 @@ def _deploy_cmd(
# -- 1. Preflight -- # -- 1. Preflight --
validate_deploy_commands(install_command, build_command) validate_deploy_commands(install_command, build_command)
if not config.exists():
message = (
"We couldn't find a langgraph.json file. Run `langgraph deploy` from "
"the root of a LangSmith Deployment project. To get started, visit "
"https://docs.langchain.com/langsmith/deployment-quickstart."
)
if json_output:
em.error(message)
raise click.exceptions.Exit(1)
raise click.ClickException(message)
config_json = langgraph_cli.config.validate_config_file(config) config_json = langgraph_cli.config.validate_config_file(config)
warn_non_wolfi_distro(config_json, emit=em.note) warn_non_wolfi_distro(config_json, emit=em.note)
-16
View File
@@ -970,22 +970,6 @@ def python_config_to_docker_uv_lock(
f"{uv_export_project_dir}/uv.lock", f"{uv_export_project_dir}/uv.lock",
) )
) )
for package_root in sorted(
plan.all_workspace_roots,
key=lambda root: root.as_posix(),
):
if package_root == plan.project_root:
continue
package_relative_path = pathlib.PurePosixPath(
package_root.relative_to(plan.project_root).as_posix()
)
package_pyproject_path = package_relative_path / "pyproject.toml"
docker_plan.add_raw(
copy_from_project_root(
package_pyproject_path,
f"{uv_export_project_dir}/{package_pyproject_path.as_posix()}",
)
)
docker_plan.add_instruction("WORKDIR", uv_export_project_dir) docker_plan.add_instruction("WORKDIR", uv_export_project_dir)
docker_plan.add_instruction( docker_plan.add_instruction(
"RUN", "RUN",
-6
View File
@@ -72,15 +72,9 @@ lint.select = [
"UP", # pyupgrade "UP", # pyupgrade
"B", # flake8-bugbear "B", # flake8-bugbear
"I", # isort "I", # isort
"PLC0415", # import-outside-top-level
"RUF100", # unused noqa directive
"UP", # pyupgrade "UP", # pyupgrade
] ]
lint.ignore = ["E501", "B008"] lint.ignore = ["E501", "B008"]
# PLC0415 (import-outside-top-level) is enforced in tests only. Library code
# still has deferred imports that have not been reviewed, so it stays exempt
# for now.
lint.per-file-ignores = { "langgraph_cli/**" = ["PLC0415"], "generate_schema.py" = ["PLC0415"] }
target-version = "py310" target-version = "py310"
[tool.ty.rules] [tool.ty.rules]
-25
View File
@@ -320,31 +320,6 @@ def test_top_level_help_truncates_command_descriptions_to_single_line() -> None:
assert "[Beta] List LangSmith Deployments." in deploy_list_line assert "[Beta] List LangSmith Deployments." in deploy_list_line
def test_deploy_missing_config_shows_actionable_error(tmp_path, monkeypatch) -> None:
runner = CliRunner()
monkeypatch.chdir(tmp_path)
result = runner.invoke(cli, ["deploy"])
assert result.exit_code == 1
assert "We couldn't find a langgraph.json file." in result.output
assert "Run `langgraph deploy` from the root" in result.output
assert "https://docs.langchain.com/langsmith/deployment-quickstart" in result.output
assert "Traceback" not in result.output
def test_deploy_missing_config_emits_json_error(tmp_path, monkeypatch) -> None:
runner = CliRunner()
monkeypatch.chdir(tmp_path)
result = runner.invoke(cli, ["deploy", "--json"])
assert result.exit_code == 1
events = [json.loads(line) for line in result.output.splitlines()]
assert events[-1]["event"] == "error"
assert "We couldn't find a langgraph.json file." in events[-1]["message"]
def test_dev_command_requires_ssl_certfile_and_keyfile_together(tmp_path) -> None: def test_dev_command_requires_ssl_certfile_and_keyfile_together(tmp_path) -> None:
config_path = tmp_path / "langgraph.json" config_path = tmp_path / "langgraph.json"
config_path.write_text( config_path.write_text(
+6 -1
View File
@@ -11,7 +11,6 @@ from langgraph_cli.archive import (
_tar_filter, _tar_filter,
create_archive, create_archive,
) )
from langgraph_cli.config import LocalDeps
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# _tar_filter # _tar_filter
@@ -199,6 +198,7 @@ class TestCreateArchive:
@patch("langgraph_cli.archive._assemble_local_deps") @patch("langgraph_cli.archive._assemble_local_deps")
def test_yields_archive_with_config(self, mock_deps, tmp_path): def test_yields_archive_with_config(self, mock_deps, tmp_path):
from langgraph_cli.config import LocalDeps
config_file = self._make_project(tmp_path) config_file = self._make_project(tmp_path)
mock_deps.return_value = LocalDeps( mock_deps.return_value = LocalDeps(
@@ -218,6 +218,7 @@ class TestCreateArchive:
@patch("langgraph_cli.archive._assemble_local_deps") @patch("langgraph_cli.archive._assemble_local_deps")
def test_excludes_pycache(self, mock_deps, tmp_path): def test_excludes_pycache(self, mock_deps, tmp_path):
from langgraph_cli.config import LocalDeps
config_file = self._make_project(tmp_path) config_file = self._make_project(tmp_path)
mock_deps.return_value = LocalDeps( mock_deps.return_value = LocalDeps(
@@ -231,6 +232,7 @@ class TestCreateArchive:
@patch("langgraph_cli.archive._assemble_local_deps") @patch("langgraph_cli.archive._assemble_local_deps")
def test_cleans_up_tmp_dir_on_normal_exit(self, mock_deps, tmp_path): def test_cleans_up_tmp_dir_on_normal_exit(self, mock_deps, tmp_path):
from langgraph_cli.config import LocalDeps
config_file = self._make_project(tmp_path) config_file = self._make_project(tmp_path)
mock_deps.return_value = LocalDeps( mock_deps.return_value = LocalDeps(
@@ -245,6 +247,7 @@ class TestCreateArchive:
@patch("langgraph_cli.archive._assemble_local_deps") @patch("langgraph_cli.archive._assemble_local_deps")
def test_cleans_up_tmp_dir_on_exception(self, mock_deps, tmp_path): def test_cleans_up_tmp_dir_on_exception(self, mock_deps, tmp_path):
from langgraph_cli.config import LocalDeps
config_file = self._make_project(tmp_path) config_file = self._make_project(tmp_path)
mock_deps.return_value = LocalDeps( mock_deps.return_value = LocalDeps(
@@ -261,6 +264,7 @@ class TestCreateArchive:
@patch("langgraph_cli.archive._assemble_local_deps") @patch("langgraph_cli.archive._assemble_local_deps")
@patch("langgraph_cli.archive._MAX_SIZE", 10) @patch("langgraph_cli.archive._MAX_SIZE", 10)
def test_raises_on_oversized_archive(self, mock_deps, tmp_path): def test_raises_on_oversized_archive(self, mock_deps, tmp_path):
from langgraph_cli.config import LocalDeps
config_file = self._make_project(tmp_path) config_file = self._make_project(tmp_path)
mock_deps.return_value = LocalDeps( mock_deps.return_value = LocalDeps(
@@ -274,6 +278,7 @@ class TestCreateArchive:
@patch("langgraph_cli.archive._assemble_local_deps") @patch("langgraph_cli.archive._assemble_local_deps")
def test_handles_extra_contexts(self, mock_deps, tmp_path): def test_handles_extra_contexts(self, mock_deps, tmp_path):
"""Monorepo case: project + sibling dependency directory.""" """Monorepo case: project + sibling dependency directory."""
from langgraph_cli.config import LocalDeps
project = tmp_path / "myproject" project = tmp_path / "myproject"
project.mkdir() project.mkdir()
-13
View File
@@ -1403,19 +1403,6 @@ def test_config_to_docker_uv_lock():
"COPY --from=uv-workspace-root uv.lock /tmp/uv_export/project/uv.lock" "COPY --from=uv-workspace-root uv.lock /tmp/uv_export/project/uv.lock"
in docker in docker
) )
workspace_pyprojects = [
"apps/agent/pyproject.toml",
"libs/extra/pyproject.toml",
"libs/shared/pyproject.toml",
]
export_instruction = "RUN uv export --package agent"
for pyproject_path in workspace_pyprojects:
copy_instruction = (
"COPY --from=uv-workspace-root "
f"{pyproject_path} /tmp/uv_export/project/{pyproject_path}"
)
assert copy_instruction in docker
assert docker.index(copy_instruction) < docker.index(export_instruction)
assert additional_contexts == {"uv-workspace-root": str(project_root.resolve())} assert additional_contexts == {"uv-workspace-root": str(project_root.resolve())}
assert ( assert (
@@ -347,6 +347,7 @@ class TestCallHostBackendWithOptionalTenant:
def test_workspace_prompt_blocked_by_no_input(self, monkeypatch): def test_workspace_prompt_blocked_by_no_input(self, monkeypatch):
"""With _no_input=True, 403 requiring workspace should raise ClickException.""" """With _no_input=True, 403 requiring workspace should raise ClickException."""
import langgraph_cli.deploy as deploy_mod
monkeypatch.setattr(deploy_mod, "_no_input", True) monkeypatch.setattr(deploy_mod, "_no_input", True)
@@ -514,6 +515,7 @@ class TestEmitterTextMode:
class TestCreateHostBackendClientNoInput: class TestCreateHostBackendClientNoInput:
def test_raises_when_no_api_key_and_no_input(self, monkeypatch, tmp_path): def test_raises_when_no_api_key_and_no_input(self, monkeypatch, tmp_path):
import langgraph_cli.deploy as deploy_mod
monkeypatch.setattr(deploy_mod, "_no_input", True) monkeypatch.setattr(deploy_mod, "_no_input", True)
monkeypatch.delenv("LANGSMITH_API_KEY", raising=False) monkeypatch.delenv("LANGSMITH_API_KEY", raising=False)
@@ -528,6 +530,7 @@ class TestCreateHostBackendClientNoInput:
) )
def test_succeeds_with_api_key_in_env(self, monkeypatch, tmp_path): def test_succeeds_with_api_key_in_env(self, monkeypatch, tmp_path):
import langgraph_cli.deploy as deploy_mod
monkeypatch.setattr(deploy_mod, "_no_input", True) monkeypatch.setattr(deploy_mod, "_no_input", True)
monkeypatch.setenv("LANGSMITH_API_KEY", "lsv2_test") monkeypatch.setenv("LANGSMITH_API_KEY", "lsv2_test")
+6 -6
View File
@@ -266,20 +266,20 @@ wheels = [
[[package]] [[package]]
name = "langgraph-checkpoint" name = "langgraph-checkpoint"
version = "4.2.0" version = "4.0.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
{ name = "ormsgpack" }, { name = "ormsgpack" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/dc/e1/089c4c9e0a2fec7f883f82ae8e6a727138d50074cfeb6644bc2d13b1019b/langgraph_checkpoint-4.2.0.tar.gz", hash = "sha256:51a593b6bee684b0818e5d6e58e28ab340c6db7794575056ce7bd1b746a84ed7", size = 180239, upload-time = "2026-08-07T20:05:03.756Z" } sdist = { url = "https://files.pythonhosted.org/packages/b1/44/a8df45d1e8b4637e29789fa8bae1db022c953cc7ac80093cfc52e923547e/langgraph_checkpoint-4.0.1.tar.gz", hash = "sha256:b433123735df11ade28829e40ce25b9be614930cd50245ff2af60629234befd9", size = 158135, upload-time = "2026-02-27T21:06:16.092Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/05/71/3b475f09bd57d3a5649792c66353312b4432afd843f301739dfcebd157f0/langgraph_checkpoint-4.2.0-py3-none-any.whl", hash = "sha256:0547fd228935a0b758865de3a3d6d7a2537c308895d0f9ab092ce9151b5da942", size = 56833, upload-time = "2026-08-07T20:05:02.655Z" }, { url = "https://files.pythonhosted.org/packages/65/4c/09a4a0c42f5d2fc38d6c4d67884788eff7fd2cfdf367fdf7033de908b4c0/langgraph_checkpoint-4.0.1-py3-none-any.whl", hash = "sha256:e3adcd7a0e0166f3b48b8cf508ce0ea366e7420b5a73aa81289888727769b034", size = 50453, upload-time = "2026-02-27T21:06:14.293Z" },
] ]
[[package]] [[package]]
name = "langgraph-checkpoint-postgres" name = "langgraph-checkpoint-postgres"
version = "3.1.1" version = "3.0.5"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint" },
@@ -287,9 +287,9 @@ dependencies = [
{ name = "psycopg" }, { name = "psycopg" },
{ name = "psycopg-pool" }, { name = "psycopg-pool" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/06/92/1e8959f8cd1b56e672fde3227f6fd642be85af6c5fd662d73921074aa39d/langgraph_checkpoint_postgres-3.1.1.tar.gz", hash = "sha256:d320e147ddad8c374cd546df0b52b532dd54d0541dd9fd23fc738cbd5de76f41", size = 150413, upload-time = "2026-07-30T19:15:39.014Z" } sdist = { url = "https://files.pythonhosted.org/packages/95/7a/8f439966643d32111248a225e6cb33a182d07c90de780c4dbfc1e0377832/langgraph_checkpoint_postgres-3.0.5.tar.gz", hash = "sha256:a8fd7278a63f4f849b5cbc7884a15ca8f41e7d5f7467d0a66b31e8c24492f7eb", size = 127856, upload-time = "2026-03-18T21:25:29.785Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/03/32/ba457698a48a0e18d786caa770033067049fbe36d6846f8e50f13b594b51/langgraph_checkpoint_postgres-3.1.1-py3-none-any.whl", hash = "sha256:6e353aecd8150de144fef8e51a49076f58b7d6830d4cf51392b7ad4d79832ba7", size = 50778, upload-time = "2026-07-30T19:15:37.405Z" }, { url = "https://files.pythonhosted.org/packages/e8/87/b0f98b33a67204bca9d5619bcd9574222f6b025cf3c125eedcec9a50ecbc/langgraph_checkpoint_postgres-3.0.5-py3-none-any.whl", hash = "sha256:86d7040a88fd70087eaafb72251d796696a0a2d856168f5c11ef620771411552", size = 42907, upload-time = "2026-03-18T21:25:28.75Z" },
] ]
[[package]] [[package]]
+229 -223
View File
@@ -158,7 +158,7 @@ name = "blockbuster"
version = "1.5.26" version = "1.5.26"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "forbiddenfruit", marker = "implementation_name == 'cpython'" }, { name = "forbiddenfruit", marker = "python_full_version >= '3.11' and implementation_name == 'cpython'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/55/e0/dcbab602790a576b0b94108c07e2c048e5897df7cc83722a89582d733987/blockbuster-1.5.26.tar.gz", hash = "sha256:cc3ce8c70fa852a97ee3411155f31e4ad2665cd1c6c7d2f8bb1851dab61dc629", size = 36085, upload-time = "2025-12-05T10:43:47.735Z" } sdist = { url = "https://files.pythonhosted.org/packages/55/e0/dcbab602790a576b0b94108c07e2c048e5897df7cc83722a89582d733987/blockbuster-1.5.26.tar.gz", hash = "sha256:cc3ce8c70fa852a97ee3411155f31e4ad2665cd1c6c7d2f8bb1851dab61dc629", size = 36085, upload-time = "2025-12-05T10:43:47.735Z" }
wheels = [ wheels = [
@@ -363,11 +363,14 @@ wheels = [
[[package]] [[package]]
name = "click" name = "click"
version = "8.5.0" version = "8.4.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" },
] ]
[[package]] [[package]]
@@ -381,11 +384,11 @@ wheels = [
[[package]] [[package]]
name = "codespell" name = "codespell"
version = "2.4.3" version = "2.4.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/80/19/45e941380f69c042b43423513d201e6592346f992394347f5e7174c31407/codespell-2.4.3.tar.gz", hash = "sha256:cbe085e331227b37bb86ef8bddd08dc768c704ee9a07ca869852c093fa2793e2", size = 352773, upload-time = "2026-07-15T11:51:54.159Z" } sdist = { url = "https://files.pythonhosted.org/packages/2d/9d/1d0903dff693160f893ca6abcabad545088e7a2ee0a6deae7c24e958be69/codespell-2.4.2.tar.gz", hash = "sha256:3c33be9ae34543807f088aeb4832dfad8cb2dae38da61cac0a7045dd376cfdf3", size = 352058, upload-time = "2026-03-05T18:10:42.936Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/bf/bdb951d34eb169140b546f44be9ec4525d1acefb9eb5572071f5492b19fc/codespell-2.4.3-py3-none-any.whl", hash = "sha256:af2505b335e8573dbd2d384d1c4ef498f4006f4ba2d6fceca01e55b91f52628a", size = 340736, upload-time = "2026-07-15T11:51:52.925Z" }, { url = "https://files.pythonhosted.org/packages/42/a1/52fa05533e95fe45bcc09bcf8a503874b1c08f221a4e35608017e0938f55/codespell-2.4.2-py3-none-any.whl", hash = "sha256:97e0c1060cf46bd1d5db89a936c98db8c2b804e1fdd4b5c645e82a1ec6b1f886", size = 353715, upload-time = "2026-03-05T18:10:41.398Z" },
] ]
[[package]] [[package]]
@@ -402,7 +405,7 @@ name = "croniter"
version = "6.2.2" version = "6.2.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "python-dateutil" }, { name = "python-dateutil", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/df/de/5832661ed55107b8a09af3f0a2e71e0957226a59eb1dcf0a445cce6daf20/croniter-6.2.2.tar.gz", hash = "sha256:ba60832a5ec8e12e51b8691c3309a113d1cf6526bdf1a48150ce8ec7a532d0ab", size = 113762, upload-time = "2026-03-15T08:43:48.112Z" } sdist = { url = "https://files.pythonhosted.org/packages/df/de/5832661ed55107b8a09af3f0a2e71e0957226a59eb1dcf0a445cce6daf20/croniter-6.2.2.tar.gz", hash = "sha256:ba60832a5ec8e12e51b8691c3309a113d1cf6526bdf1a48150ce8ec7a532d0ab", size = 113762, upload-time = "2026-03-15T08:43:48.112Z" }
wheels = [ wheels = [
@@ -411,59 +414,62 @@ wheels = [
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "50.0.0" version = "48.0.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } sdist = { url = "https://files.pythonhosted.org/packages/12/45/870e7f4bef50e5f53b9f51d4428aee5290eedf58ba443f16b1ebb7ab8e66/cryptography-48.0.1.tar.gz", hash = "sha256:266f4ee051abb2f725b74ef8072b521ce1feacf685a3364fa6a6b45548db791a", size = 832989, upload-time = "2026-06-09T22:32:31.8Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/5c/59086b4aac5e879d38ddbcf74e4be7ade89cebc3eb199a55da998c3bb46a/cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03", size = 4001252, upload-time = "2026-07-31T14:23:33.331Z" }, { url = "https://files.pythonhosted.org/packages/1b/bc/ee4137cbbe105652c0ee4252792b78fc8e7afa4b8e61d9d5dc05a7f45731/cryptography-48.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e4a1a3232eef2e6c732827d5722db29a0cc8b27af2a4d865b094cf954be9ca1", size = 8008324, upload-time = "2026-06-09T22:31:00.702Z" },
{ url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" }, { url = "https://files.pythonhosted.org/packages/d5/85/6379d42181bfc713094f081360fc5784d6c816b599d45e7f082502d173ce/cryptography-48.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32143b24adb918f078134e1e230f1eb8cc04886b92c28b5f0041aaf3e5699225", size = 4696243, upload-time = "2026-06-09T22:32:33.446Z" },
{ url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" }, { url = "https://files.pythonhosted.org/packages/9c/87/c85d147b53323c7eb4d850920c8901377323c2a0ff8d79c262d4fee89aa2/cryptography-48.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0d27a5696721ef7a672b8c810f6aded391058e0b9486e63e6d93baf765da691", size = 4713235, upload-time = "2026-06-09T22:31:40.141Z" },
{ url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" }, { url = "https://files.pythonhosted.org/packages/79/58/67cbf8cf1ee7c54b439ca07bbecf8362c07afc11a3724fea70f745784add/cryptography-48.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb86ce1af36fe65041b6db9a8bb064ee621a7e5fded0f80d475ec243477cd242", size = 4702323, upload-time = "2026-06-09T22:31:42.191Z" },
{ url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" }, { url = "https://files.pythonhosted.org/packages/89/c6/24266ac10c47f6cd2a865f4446062b466da1d1f10b27189eac00e61bf0c9/cryptography-48.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b024e784ad6c077ee0147b35ea9cbfc1e34e1fd4c1dcca214c2794d73a12df08", size = 5300085, upload-time = "2026-06-09T22:31:58.703Z" },
{ url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" }, { url = "https://files.pythonhosted.org/packages/d2/bb/cc4b78784f97efc8c5874c2a9743708d172be6663024b34a0467885ae0c8/cryptography-48.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3752f2dbc8f07a30aad2932c986cea495b03bb554887828225da104f732852b6", size = 4746137, upload-time = "2026-06-09T22:31:31.01Z" },
{ url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" }, { url = "https://files.pythonhosted.org/packages/1f/52/0c44de3f5267f8fbe8e835138017522a333436166e406f0db9b9e6e3033f/cryptography-48.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:bd81490cd5801d755cf97bb68ac191f14b708470b1c7cf4580f669b9c9264cd8", size = 4333867, upload-time = "2026-06-09T22:32:28.096Z" },
{ url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" }, { url = "https://files.pythonhosted.org/packages/9a/2e/772d7adbfa931537bc401640b7cac9976bff689bda187833e5d63b428e49/cryptography-48.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:66fd0771e7b9c6dcd44cf1120690d2338d16d72795cf40cae2786a39eba65429", size = 4701805, upload-time = "2026-06-09T22:31:38.284Z" },
{ url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" }, { url = "https://files.pythonhosted.org/packages/f8/a3/b06844f303873493c963caf581c04df31c7035e0c1b0f02c4814d319ec80/cryptography-48.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:3fd2ca57062b241c856670b073487d2e86c4637937ca5601e48f97bf8e11fc8f", size = 5258461, upload-time = "2026-06-09T22:31:04.187Z" },
{ url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" }, { url = "https://files.pythonhosted.org/packages/9f/13/8b765e2e12b07c74941caadb9d1c8fdc006c4dfbf2b8f2d610519758954d/cryptography-48.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:0ee6ea481db1ab889cba043ec1eda17bb9c1ea79db6722f779c3667f9f70322f", size = 4745488, upload-time = "2026-06-09T22:32:30.07Z" },
{ url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" }, { url = "https://files.pythonhosted.org/packages/2e/aa/48972bce55049b32a94f4907eda4d75fa385aad8a39506cc2fc72196ecf0/cryptography-48.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2ceef93cb096aa3c4cc4b5c94ca6131f9196d28c64d6111533402a9b2054d41", size = 4830256, upload-time = "2026-06-09T22:31:43.868Z" },
{ url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" }, { url = "https://files.pythonhosted.org/packages/47/a2/e5079a032fb85cf6005046ca92bbd78b0c82dad2b5751ab8c311659da06f/cryptography-48.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bd3f92d76217892b15df84ca256c2c113d386fdda7a7d8691aeeced976507c6", size = 4979117, upload-time = "2026-06-09T22:31:05.845Z" },
{ url = "https://files.pythonhosted.org/packages/32/2e/c9db68a0c4bfa28e310707527c0ee3a2bd254104d2e02e68f368e197aa4c/cryptography-50.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", size = 3840395, upload-time = "2026-07-31T14:23:56.677Z" }, { url = "https://files.pythonhosted.org/packages/b7/a0/8f50cae9c74e718ed769d63ed5c74bd0ea830c9550a74629cebd1b9c7bc7/cryptography-48.0.1-cp311-abi3-win32.whl", hash = "sha256:b9a32b876490d66c8bcc9963ef220199569748434ab01a9d6aaeabf88e7f5158", size = 3304154, upload-time = "2026-06-09T22:32:16.845Z" },
{ url = "https://files.pythonhosted.org/packages/c3/fb/951032a3bf22a5697c83183fb6294a4843772947a70e616c57b3ff5f522e/cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41", size = 3989258, upload-time = "2026-07-31T14:23:58.881Z" }, { url = "https://files.pythonhosted.org/packages/c5/69/0572c77dbace6fef72f33755bd52ea399c71367250d366237f8691826b9e/cryptography-48.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:39489bfca54c7a1f6b297efcd8bc608ab92d16c4ca631b0cad4da46724588b24", size = 3817138, upload-time = "2026-06-09T22:32:00.388Z" },
{ url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" }, { url = "https://files.pythonhosted.org/packages/42/06/3e768b4c3bc78201583fa35a0e18f640dd782ff41afba88f8545481a8874/cryptography-48.0.1-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:f817adc181390bd54f2f700107a7419040fb7c1bdf2fc26f36551a06a68c3345", size = 7989830, upload-time = "2026-06-09T22:31:07.8Z" },
{ url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" }, { url = "https://files.pythonhosted.org/packages/8a/13/6476736484b94041110c8340a3eb63962fea4975baea8cb4a512adb44d4d/cryptography-48.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d5d30989c6917b478b5817902e85fddaea2261efa8648383d965381ccb9e1ac4", size = 4689201, upload-time = "2026-06-09T22:31:09.745Z" },
{ url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" }, { url = "https://files.pythonhosted.org/packages/79/62/65a87f34d2a431546e2509b85d55e8c90df86d668f6731da64d538512ac2/cryptography-48.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:df637c05205ea7c1d7fbcbe54bbfea648a52951155f997af13d895d0ecc96991", size = 4702822, upload-time = "2026-06-09T22:32:24.409Z" },
{ url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" }, { url = "https://files.pythonhosted.org/packages/7f/59/810b5204b0a9b10f4b6bc06bd551a8b609803cd931806bc3b71884b225e5/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:869c3b8a53bfe27147832df48b32adadf558249d50e76cb3769d40e986b13265", size = 4694875, upload-time = "2026-06-09T22:32:08.737Z" },
{ url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" }, { url = "https://files.pythonhosted.org/packages/24/dc/d8ca05ffea724eec6d232ea6f18e74c269eb6bdfdcc9bfba689790d1325f/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:e361afba8918070d376df76f408a4f67fec0ee9cff81a99e48fe9a233ef59e17", size = 5290385, upload-time = "2026-06-09T22:31:15.212Z" },
{ url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" }, { url = "https://files.pythonhosted.org/packages/03/8c/3be6cb4da181f5bb6c19cf560c2359d60644a6b5fc5b57854e528f47b296/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d069066deead00ac7f090be101be875a06855908f7ec004c27b8fefb4acfb411", size = 4737082, upload-time = "2026-06-09T22:32:22.66Z" },
{ url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" }, { url = "https://files.pythonhosted.org/packages/aa/f6/d5f60a5a1434dbfd949e227fd0065d194c7e6b6ac526b17f5c06152b8231/cryptography-48.0.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:09f73a725d582cef64b91281a322cd798d14a33b2b6f2b7ad9531dc336d84c02", size = 4325328, upload-time = "2026-06-09T22:32:10.777Z" },
{ url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" }, { url = "https://files.pythonhosted.org/packages/17/b7/ba75dd947a14b6ad907b01ae8f6b5b348cdd1b48142f0063dee9e20c1d9d/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:15254441469dd6bf027039453288e2072124f8b6603563f5d759e1c9b69273fa", size = 4694530, upload-time = "2026-06-09T22:31:53.105Z" },
{ url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" }, { url = "https://files.pythonhosted.org/packages/62/29/50d6b9e8aff12d8b67afaeb3569335e32dc83a5723e3bbded24fdac9f809/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:8ace4507d1e6533c125f4fac754f8bb8b6a74c08e92179dabd7e16571a3efbf3", size = 5245046, upload-time = "2026-06-09T22:31:25.774Z" },
{ url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" }, { url = "https://files.pythonhosted.org/packages/9f/04/618f4115cfc0add0838c82507aa18a346089428da8653ad38b3ff36f5cb3/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b4e391975f038e66432328639620a4aff2d307513b004f1ca06d6225bced815c", size = 4736660, upload-time = "2026-06-09T22:32:12.676Z" },
{ url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" }, { url = "https://files.pythonhosted.org/packages/24/9c/06e062462a0de28a3b3911322eded4c16deb9f441b1b7575d3dc59488ab5/cryptography-48.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42fcd8e26fe555d9b3577a135f5091fefa0aa4e99129c23fb56787a1bd4ada72", size = 4822229, upload-time = "2026-06-09T22:31:17.062Z" },
{ url = "https://files.pythonhosted.org/packages/74/9a/02ffe35b2853d121689871eb5dce862092562b3a1ed5cc98f1aaed441506/cryptography-50.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269", size = 3816291, upload-time = "2026-07-31T14:24:22.125Z" }, { url = "https://files.pythonhosted.org/packages/f4/be/0561971eaaee4b8a0e7d5113c536921063ab91aaf23278ac374eaf881e11/cryptography-48.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1400da5e32a43253392277eac7490a60e497d810a63dd5608d71bbd7af507c9", size = 4966364, upload-time = "2026-06-09T22:31:32.842Z" },
{ url = "https://files.pythonhosted.org/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07", size = 4036009, upload-time = "2026-07-31T14:24:24.122Z" }, { url = "https://files.pythonhosted.org/packages/a4/27/728c77876f12b000820b69ae490f3c4083775e79e07827e9e60be07ad209/cryptography-48.0.1-cp314-cp314t-win32.whl", hash = "sha256:0df56b056bc17c1b7d6821dfa65216e62bd232d8ab05eb3db44e71d235651471", size = 3278498, upload-time = "2026-06-09T22:31:29.154Z" },
{ url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" }, { url = "https://files.pythonhosted.org/packages/06/e3/79a612c6d7b1e6ee0edd43633d53035bec2cfb78c82b76f7864f39e36f34/cryptography-48.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:9de21387aa95e2a895823d0745b430bed4f33503ba9ab5e0b5311f33e37d66d2", size = 3798790, upload-time = "2026-06-09T22:31:56.697Z" },
{ url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" }, { url = "https://files.pythonhosted.org/packages/ca/6c/00fa2a95997164c8b2072ce327c23d4ab20809ccc323ea5fab91e53a4bba/cryptography-48.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:4fdc69f8e4316bcf0c8c8ec1f26f285d12e8142d88d96c876a59a03be3f6ae67", size = 7987408, upload-time = "2026-06-09T22:32:20.777Z" },
{ url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" }, { url = "https://files.pythonhosted.org/packages/b0/d9/45f309a7e4e5f3f8f121d6d3be9e94024a7726ec598d6e08ae04edb2f04d/cryptography-48.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48fe40804d4caa2288f24e70ca8c64c42dd826da0ad7e4f1b41b2128d679e6c8", size = 4690196, upload-time = "2026-06-09T22:31:54.74Z" },
{ url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" }, { url = "https://files.pythonhosted.org/packages/5f/9f/a1bc8bcc798811b8527eb374bbccf30a3f3e806829d967118222bf1125eb/cryptography-48.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:86be3b1b0b6bf09482fb50a979c508d2950ed95f5621ec77f4e385962006b83a", size = 4696782, upload-time = "2026-06-09T22:31:45.615Z" },
{ url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" }, { url = "https://files.pythonhosted.org/packages/66/c2/81a4fb4e4373c500bb526bc337ac5719dd31dd15b970b84a238168c6aa08/cryptography-48.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab0a343c807bbcd90c971cd1ecf072937cd01847a9e002bef88fb47ac6be577", size = 4696618, upload-time = "2026-06-09T22:31:11.564Z" },
{ url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" }, { url = "https://files.pythonhosted.org/packages/e5/0b/aa68b221dde92d09cb29a024ede17550ee21e77a404e59fc093c82bb51e1/cryptography-48.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9621de99d2da096006b629979efd8ae7eb2d8b822488d0c89ee4000c306c59b1", size = 5289970, upload-time = "2026-06-09T22:31:20.368Z" },
{ url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" }, { url = "https://files.pythonhosted.org/packages/78/13/fba657f958d2af66ea959a4ba01212632089249d34af1ae48054136344d7/cryptography-48.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:88c852a0ae366e262e5a1744b685e6a433dc8788dd2a277e418bf4904203609d", size = 4731873, upload-time = "2026-06-09T22:31:22.253Z" },
{ url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" }, { url = "https://files.pythonhosted.org/packages/4c/4c/9a964756d24a26b3e34dfcb16f961b89838786e6700b635b0d1e3adff4b6/cryptography-48.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:43c5835e2cb98c8733d86f57d6fc879b613f5c3478607281c3e36daffc6dd8a6", size = 4330804, upload-time = "2026-06-09T22:31:36.56Z" },
{ url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" }, { url = "https://files.pythonhosted.org/packages/4b/0f/a10f3a6eb12950a10e3a874070283aa2dd5875b2bfd15fad8a3e17b3f13e/cryptography-48.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:fe0180af5bf9236518a087e35bf2d9a347d5f5f51e63c579d683ddff424e3d46", size = 4696217, upload-time = "2026-06-09T22:31:13.351Z" },
{ url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" }, { url = "https://files.pythonhosted.org/packages/f3/6f/5cd12f951165ea73ef85266775d97e4c763b2474ccfd816dd69d3a18d6f8/cryptography-48.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:b7a2d1a937a738a881737cec135a38bb61470589b17515b9f73f571d0ae10401", size = 5245252, upload-time = "2026-06-09T22:32:02.193Z" },
{ url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" }, { url = "https://files.pythonhosted.org/packages/68/ab/8aaa12e4516ec4464033ab79b6f3b592bd5a92102467c4ace8a0d970203f/cryptography-48.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b74ca3b8e5ecdd833bf6a002ca41b4793bb27fb8f1c06ffaf2643c9e9140e31b", size = 4731388, upload-time = "2026-06-09T22:32:04.019Z" },
{ url = "https://files.pythonhosted.org/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba", size = 3874135, upload-time = "2026-07-31T14:24:50.085Z" }, { url = "https://files.pythonhosted.org/packages/1b/24/50027ea4dca85ec1f40688f3c24fb32ccacd520583c9592c3cc95628e6fb/cryptography-48.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c37f2461406063b417837f5f3daab668652acd82423efcd7f0a9f04be972de1", size = 4824186, upload-time = "2026-06-09T22:32:18.707Z" },
{ url = "https://files.pythonhosted.org/packages/9d/3e/e54cde8c01631a5a8226ccd617eab9e57fd5cfdad90f1a9e6bb570794631/cryptography-50.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c", size = 3963170, upload-time = "2026-07-31T14:24:51.968Z" }, { url = "https://files.pythonhosted.org/packages/52/41/04cb5eb17085ade6f50cc611fb657df6a0f5885350de8764ece89c050197/cryptography-48.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86fe77abb1bd87afb251d4d02ada7ecf53a32cee9b67d976abb2e45a13297475", size = 4964539, upload-time = "2026-06-09T22:31:18.793Z" },
{ url = "https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a", size = 4692441, upload-time = "2026-07-31T14:24:53.743Z" }, { url = "https://files.pythonhosted.org/packages/36/bf/ed70785c496e89d7e73b7cda2d21f2447fd6d4e821714b8d04ff217fed92/cryptography-48.0.1-cp39-abi3-win32.whl", hash = "sha256:6b2c0c3e6ccf3ade7750f836ef3ee36eea250cc467d45c256895573ac08cc6f1", size = 3282307, upload-time = "2026-06-09T22:30:53.162Z" },
{ url = "https://files.pythonhosted.org/packages/53/c9/a5151588710785a96d7bc4de27d4cd62f263bbbcb203cfe29df537eb6505/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e", size = 4699810, upload-time = "2026-07-31T14:24:55.746Z" }, { url = "https://files.pythonhosted.org/packages/b3/ff/371ea7d252656ee1eb6d83eeeef3d1d0c6baf1d6497687d081ea03814670/cryptography-48.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:9a49ca6c81417f6a5edb50375a60cccdd70fa0a91a5211829dbea74eba94d2ac", size = 3793408, upload-time = "2026-06-09T22:32:15.191Z" },
{ url = "https://files.pythonhosted.org/packages/c7/1a/15b92b25eb6ce3089cd49377ae990a0f3ad485a510f968aed1f19dbdcdf2/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d", size = 4691924, upload-time = "2026-07-31T14:24:58.082Z" }, { url = "https://files.pythonhosted.org/packages/a9/d3/eb4e394e587341fdad09a09101fa76478ead3a78b0ad63e55c22f0d75c02/cryptography-48.0.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:08a597acce1ff37f347400087776599e2348a3a8bc53b44120e463cd274efe4a", size = 3951747, upload-time = "2026-06-09T22:31:23.871Z" },
{ url = "https://files.pythonhosted.org/packages/62/15/219075012ab13e8905f3cd572204f4acb4b111df787104346b9bc0cea789/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437", size = 4699593, upload-time = "2026-07-31T14:24:59.951Z" }, { url = "https://files.pythonhosted.org/packages/e0/4a/3f43451b4f858bfceaaaffc649e6e787e8d4fb332a1d443af39ab02cc8f1/cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:735824ec41b7f74a7c45fb1591349333e4c696cb6c044e5f46356e560143e4cd", size = 4641226, upload-time = "2026-06-09T22:31:02.532Z" },
{ url = "https://files.pythonhosted.org/packages/8e/b5/c2c5fce26f0ee40d21bafe7f191d29a34b35a65ac4fe8a1191d1983612e9/cryptography-50.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9", size = 3813796, upload-time = "2026-07-31T14:25:02.298Z" }, { url = "https://files.pythonhosted.org/packages/73/4e/855584c2c23b09e4ce2d3b9c30e983e679cd60b068c513c6bbdb91e11782/cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:92a46e1d638daa264ba2971c0b0489c9409787943efae4d60ffda3d091ef832c", size = 4668958, upload-time = "2026-06-09T22:32:06.213Z" },
{ url = "https://files.pythonhosted.org/packages/42/3b/d35750e41d803d1e516fd6d6011f065424924da7af1748cef4cc9cb3ede1/cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:7e234ac052af99f2700826a5c29ea99d9c1b1f80341cde62d11c8154dc8e0bd9", size = 4640793, upload-time = "2026-06-09T22:32:26.331Z" },
{ url = "https://files.pythonhosted.org/packages/ca/aa/cdb7181fe865285e87e96825aaab239400f1de0c3bfba9bd9769b79f1a92/cryptography-48.0.1-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:33842cf0888951cef5bc7ac724ab844a42044c1727b967b7f8997289a0464f92", size = 4668505, upload-time = "2026-06-09T22:31:27.534Z" },
{ url = "https://files.pythonhosted.org/packages/5d/8c/ce3823c06c2804f194f9e64f0d67fa3f4094a39f2bb1a990cd03603af8fc/cryptography-48.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6184ca7b174f28d7c703f1290d4b297217c45355f77a98f67e9b7f14549ac54a", size = 3742204, upload-time = "2026-06-09T22:31:34.773Z" },
] ]
[[package]] [[package]]
@@ -495,7 +501,7 @@ name = "exceptiongroup"
version = "1.3.1" version = "1.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
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" } 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 = [ wheels = [
@@ -522,7 +528,7 @@ name = "googleapis-common-protos"
version = "1.74.0" version = "1.74.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "protobuf" }, { name = "protobuf", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" } sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" }
wheels = [ wheels = [
@@ -534,7 +540,7 @@ name = "grpcio"
version = "1.80.0" version = "1.80.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" } sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" }
wheels = [ wheels = [
@@ -595,8 +601,8 @@ name = "grpcio-health-checking"
version = "1.80.0" version = "1.80.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "grpcio" }, { name = "grpcio", marker = "python_full_version >= '3.11'" },
{ name = "protobuf" }, { name = "protobuf", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/d1/a2/aa3cc47f19c03f8e5287b987317059753141a3af8f66b96d5a64b3be10b8/grpcio_health_checking-1.80.0.tar.gz", hash = "sha256:2cc5f08bc8b816b8655ab6f59c71450063ba20766d31e21a493e912e3560c8b1", size = 17117, upload-time = "2026-03-30T08:54:41.899Z" } sdist = { url = "https://files.pythonhosted.org/packages/d1/a2/aa3cc47f19c03f8e5287b987317059753141a3af8f66b96d5a64b3be10b8/grpcio_health_checking-1.80.0.tar.gz", hash = "sha256:2cc5f08bc8b816b8655ab6f59c71450063ba20766d31e21a493e912e3560c8b1", size = 17117, upload-time = "2026-03-30T08:54:41.899Z" }
wheels = [ wheels = [
@@ -608,9 +614,9 @@ name = "grpcio-tools"
version = "1.80.0" version = "1.80.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "grpcio" }, { name = "grpcio", marker = "python_full_version >= '3.11'" },
{ name = "protobuf" }, { name = "protobuf", marker = "python_full_version >= '3.11'" },
{ name = "setuptools" }, { name = "setuptools", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/94/c8/1223f29c84a143ae9a56c084fc96894de0ba84b6e8d60a26241abd81d278/grpcio_tools-1.80.0.tar.gz", hash = "sha256:26052b19c6ce0dcf52d1024496aea3e2bdfa864159f06dc7b97b22d041a94b26", size = 6133212, upload-time = "2026-03-30T08:52:39.077Z" } sdist = { url = "https://files.pythonhosted.org/packages/94/c8/1223f29c84a143ae9a56c084fc96894de0ba84b6e8d60a26241abd81d278/grpcio_tools-1.80.0.tar.gz", hash = "sha256:26052b19c6ce0dcf52d1024496aea3e2bdfa864159f06dc7b97b22d041a94b26", size = 6133212, upload-time = "2026-03-30T08:52:39.077Z" }
wheels = [ wheels = [
@@ -677,7 +683,7 @@ wheels = [
[[package]] [[package]]
name = "hatch" name = "hatch"
version = "1.18.0" version = "1.17.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "backports-zstd", marker = "python_full_version < '3.14'" }, { name = "backports-zstd", marker = "python_full_version < '3.14'" },
@@ -700,9 +706,9 @@ dependencies = [
{ name = "uv" }, { name = "uv" },
{ name = "virtualenv" }, { 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/e4/0c/7ae1ace8d19f36624cfbbffe227169d670805f8a50a20ce65a4be14c2924/hatch-1.17.0.tar.gz", hash = "sha256:2bbbf4aa8f6fda71a61fc24c22619261327b53d272b22d4cc16f4a6e9a8592bb", size = 5250272, upload-time = "2026-06-02T00:11:58.603Z" }
wheels = [ 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/e4/bd/0a7f877ec78e6910868e39a68c2a1964fa0737a4008ee1f3fd7e2fd8915f/hatch-1.17.0-py3-none-any.whl", hash = "sha256:cb742cc9113085c7dc88fde5a54e594846d8395102ae86e32179d5e72ff3c589", size = 161751, upload-time = "2026-06-02T00:11:56.922Z" },
] ]
[[package]] [[package]]
@@ -908,7 +914,7 @@ name = "jsonpatch"
version = "1.33" version = "1.33"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jsonpointer" }, { name = "jsonpointer", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699, upload-time = "2023-06-26T12:07:29.144Z" } sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699, upload-time = "2023-06-26T12:07:29.144Z" }
wheels = [ wheels = [
@@ -982,15 +988,15 @@ name = "langchain-core"
version = "1.4.0" version = "1.4.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jsonpatch" }, { name = "jsonpatch", marker = "python_full_version >= '3.11'" },
{ name = "langchain-protocol" }, { name = "langchain-protocol", marker = "python_full_version >= '3.11'" },
{ name = "langsmith" }, { name = "langsmith", marker = "python_full_version >= '3.11'" },
{ name = "packaging" }, { name = "packaging", marker = "python_full_version >= '3.11'" },
{ name = "pydantic" }, { name = "pydantic", marker = "python_full_version >= '3.11'" },
{ name = "pyyaml" }, { name = "pyyaml", marker = "python_full_version >= '3.11'" },
{ name = "tenacity" }, { name = "tenacity", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
{ name = "uuid-utils" }, { name = "uuid-utils", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/59/de/679a53472c25860837e32c0442c962fa86e95317a36460e2c9d5c91b17c2/langchain_core-1.4.0.tar.gz", hash = "sha256:1dc341eed802ed9c117c0df3923c991e5e9e226571e5725c194eeb5bd93d1a7f", size = 920260, upload-time = "2026-05-11T18:42:35.919Z" } sdist = { url = "https://files.pythonhosted.org/packages/59/de/679a53472c25860837e32c0442c962fa86e95317a36460e2c9d5c91b17c2/langchain_core-1.4.0.tar.gz", hash = "sha256:1dc341eed802ed9c117c0df3923c991e5e9e226571e5725c194eeb5bd93d1a7f", size = 920260, upload-time = "2026-05-11T18:42:35.919Z" }
wheels = [ wheels = [
@@ -1002,7 +1008,7 @@ name = "langchain-protocol"
version = "0.0.17" version = "0.0.17"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/44/b3/4e2429876c7a35585618caa2b9f9089f7162a6b50562b614ad82ac11c17e/langchain_protocol-0.0.17.tar.gz", hash = "sha256:e7cbe58c205df4b4fd87dc6d5bb23f10e13b236d0e2e1b0b9d05bc2b648f3eea", size = 6026, upload-time = "2026-06-12T18:39:51.923Z" } sdist = { url = "https://files.pythonhosted.org/packages/44/b3/4e2429876c7a35585618caa2b9f9089f7162a6b50562b614ad82ac11c17e/langchain_protocol-0.0.17.tar.gz", hash = "sha256:e7cbe58c205df4b4fd87dc6d5bb23f10e13b236d0e2e1b0b9d05bc2b648f3eea", size = 6026, upload-time = "2026-06-12T18:39:51.923Z" }
wheels = [ wheels = [
@@ -1014,12 +1020,12 @@ name = "langgraph"
version = "1.2.3" version = "1.2.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-prebuilt" }, { name = "langgraph-prebuilt", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-sdk" }, { name = "langgraph-sdk", marker = "python_full_version >= '3.11'" },
{ name = "pydantic" }, { name = "pydantic", marker = "python_full_version >= '3.11'" },
{ name = "xxhash" }, { name = "xxhash", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/5c/55/e42f59b5e4cce75085f68abb77114c4d1152628157bd60c2587881bca559/langgraph-1.2.3.tar.gz", hash = "sha256:7f89cd5f0946fe29bd7ca048e2a84d3c14e7f652e38bb98e00f0ba8b7004b9d0", size = 718812, upload-time = "2026-06-01T18:55:54.458Z" } sdist = { url = "https://files.pythonhosted.org/packages/5c/55/e42f59b5e4cce75085f68abb77114c4d1152628157bd60c2587881bca559/langgraph-1.2.3.tar.gz", hash = "sha256:7f89cd5f0946fe29bd7ca048e2a84d3c14e7f652e38bb98e00f0ba8b7004b9d0", size = 718812, upload-time = "2026-06-01T18:55:54.458Z" }
wheels = [ wheels = [
@@ -1031,38 +1037,38 @@ name = "langgraph-api"
version = "0.10.0" version = "0.10.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cloudpickle" }, { name = "cloudpickle", marker = "python_full_version >= '3.11'" },
{ name = "cryptography" }, { name = "cryptography", marker = "python_full_version >= '3.11'" },
{ name = "grpcio" }, { name = "grpcio", marker = "python_full_version >= '3.11'" },
{ name = "grpcio-health-checking" }, { name = "grpcio-health-checking", marker = "python_full_version >= '3.11'" },
{ name = "grpcio-tools" }, { name = "grpcio-tools", marker = "python_full_version >= '3.11'" },
{ name = "httptools", marker = "sys_platform != 'win32'" }, { name = "httptools", marker = "python_full_version >= '3.11' and sys_platform != 'win32'" },
{ name = "httpx" }, { name = "httpx", marker = "python_full_version >= '3.11'" },
{ name = "jsonschema-rs" }, { name = "jsonschema-rs", marker = "python_full_version >= '3.11'" },
{ name = "langchain-core" }, { name = "langchain-core", marker = "python_full_version >= '3.11'" },
{ name = "langchain-protocol" }, { name = "langchain-protocol", marker = "python_full_version >= '3.11'" },
{ name = "langgraph" }, { name = "langgraph", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-runtime-inmem" }, { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-sdk" }, { name = "langgraph-sdk", marker = "python_full_version >= '3.11'" },
{ name = "langsmith", extra = ["otel"] }, { name = "langsmith", extra = ["otel"], marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-api" }, { name = "opentelemetry-api", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-exporter-otlp-proto-http", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-sdk" }, { name = "opentelemetry-sdk", marker = "python_full_version >= '3.11'" },
{ name = "orjson" }, { name = "orjson", marker = "python_full_version >= '3.11'" },
{ name = "protobuf" }, { name = "protobuf", marker = "python_full_version >= '3.11'" },
{ name = "pyjwt" }, { name = "pyjwt", marker = "python_full_version >= '3.11'" },
{ name = "sse-starlette" }, { name = "sse-starlette", marker = "python_full_version >= '3.11'" },
{ name = "starlette" }, { name = "starlette", marker = "python_full_version >= '3.11'" },
{ name = "structlog" }, { name = "structlog", marker = "python_full_version >= '3.11'" },
{ name = "tenacity" }, { name = "tenacity", marker = "python_full_version >= '3.11'" },
{ name = "truststore" }, { name = "truststore", marker = "python_full_version >= '3.11'" },
{ name = "uuid-utils" }, { name = "uuid-utils", marker = "python_full_version >= '3.11'" },
{ name = "uvicorn" }, { name = "uvicorn", marker = "python_full_version >= '3.11'" },
{ name = "uvloop", marker = "sys_platform != 'win32'" }, { name = "uvloop", marker = "python_full_version >= '3.11' and sys_platform != 'win32'" },
{ name = "watchfiles" }, { name = "watchfiles", marker = "python_full_version >= '3.11'" },
{ name = "websockets" }, { name = "websockets", marker = "python_full_version >= '3.11'" },
{ name = "zstandard" }, { name = "zstandard", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/7d/17/a23ecdad3de7e48209bc8d534312ba83e332126ec60da9e6487461b02597/langgraph_api-0.10.0.tar.gz", hash = "sha256:f594a160857e2cd7fb2352bba585111177a4a394625ad0cf5898814754fb7bc1", size = 713903, upload-time = "2026-06-11T14:36:50.469Z" } sdist = { url = "https://files.pythonhosted.org/packages/7d/17/a23ecdad3de7e48209bc8d534312ba83e332126ec60da9e6487461b02597/langgraph_api-0.10.0.tar.gz", hash = "sha256:f594a160857e2cd7fb2352bba585111177a4a394625ad0cf5898814754fb7bc1", size = 713903, upload-time = "2026-06-11T14:36:50.469Z" }
wheels = [ wheels = [
@@ -1074,8 +1080,8 @@ name = "langgraph-checkpoint"
version = "4.1.1" version = "4.1.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core", marker = "python_full_version >= '3.11'" },
{ name = "ormsgpack" }, { name = "ormsgpack", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/83/47/886af6f886f0bff2273164a45f008694e48a96ff3cd25ff0228f2aa9480e/langgraph_checkpoint-4.1.1.tar.gz", hash = "sha256:6c2bdb530c91f91d7d9c1bd100925d0fc4f498d418c17f3587d1526279482a25", size = 184020, upload-time = "2026-05-22T16:57:38.503Z" } sdist = { url = "https://files.pythonhosted.org/packages/83/47/886af6f886f0bff2273164a45f008694e48a96ff3cd25ff0228f2aa9480e/langgraph_checkpoint-4.1.1.tar.gz", hash = "sha256:6c2bdb530c91f91d7d9c1bd100925d0fc4f498d418c17f3587d1526279482a25", size = 184020, upload-time = "2026-05-22T16:57:38.503Z" }
wheels = [ wheels = [
@@ -1168,8 +1174,8 @@ name = "langgraph-prebuilt"
version = "1.1.0" version = "1.1.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/29/66/ed9b93f56bc17ef22d551892f0ac2b225a97fe0fcf23a511b857f70d590b/langgraph_prebuilt-1.1.0.tar.gz", hash = "sha256:3c579cf6eed2d17f9c157c2d0fcaddcd8688524e7022d3b22b37a3bf4589d528", size = 178833, upload-time = "2026-05-12T03:37:49.332Z" } sdist = { url = "https://files.pythonhosted.org/packages/29/66/ed9b93f56bc17ef22d551892f0ac2b225a97fe0fcf23a511b857f70d590b/langgraph_prebuilt-1.1.0.tar.gz", hash = "sha256:3c579cf6eed2d17f9c157c2d0fcaddcd8688524e7022d3b22b37a3bf4589d528", size = 178833, upload-time = "2026-05-12T03:37:49.332Z" }
wheels = [ wheels = [
@@ -1181,13 +1187,13 @@ name = "langgraph-runtime-inmem"
version = "0.30.0" version = "0.30.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "blockbuster" }, { name = "blockbuster", marker = "python_full_version >= '3.11'" },
{ name = "croniter" }, { name = "croniter", marker = "python_full_version >= '3.11'" },
{ name = "langgraph" }, { name = "langgraph", marker = "python_full_version >= '3.11'" },
{ name = "langgraph-checkpoint" }, { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" },
{ name = "sse-starlette" }, { name = "sse-starlette", marker = "python_full_version >= '3.11'" },
{ name = "starlette" }, { name = "starlette", marker = "python_full_version >= '3.11'" },
{ name = "structlog" }, { name = "structlog", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/98/19/5d330e22e3ee2569742d3577b4ddfbb6b8e383f2edd9724b4a4b73735717/langgraph_runtime_inmem-0.30.0.tar.gz", hash = "sha256:31954e1de6cd43c284b425243d4faeae818f68b3daca4dbe4313e651b6f5e11d", size = 129940, upload-time = "2026-06-11T14:22:46.985Z" } sdist = { url = "https://files.pythonhosted.org/packages/98/19/5d330e22e3ee2569742d3577b4ddfbb6b8e383f2edd9724b4a4b73735717/langgraph_runtime_inmem-0.30.0.tar.gz", hash = "sha256:31954e1de6cd43c284b425243d4faeae818f68b3daca4dbe4313e651b6f5e11d", size = 129940, upload-time = "2026-06-11T14:22:46.985Z" }
wheels = [ wheels = [
@@ -1196,18 +1202,18 @@ wheels = [
[[package]] [[package]]
name = "langgraph-sdk" name = "langgraph-sdk"
version = "0.4.4" version = "0.4.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "httpx" }, { name = "httpx", marker = "python_full_version >= '3.11'" },
{ name = "langchain-core" }, { name = "langchain-core", marker = "python_full_version >= '3.11'" },
{ name = "langchain-protocol" }, { name = "langchain-protocol", marker = "python_full_version >= '3.11'" },
{ name = "orjson" }, { name = "orjson", marker = "python_full_version >= '3.11'" },
{ name = "websockets" }, { name = "websockets", marker = "python_full_version >= '3.11'" },
] ]
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/b4/2b/bd8ac26d4e97f6df88ef05ce5b6a38945a3903e1025d926f4752aa88aa97/langgraph_sdk-0.4.2.tar.gz", hash = "sha256:b88f0f5f6328ac0680d6790614a905b2bcfa257f2276dba4e38f0e86db0aa738", size = 348327, upload-time = "2026-06-01T17:51:19.856Z" }
wheels = [ 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/a0/05/aac507337cceae773c2cc9ab91eb6301963af7aeeb55b4217a00e15aff17/langgraph_sdk-0.4.2-py3-none-any.whl", hash = "sha256:75fa5096c1177ce39c847096a8fe3745ffd480ddb412995f836e9f5f884c43dd", size = 160521, upload-time = "2026-06-01T17:51:18.849Z" },
] ]
[[package]] [[package]]
@@ -1215,16 +1221,16 @@ name = "langsmith"
version = "0.8.18" version = "0.8.18"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "httpx" }, { name = "httpx", marker = "python_full_version >= '3.11'" },
{ name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, { name = "orjson", marker = "python_full_version >= '3.11' and platform_python_implementation != 'PyPy'" },
{ name = "packaging" }, { name = "packaging", marker = "python_full_version >= '3.11'" },
{ name = "pydantic" }, { name = "pydantic", marker = "python_full_version >= '3.11'" },
{ name = "requests" }, { name = "requests", marker = "python_full_version >= '3.11'" },
{ name = "requests-toolbelt" }, { name = "requests-toolbelt", marker = "python_full_version >= '3.11'" },
{ name = "uuid-utils" }, { name = "uuid-utils", marker = "python_full_version >= '3.11'" },
{ name = "websockets" }, { name = "websockets", marker = "python_full_version >= '3.11'" },
{ name = "xxhash" }, { name = "xxhash", marker = "python_full_version >= '3.11'" },
{ name = "zstandard" }, { name = "zstandard", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/9a/d9/a6681aa9847bbbc5ec21abe20a5e233b94e5edcfe39624db607ac7e8ccb4/langsmith-0.8.18.tar.gz", hash = "sha256:32dde9c0e67e053e0fb738921fc8ced768af7b8fa83d7a0e3fd63597cf8776dd", size = 4526988, upload-time = "2026-06-19T13:12:17.123Z" } sdist = { url = "https://files.pythonhosted.org/packages/9a/d9/a6681aa9847bbbc5ec21abe20a5e233b94e5edcfe39624db607ac7e8ccb4/langsmith-0.8.18.tar.gz", hash = "sha256:32dde9c0e67e053e0fb738921fc8ced768af7b8fa83d7a0e3fd63597cf8776dd", size = 4526988, upload-time = "2026-06-19T13:12:17.123Z" }
wheels = [ wheels = [
@@ -1233,9 +1239,9 @@ wheels = [
[package.optional-dependencies] [package.optional-dependencies]
otel = [ otel = [
{ name = "opentelemetry-api" }, { name = "opentelemetry-api", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-exporter-otlp-proto-http", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-sdk" }, { name = "opentelemetry-sdk", marker = "python_full_version >= '3.11'" },
] ]
[[package]] [[package]]
@@ -1329,8 +1335,8 @@ name = "opentelemetry-api"
version = "1.41.0" version = "1.41.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "importlib-metadata" }, { name = "importlib-metadata", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/47/8e/3778a7e87801d994869a9396b9fc2a289e5f9be91ff54a27d41eace494b0/opentelemetry_api-1.41.0.tar.gz", hash = "sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09", size = 71416, upload-time = "2026-04-09T14:38:34.544Z" } sdist = { url = "https://files.pythonhosted.org/packages/47/8e/3778a7e87801d994869a9396b9fc2a289e5f9be91ff54a27d41eace494b0/opentelemetry_api-1.41.0.tar.gz", hash = "sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09", size = 71416, upload-time = "2026-04-09T14:38:34.544Z" }
wheels = [ wheels = [
@@ -1342,7 +1348,7 @@ name = "opentelemetry-exporter-otlp-proto-common"
version = "1.41.0" version = "1.41.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "opentelemetry-proto" }, { name = "opentelemetry-proto", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/8c/28/e8eca94966fe9a1465f6094dc5ddc5398473682180279c94020bc23b4906/opentelemetry_exporter_otlp_proto_common-1.41.0.tar.gz", hash = "sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee", size = 20411, upload-time = "2026-04-09T14:38:36.572Z" } sdist = { url = "https://files.pythonhosted.org/packages/8c/28/e8eca94966fe9a1465f6094dc5ddc5398473682180279c94020bc23b4906/opentelemetry_exporter_otlp_proto_common-1.41.0.tar.gz", hash = "sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee", size = 20411, upload-time = "2026-04-09T14:38:36.572Z" }
wheels = [ wheels = [
@@ -1354,13 +1360,13 @@ name = "opentelemetry-exporter-otlp-proto-http"
version = "1.41.0" version = "1.41.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "googleapis-common-protos" }, { name = "googleapis-common-protos", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-api" }, { name = "opentelemetry-api", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-exporter-otlp-proto-common" }, { name = "opentelemetry-exporter-otlp-proto-common", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-proto" }, { name = "opentelemetry-proto", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-sdk" }, { name = "opentelemetry-sdk", marker = "python_full_version >= '3.11'" },
{ name = "requests" }, { name = "requests", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/19/63/d9f43cd75f3fabb7e01148c89cfa9491fc18f6580a6764c554ff7c953c46/opentelemetry_exporter_otlp_proto_http-1.41.0.tar.gz", hash = "sha256:dcd6e0686f56277db4eecbadd5262124e8f2cc739cadbc3fae3d08a12c976cf5", size = 24139, upload-time = "2026-04-09T14:38:38.128Z" } sdist = { url = "https://files.pythonhosted.org/packages/19/63/d9f43cd75f3fabb7e01148c89cfa9491fc18f6580a6764c554ff7c953c46/opentelemetry_exporter_otlp_proto_http-1.41.0.tar.gz", hash = "sha256:dcd6e0686f56277db4eecbadd5262124e8f2cc739cadbc3fae3d08a12c976cf5", size = 24139, upload-time = "2026-04-09T14:38:38.128Z" }
wheels = [ wheels = [
@@ -1372,7 +1378,7 @@ name = "opentelemetry-proto"
version = "1.41.0" version = "1.41.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "protobuf" }, { name = "protobuf", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e0/d9/08e3dc6156878713e8c811682bc76151f5fe1a3cb7f3abda3966fd56e71e/opentelemetry_proto-1.41.0.tar.gz", hash = "sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6", size = 45669, upload-time = "2026-04-09T14:38:45.978Z" } sdist = { url = "https://files.pythonhosted.org/packages/e0/d9/08e3dc6156878713e8c811682bc76151f5fe1a3cb7f3abda3966fd56e71e/opentelemetry_proto-1.41.0.tar.gz", hash = "sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6", size = 45669, upload-time = "2026-04-09T14:38:45.978Z" }
wheels = [ wheels = [
@@ -1384,9 +1390,9 @@ name = "opentelemetry-sdk"
version = "1.41.0" version = "1.41.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "opentelemetry-api" }, { name = "opentelemetry-api", marker = "python_full_version >= '3.11'" },
{ name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-semantic-conventions", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f8/0e/a586df1186f9f56b5a0879d52653effc40357b8e88fc50fe300038c3c08b/opentelemetry_sdk-1.41.0.tar.gz", hash = "sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd", size = 230181, upload-time = "2026-04-09T14:38:47.225Z" } sdist = { url = "https://files.pythonhosted.org/packages/f8/0e/a586df1186f9f56b5a0879d52653effc40357b8e88fc50fe300038c3c08b/opentelemetry_sdk-1.41.0.tar.gz", hash = "sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd", size = 230181, upload-time = "2026-04-09T14:38:47.225Z" }
wheels = [ wheels = [
@@ -1398,8 +1404,8 @@ name = "opentelemetry-semantic-conventions"
version = "0.62b0" version = "0.62b0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "opentelemetry-api" }, { name = "opentelemetry-api", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/a3/b0/c14f723e86c049b7bf8ff431160d982519b97a7be2857ed2247377397a24/opentelemetry_semantic_conventions-0.62b0.tar.gz", hash = "sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097", size = 145753, upload-time = "2026-04-09T14:38:48.274Z" } sdist = { url = "https://files.pythonhosted.org/packages/a3/b0/c14f723e86c049b7bf8ff431160d982519b97a7be2857ed2247377397a24/opentelemetry_semantic_conventions-0.62b0.tar.gz", hash = "sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097", size = 145753, upload-time = "2026-04-09T14:38:48.274Z" }
wheels = [ wheels = [
@@ -1629,10 +1635,10 @@ name = "pydantic"
version = "2.13.2" version = "2.13.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-types" }, { name = "annotated-types", marker = "python_full_version >= '3.11'" },
{ name = "pydantic-core" }, { name = "pydantic-core", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
{ name = "typing-inspection" }, { name = "typing-inspection", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/09/e5/06d23afac9973109d1e3c8ad38e1547a12e860610e327c05ee686827dc37/pydantic-2.13.2.tar.gz", hash = "sha256:b418196607e61081c3226dcd4f0672f2a194828abb9109e9cfb84026564df2d1", size = 843836, upload-time = "2026-04-17T09:31:59.636Z" } sdist = { url = "https://files.pythonhosted.org/packages/09/e5/06d23afac9973109d1e3c8ad38e1547a12e860610e327c05ee686827dc37/pydantic-2.13.2.tar.gz", hash = "sha256:b418196607e61081c3226dcd4f0672f2a194828abb9109e9cfb84026564df2d1", size = 843836, upload-time = "2026-04-17T09:31:59.636Z" }
wheels = [ wheels = [
@@ -1644,7 +1650,7 @@ name = "pydantic-core"
version = "2.46.2" version = "2.46.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/43/bb/4742f05b739b2478459bb16fa8470549518c802e06ddcf3f106c5081315e/pydantic_core-2.46.2.tar.gz", hash = "sha256:37bb079f9ee3f1a519392b73fda2a96379b31f2013c6b467fe693e7f2987f596", size = 471269, upload-time = "2026-04-17T09:10:07.017Z" } sdist = { url = "https://files.pythonhosted.org/packages/43/bb/4742f05b739b2478459bb16fa8470549518c802e06ddcf3f106c5081315e/pydantic_core-2.46.2.tar.gz", hash = "sha256:37bb079f9ee3f1a519392b73fda2a96379b31f2013c6b467fe693e7f2987f596", size = 471269, upload-time = "2026-04-17T09:10:07.017Z" }
wheels = [ wheels = [
@@ -1843,7 +1849,7 @@ name = "python-dateutil"
version = "2.9.0.post0" version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "six" }, { name = "six", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [ wheels = [
@@ -1865,11 +1871,11 @@ wheels = [
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "1.2.3" version = "1.2.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6a/53/ed9d74092561d4b01a2ef1349d52cdbc135e526c245f366b089cfca6de49/python_dotenv-1.2.3.tar.gz", hash = "sha256:a20a594dabeaa385725aa239d5244871c143ecb356add8a20fcf23773a6c3a35", size = 58945, upload-time = "2026-08-16T16:54:54.067Z" } sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/17/c5c6b53ddc18f297992099b3d9ec16c855c0ccc83263a21fe4d1c625ec6c/python_dotenv-1.2.3-py3-none-any.whl", hash = "sha256:904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9", size = 22780, upload-time = "2026-08-16T16:54:52.473Z" }, { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
] ]
[[package]] [[package]]
@@ -1950,10 +1956,10 @@ name = "requests"
version = "2.33.1" version = "2.33.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "certifi" }, { name = "certifi", marker = "python_full_version >= '3.11'" },
{ name = "charset-normalizer" }, { name = "charset-normalizer", marker = "python_full_version >= '3.11'" },
{ name = "idna" }, { name = "idna", marker = "python_full_version >= '3.11'" },
{ name = "urllib3" }, { name = "urllib3", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" }
wheels = [ wheels = [
@@ -1965,7 +1971,7 @@ name = "requests-toolbelt"
version = "1.0.0" version = "1.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "requests" }, { name = "requests", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" }
wheels = [ wheels = [
@@ -1987,27 +1993,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.16.5" version = "0.15.20"
source = { registry = "https://pypi.org/simple" } 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/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" }
wheels = [ 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/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" },
{ 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/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" },
{ 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/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" },
{ 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/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" },
{ 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/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" },
{ 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/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" },
{ 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/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" },
{ 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/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" },
{ 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/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" },
{ 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/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" },
{ 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/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" },
{ 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/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" },
{ 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/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" },
{ 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/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" },
{ 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/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" },
{ 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/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" },
{ 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/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
] ]
[[package]] [[package]]
@@ -2055,8 +2061,8 @@ name = "sse-starlette"
version = "3.3.4" version = "3.3.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "anyio" }, { name = "anyio", marker = "python_full_version >= '3.11'" },
{ name = "starlette" }, { name = "starlette", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/26/8c/f9290339ef6d79badbc010f067cd769d6601ec11a57d78569c683fb4dd87/sse_starlette-3.3.4.tar.gz", hash = "sha256:aaf92fc067af8a5427192895ac028e947b484ac01edbc3caf00e7e7137c7bef1", size = 32427, upload-time = "2026-03-29T09:00:23.307Z" } sdist = { url = "https://files.pythonhosted.org/packages/26/8c/f9290339ef6d79badbc010f067cd769d6601ec11a57d78569c683fb4dd87/sse_starlette-3.3.4.tar.gz", hash = "sha256:aaf92fc067af8a5427192895ac028e947b484ac01edbc3caf00e7e7137c7bef1", size = 32427, upload-time = "2026-03-29T09:00:23.307Z" }
wheels = [ wheels = [
@@ -2068,8 +2074,8 @@ name = "starlette"
version = "1.3.1" version = "1.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "anyio" }, { name = "anyio", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" }, { name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" }
wheels = [ wheels = [
@@ -2186,27 +2192,27 @@ wheels = [
[[package]] [[package]]
name = "ty" name = "ty"
version = "0.0.75" version = "0.0.55"
source = { registry = "https://pypi.org/simple" } 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/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" }
wheels = [ 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/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" },
{ 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/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" },
{ 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/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" },
{ 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/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" },
{ 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/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" },
{ 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/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" },
{ 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/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" },
{ 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/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" },
{ 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/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" },
{ 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/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" },
{ 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/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" },
{ 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/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" },
{ 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/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" },
{ 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/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" },
{ 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/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" },
{ 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/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" },
{ 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/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" },
] ]
[[package]] [[package]]
@@ -2223,7 +2229,7 @@ name = "typing-inspection"
version = "0.4.2" version = "0.4.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" },
] ]
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" } 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 = [ wheels = [
@@ -2311,8 +2317,8 @@ name = "uvicorn"
version = "0.44.0" version = "0.44.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click", marker = "python_full_version >= '3.11'" },
{ name = "h11" }, { name = "h11", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" } sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" }
wheels = [ wheels = [
@@ -2416,7 +2422,7 @@ name = "watchfiles"
version = "1.1.1" version = "1.1.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "anyio" }, { name = "anyio", marker = "python_full_version >= '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" }
wheels = [ wheels = [
@@ -68,7 +68,7 @@ def _create_root_model(
def schema( def schema(
cls: type[BaseModel], cls: type[BaseModel],
by_alias: bool = True, by_alias: bool = True, # noqa: FBT001,FBT002
ref_template: str = DEFAULT_REF_TEMPLATE, ref_template: str = DEFAULT_REF_TEMPLATE,
) -> dict[str, Any]: ) -> dict[str, Any]:
# Complains about schema not being defined in superclass # Complains about schema not being defined in superclass
@@ -80,7 +80,7 @@ def _create_root_model(
def model_json_schema( def model_json_schema(
cls: type[BaseModel], cls: type[BaseModel],
by_alias: bool = True, by_alias: bool = True, # noqa: FBT001,FBT002
ref_template: str = DEFAULT_REF_TEMPLATE, ref_template: str = DEFAULT_REF_TEMPLATE,
schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
mode: JsonSchemaMode = "validation", mode: JsonSchemaMode = "validation",
+22 -37
View File
@@ -3,7 +3,6 @@ from __future__ import annotations
import asyncio import asyncio
import enum import enum
import inspect import inspect
import logging
import sys import sys
import warnings import warnings
from collections.abc import ( from collections.abc import (
@@ -64,26 +63,6 @@ try:
except ImportError: except ImportError:
_StreamingCallbackHandler = None # type: ignore _StreamingCallbackHandler = None # type: ignore
logger = logging.getLogger(__name__)
def _trace_payload(value: Any, transform: Callable[[Any], Any] | None) -> Any:
"""Return the payload to record on a run for `value`.
When `transform` is unset this is a passthrough, so unspecified nodes record exactly
as before. When set it always runs (regardless of tracing), but never affects
execution: if it raises, the untransformed value is recorded instead.
"""
if transform is None:
return value
try:
return transform(value)
except Exception:
logger.exception(
"trace input/output processor raised; recording untransformed payload"
)
return value
def _set_config_context( def _set_config_context(
config: RunnableConfig, run: Any = None config: RunnableConfig, run: Any = None
@@ -163,6 +142,9 @@ ANY_TYPE = object()
ASYNCIO_ACCEPTS_CONTEXT = sys.version_info >= (3, 11) ASYNCIO_ACCEPTS_CONTEXT = sys.version_info >= (3, 11)
# Cache for inspect.signature results, keyed by function object
_SIGNATURE_CACHE: dict[Callable, inspect.Signature] = {}
# List of keyword arguments that can be injected into nodes / tasks / tools at runtime. # List of keyword arguments that can be injected into nodes / tasks / tools at runtime.
# A named argument may appear multiple times if it appears with distinct types. # A named argument may appear multiple times if it appears with distinct types.
KWARGS_CONFIG_KEYS: tuple[tuple[str, tuple[Any, ...], str, Any], ...] = ( KWARGS_CONFIG_KEYS: tuple[tuple[str, tuple[Any, ...], str, Any], ...] = (
@@ -336,7 +318,16 @@ class RunnableCallable(Runnable):
raise ValueError("At least one of func or afunc must be provided.") raise ValueError("At least one of func or afunc must be provided.")
self.func_accepts: dict[str, tuple[str, Any]] = {} self.func_accepts: dict[str, tuple[str, Any]] = {}
params = inspect.signature(cast(Callable, func or afunc)).parameters target = cast(Callable, func or afunc)
try:
sig = _SIGNATURE_CACHE[target]
except (KeyError, TypeError):
sig = inspect.signature(target)
try:
_SIGNATURE_CACHE[target] = sig
except TypeError:
pass # unhashable function, skip caching
params = sig.parameters
for kw, typ, runtime_key, default in KWARGS_CONFIG_KEYS: for kw, typ, runtime_key, default in KWARGS_CONFIG_KEYS:
p = params.get(kw) p = params.get(kw)
@@ -593,7 +584,6 @@ class RunnableSeq(Runnable):
*steps: RunnableLike, *steps: RunnableLike,
name: str | None = None, name: str | None = None,
trace_inputs: Callable[[Any], Any] | None = None, trace_inputs: Callable[[Any], Any] | None = None,
trace_outputs: Callable[[Any], Any] | None = None,
) -> None: ) -> None:
"""Create a new RunnableSeq. """Create a new RunnableSeq.
@@ -619,7 +609,6 @@ class RunnableSeq(Runnable):
self.steps = steps_flat self.steps = steps_flat
self.name = name self.name = name
self.trace_inputs = trace_inputs self.trace_inputs = trace_inputs
self.trace_outputs = trace_outputs
def __or__( def __or__(
self, self,
@@ -681,7 +670,7 @@ class RunnableSeq(Runnable):
# start the root run # start the root run
run_manager = callback_manager.on_chain_start( run_manager = callback_manager.on_chain_start(
None, None,
_trace_payload(input, self.trace_inputs), self.trace_inputs(input) if self.trace_inputs is not None else input,
name=config.get("run_name") or self.get_name(), name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None), run_id=config.pop("run_id", None),
) )
@@ -712,7 +701,7 @@ class RunnableSeq(Runnable):
run_manager.on_chain_error(e) run_manager.on_chain_error(e)
raise raise
else: else:
run_manager.on_chain_end(_trace_payload(input, self.trace_outputs)) run_manager.on_chain_end(input)
return input return input
async def ainvoke( async def ainvoke(
@@ -728,7 +717,7 @@ class RunnableSeq(Runnable):
# start the root run # start the root run
run_manager = await callback_manager.on_chain_start( run_manager = await callback_manager.on_chain_start(
None, None,
_trace_payload(input, self.trace_inputs), self.trace_inputs(input) if self.trace_inputs is not None else input,
name=config.get("run_name") or self.get_name(), name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None), run_id=config.pop("run_id", None),
) )
@@ -765,7 +754,7 @@ class RunnableSeq(Runnable):
await run_manager.on_chain_error(e) await run_manager.on_chain_error(e)
raise raise
else: else:
await run_manager.on_chain_end(_trace_payload(input, self.trace_outputs)) await run_manager.on_chain_end(input)
return input return input
def stream( def stream(
@@ -781,7 +770,7 @@ class RunnableSeq(Runnable):
# start the root run # start the root run
run_manager = callback_manager.on_chain_start( run_manager = callback_manager.on_chain_start(
None, None,
_trace_payload(input, self.trace_inputs), self.trace_inputs(input) if self.trace_inputs is not None else input,
name=config.get("run_name") or self.get_name(), name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None), run_id=config.pop("run_id", None),
) )
@@ -826,7 +815,7 @@ class RunnableSeq(Runnable):
run_manager.on_chain_error(e) run_manager.on_chain_error(e)
raise raise
else: else:
run_manager.on_chain_end(_trace_payload(output, self.trace_outputs)) run_manager.on_chain_end(output)
async def astream( async def astream(
self, self,
@@ -841,7 +830,7 @@ class RunnableSeq(Runnable):
# start the root run # start the root run
run_manager = await callback_manager.on_chain_start( run_manager = await callback_manager.on_chain_start(
None, None,
_trace_payload(input, self.trace_inputs), self.trace_inputs(input) if self.trace_inputs is not None else input,
name=config.get("run_name") or self.get_name(), name=config.get("run_name") or self.get_name(),
run_id=config.pop("run_id", None), run_id=config.pop("run_id", None),
) )
@@ -896,9 +885,7 @@ class RunnableSeq(Runnable):
await run_manager.on_chain_error(e) await run_manager.on_chain_error(e)
raise raise
else: else:
await run_manager.on_chain_end( await run_manager.on_chain_end(output)
_trace_payload(output, self.trace_outputs)
)
else: else:
try: try:
async with AsyncExitStack() as stack: async with AsyncExitStack() as stack:
@@ -928,9 +915,7 @@ class RunnableSeq(Runnable):
await run_manager.on_chain_error(e) await run_manager.on_chain_error(e)
raise raise
else: else:
await run_manager.on_chain_end( await run_manager.on_chain_end(output)
_trace_payload(output, self.trace_outputs)
)
def _consume_iter(it: Iterator[Any]) -> Any: def _consume_iter(it: Iterator[Any]) -> Any:
+1 -1
View File
@@ -22,7 +22,7 @@ from pydantic import BaseModel
from typing_extensions import NotRequired, Required, is_typeddict from typing_extensions import NotRequired, Required, is_typeddict
try: try:
from langgraph.checkpoint.serde._msgpack import ( from langgraph.checkpoint.serde._msgpack import ( # noqa: F401
STRICT_MSGPACK_ENABLED, STRICT_MSGPACK_ENABLED,
) )
except ImportError: except ImportError:
+1 -1
View File
@@ -7,7 +7,7 @@ from typing import Any, Literal
from warnings import warn from warnings import warn
# EmptyChannelError is re-exported from langgraph.channels.base # EmptyChannelError is re-exported from langgraph.channels.base
from langgraph.checkpoint.base import EmptyChannelError from langgraph.checkpoint.base import EmptyChannelError # noqa: F401
from typing_extensions import deprecated from typing_extensions import deprecated
from langgraph.types import Command, Interrupt from langgraph.types import Command, Interrupt
+1 -9
View File
@@ -9,13 +9,7 @@ from langgraph.store.base import BaseStore
from langgraph._internal._typing import EMPTY_SEQ from langgraph._internal._typing import EMPTY_SEQ
from langgraph.runtime import Runtime from langgraph.runtime import Runtime
from langgraph.types import ( from langgraph.types import CachePolicy, RetryPolicy, StreamWriter, TimeoutPolicy
CachePolicy,
RetryPolicy,
StreamWriter,
TimeoutPolicy,
TracePolicy,
)
from langgraph.typing import ContextT, NodeInputT, NodeInputT_contra from langgraph.typing import ContextT, NodeInputT, NodeInputT_contra
@@ -99,5 +93,3 @@ class StateNodeSpec(Generic[NodeInputT, ContextT]):
ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ
defer: bool = False defer: bool = False
timeout: TimeoutPolicy | None = None timeout: TimeoutPolicy | None = None
trace_policy: TracePolicy | None = None
"""Optional policy controlling what this node records on its trace run."""
+1 -15
View File
@@ -85,7 +85,6 @@ from langgraph.types import (
RetryPolicy, RetryPolicy,
Send, Send,
TimeoutPolicy, TimeoutPolicy,
TracePolicy,
ensure_valid_checkpointer, ensure_valid_checkpointer,
) )
from langgraph.typing import ContextT, InputT, NodeInputT, OutputT, StateT from langgraph.typing import ContextT, InputT, NodeInputT, OutputT, StateT
@@ -385,7 +384,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
error_handler: StateNode[Any, ContextT] | None = None, error_handler: StateNode[Any, ContextT] | None = None,
destinations: dict[str, str] | tuple[str, ...] | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None,
timeout: float | timedelta | TimeoutPolicy | None = None, timeout: float | timedelta | TimeoutPolicy | None = None,
trace_policy: TracePolicy | None = None,
**kwargs: Unpack[DeprecatedKwargs], **kwargs: Unpack[DeprecatedKwargs],
) -> Self: ) -> Self:
"""Add a new node to the `StateGraph`, input schema is inferred as the state schema. """Add a new node to the `StateGraph`, input schema is inferred as the state schema.
@@ -455,7 +453,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
error_handler: StateNode[Any, ContextT] | None = None, error_handler: StateNode[Any, ContextT] | None = None,
destinations: dict[str, str] | tuple[str, ...] | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None,
timeout: float | timedelta | TimeoutPolicy | None = None, timeout: float | timedelta | TimeoutPolicy | None = None,
trace_policy: TracePolicy | None = None,
**kwargs: Unpack[DeprecatedKwargs], **kwargs: Unpack[DeprecatedKwargs],
) -> Self: ) -> Self:
"""Add a new node to the `StateGraph` where input schema is specified. """Add a new node to the `StateGraph` where input schema is specified.
@@ -530,7 +527,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
error_handler: StateNode[Any, ContextT] | None = None, error_handler: StateNode[Any, ContextT] | None = None,
destinations: dict[str, str] | tuple[str, ...] | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None,
timeout: float | timedelta | TimeoutPolicy | None = None, timeout: float | timedelta | TimeoutPolicy | None = None,
trace_policy: TracePolicy | None = None,
**kwargs: Unpack[DeprecatedKwargs], **kwargs: Unpack[DeprecatedKwargs],
) -> Self: ) -> Self:
"""Add a new node to the `StateGraph`, input schema is inferred as the state schema. """Add a new node to the `StateGraph`, input schema is inferred as the state schema.
@@ -600,7 +596,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
error_handler: StateNode[Any, ContextT] | None = None, error_handler: StateNode[Any, ContextT] | None = None,
destinations: dict[str, str] | tuple[str, ...] | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None,
timeout: float | timedelta | TimeoutPolicy | None = None, timeout: float | timedelta | TimeoutPolicy | None = None,
trace_policy: TracePolicy | None = None,
**kwargs: Unpack[DeprecatedKwargs], **kwargs: Unpack[DeprecatedKwargs],
) -> Self: ) -> Self:
"""Add a new node to the `StateGraph`, input schema is specified. """Add a new node to the `StateGraph`, input schema is specified.
@@ -677,7 +672,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
error_handler: StateNode[Any, ContextT] | None = None, error_handler: StateNode[Any, ContextT] | None = None,
destinations: dict[str, str] | tuple[str, ...] | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None,
timeout: float | timedelta | TimeoutPolicy | None = None, timeout: float | timedelta | TimeoutPolicy | None = None,
trace_policy: TracePolicy | None = None,
**kwargs: Unpack[DeprecatedKwargs], **kwargs: Unpack[DeprecatedKwargs],
) -> Self: ) -> Self:
"""Add a new node to the `StateGraph`. """Add a new node to the `StateGraph`.
@@ -697,10 +691,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
If a sequence is provided, the first matching policy will be applied. If a sequence is provided, the first matching policy will be applied.
cache_policy: The cache policy for the node. cache_policy: The cache policy for the node.
error_handler: Optional node-level error handler callable for this node. error_handler: Optional node-level error handler callable for this node.
trace_policy: Optional policy controlling how this node's run is traced. Its
`process_inputs` callable transforms the node's input before it is
recorded (e.g. to omit or summarize large message history) without
changing the value passed to the node. Does not affect execution.
destinations: Destinations that indicate where a node can route to. destinations: Destinations that indicate where a node can route to.
Useful for edgeless graphs with nodes that return `Command` objects. Useful for edgeless graphs with nodes that return `Command` objects.
@@ -890,7 +880,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
ends=ends, ends=ends,
defer=defer, defer=defer,
timeout=timeout, timeout=timeout,
trace_policy=trace_policy,
) )
elif inferred_input_schema is not None: elif inferred_input_schema is not None:
self.nodes[node] = StateNodeSpec( self.nodes[node] = StateNodeSpec(
@@ -903,7 +892,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
ends=ends, ends=ends,
defer=defer, defer=defer,
timeout=timeout, timeout=timeout,
trace_policy=trace_policy,
) )
else: else:
self.nodes[node] = StateNodeSpec[StateT, ContextT]( self.nodes[node] = StateNodeSpec[StateT, ContextT](
@@ -916,7 +904,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
ends=ends, ends=ends,
defer=defer, defer=defer,
timeout=timeout, timeout=timeout,
trace_policy=trace_policy,
) )
input_schema = input_schema or inferred_input_schema input_schema = input_schema or inferred_input_schema
@@ -1008,7 +995,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
Without type hints on the `path` function's return value (e.g., `-> Literal["foo", "__end__"]:`) Without type hints on the `path` function's return value (e.g., `-> Literal["foo", "__end__"]:`)
or a path_map, the graph visualization assumes the edge could transition to any node in the graph. or a path_map, the graph visualization assumes the edge could transition to any node in the graph.
""" """ # noqa: E501
if self.compiled: if self.compiled:
logger.warning( logger.warning(
"Adding an edge to a graph that has already been compiled. This will " "Adding an edge to a graph that has already been compiled. This will "
@@ -1543,7 +1530,6 @@ class CompiledStateGraph(
error_handler_node=node.error_handler_node, error_handler_node=node.error_handler_node,
bound=node.runnable, # type: ignore[arg-type] bound=node.runnable, # type: ignore[arg-type]
timeout=node.timeout, timeout=node.timeout,
trace_policy=node.trace_policy,
) )
else: else:
raise RuntimeError raise RuntimeError
+9 -6
View File
@@ -198,6 +198,7 @@ class PregelLoop:
_migrate_checkpoint: Callable[[Checkpoint], None] | None _migrate_checkpoint: Callable[[Checkpoint], None] | None
submit: Submit submit: Submit
channels: Mapping[str, BaseChannel] channels: Mapping[str, BaseChannel]
_has_untracked_channels: bool
# Futures from `checkpointer.put_writes` calls that produced delta-channel # Futures from `checkpointer.put_writes` calls that produced delta-channel
# writes. `_checkpointer_put_after_previous` drains this list (swap to a # writes. `_checkpointer_put_after_previous` drains this list (swap to a
# local `futs` then reset to `[]` and wait/gather) before putting the # local `futs` then reset to `[]` and wait/gather) before putting the
@@ -437,9 +438,7 @@ class PregelLoop:
writes_to_save = writes writes_to_save = writes
# check if any writes are to an UntrackedValue channel # check if any writes are to an UntrackedValue channel
if any( if self._has_untracked_channels:
isinstance(channel, UntrackedValue) for channel in self.channels.values()
):
# we do not persist untracked values in checkpoints # we do not persist untracked values in checkpoints
writes_to_save = [ writes_to_save = [
# sanitize UntrackedValues that are nested within Send packets # sanitize UntrackedValues that are nested within Send packets
@@ -1161,9 +1160,7 @@ class PregelLoop:
elif "counters_since_delta_snapshot" in self.checkpoint_metadata: elif "counters_since_delta_snapshot" in self.checkpoint_metadata:
del self.checkpoint_metadata["counters_since_delta_snapshot"] del self.checkpoint_metadata["counters_since_delta_snapshot"]
# sanitize TASK channel in the checkpoint before saving (durability=="exit") # sanitize TASK channel in the checkpoint before saving (durability=="exit")
if TASKS in self.checkpoint["channel_values"] and any( if TASKS in self.checkpoint["channel_values"] and self._has_untracked_channels:
isinstance(channel, UntrackedValue) for channel in self.channels.values()
):
sanitized_tasks = [ sanitized_tasks = [
sanitize_untracked_values_in_send(value, self.channels) sanitize_untracked_values_in_send(value, self.channels)
if isinstance(value, Send) if isinstance(value, Send)
@@ -1695,6 +1692,9 @@ class SyncPregelLoop(PregelLoop, AbstractContextManager):
saver=self.checkpointer, saver=self.checkpointer,
config=self.checkpoint_config, config=self.checkpoint_config,
) )
self._has_untracked_channels = any(
isinstance(ch, UntrackedValue) for ch in self.channels.values()
)
self.stack.push(self._suppress_interrupt) self.stack.push(self._suppress_interrupt)
self.status = "input" self.status = "input"
self.step = self.checkpoint_metadata["step"] + 1 self.step = self.checkpoint_metadata["step"] + 1
@@ -1955,6 +1955,9 @@ class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager):
saver=self.checkpointer, saver=self.checkpointer,
config=self.checkpoint_config, config=self.checkpoint_config,
) )
self._has_untracked_channels = any(
isinstance(ch, UntrackedValue) for ch in self.channels.values()
)
self.stack.push(self._suppress_interrupt) self.stack.push(self._suppress_interrupt)
self.status = "input" self.status = "input"
self.step = self.checkpoint_metadata["step"] + 1 self.step = self.checkpoint_metadata["step"] + 1
+3 -17
View File
@@ -16,7 +16,7 @@ from langgraph._internal._timeout import coerce_timeout_policy
from langgraph.pregel._utils import find_subgraph_pregel from langgraph.pregel._utils import find_subgraph_pregel
from langgraph.pregel._write import ChannelWrite from langgraph.pregel._write import ChannelWrite
from langgraph.pregel.protocol import PregelProtocol from langgraph.pregel.protocol import PregelProtocol
from langgraph.types import CachePolicy, RetryPolicy, TimeoutPolicy, TracePolicy from langgraph.types import CachePolicy, RetryPolicy, TimeoutPolicy
READ_TYPE = Callable[[str | Sequence[str], bool], Any | dict[str, Any]] READ_TYPE = Callable[[str | Sequence[str], bool], Any | dict[str, Any]]
INPUT_CACHE_KEY_TYPE = tuple[Callable[..., Any], tuple[str, ...]] INPUT_CACHE_KEY_TYPE = tuple[Callable[..., Any], tuple[str, ...]]
@@ -138,9 +138,6 @@ class PregelNode:
metadata: Mapping[str, Any] | None metadata: Mapping[str, Any] | None
"""Metadata to attach to the node for tracing.""" """Metadata to attach to the node for tracing."""
trace_policy: TracePolicy | None
"""Optional policy controlling what this node records on its trace run."""
is_error_handler: bool is_error_handler: bool
"""Whether this node is registered as an error handler node.""" """Whether this node is registered as an error handler node."""
@@ -159,7 +156,6 @@ class PregelNode:
writers: list[Runnable] | None = None, writers: list[Runnable] | None = None,
tags: list[str] | None = None, tags: list[str] | None = None,
metadata: Mapping[str, Any] | None = None, metadata: Mapping[str, Any] | None = None,
trace_policy: TracePolicy | None = None,
bound: Runnable[Any, Any] | None = None, bound: Runnable[Any, Any] | None = None,
retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None, retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None,
cache_policy: CachePolicy | None = None, cache_policy: CachePolicy | None = None,
@@ -181,7 +177,6 @@ class PregelNode:
self.timeout = coerce_timeout_policy(timeout) self.timeout = coerce_timeout_policy(timeout)
self.tags = tags self.tags = tags
self.metadata = metadata self.metadata = metadata
self.trace_policy = trace_policy
self.is_error_handler = is_error_handler self.is_error_handler = is_error_handler
self.error_handler_node = error_handler_node self.error_handler_node = error_handler_node
if subgraphs is not None: if subgraphs is not None:
@@ -227,23 +222,14 @@ class PregelNode:
def node(self) -> Runnable[Any, Any] | None: def node(self) -> Runnable[Any, Any] | None:
"""Get a runnable that combines `bound` and `writers`.""" """Get a runnable that combines `bound` and `writers`."""
writers = self.flat_writers writers = self.flat_writers
trace_inputs = self.trace_policy.process_inputs if self.trace_policy else None
trace_outputs = self.trace_policy.process_outputs if self.trace_policy else None
if self.bound is DEFAULT_BOUND and not writers: if self.bound is DEFAULT_BOUND and not writers:
return None return None
elif self.bound is DEFAULT_BOUND and len(writers) == 1: elif self.bound is DEFAULT_BOUND and len(writers) == 1:
return writers[0] return writers[0]
elif self.bound is DEFAULT_BOUND: elif self.bound is DEFAULT_BOUND:
return RunnableSeq( return RunnableSeq(*writers)
*writers, trace_inputs=trace_inputs, trace_outputs=trace_outputs
)
elif writers: elif writers:
return RunnableSeq( return RunnableSeq(self.bound, *writers)
self.bound,
*writers,
trace_inputs=trace_inputs,
trace_outputs=trace_outputs,
)
else: else:
return self.bound return self.bound
+141 -71
View File
@@ -1,10 +1,11 @@
from __future__ import annotations from __future__ import annotations
import dis import ast
import inspect
import re import re
import textwrap
from collections.abc import Callable, Sequence from collections.abc import Callable, Sequence
from functools import partial from functools import partial
from types import CodeType, FunctionType
from typing import Any from typing import Any
from langchain_core.runnables import ( from langchain_core.runnables import (
@@ -16,6 +17,7 @@ from langchain_core.runnables import (
from langchain_core.runnables.base import RunnableBindingBase from langchain_core.runnables.base import RunnableBindingBase
from langchain_core.runnables.config import run_in_executor from langchain_core.runnables.config import run_in_executor
from langgraph.checkpoint.base import ChannelVersions from langgraph.checkpoint.base import ChannelVersions
from typing_extensions import override
from langgraph._internal._runnable import RunnableCallable, RunnableSeq from langgraph._internal._runnable import RunnableCallable, RunnableSeq
from langgraph._internal._timeout import sync_timeout_unsupported from langgraph._internal._timeout import sync_timeout_unsupported
@@ -135,87 +137,155 @@ def validate_timeout_supported(runnable: Runnable, *, name: str) -> None:
raise sync_timeout_unsupported(name) raise sync_timeout_unsupported(name)
# Values treated as dead ends when deciding whether to walk a function's
# bytecode. A container can hold a graph, but `find_subgraph_pregel` does not
# look inside one, so skipping it costs nothing while that holds. Matched by
# exact type, since a subclass of a builtin can carry attributes.
_LEAF_TYPES = frozenset(
{
int,
float,
complex,
bool,
str,
bytes,
bytearray,
list,
tuple,
dict,
set,
frozenset,
type(None),
}
)
def get_function_nonlocals(func: Callable) -> list[Any]: def get_function_nonlocals(func: Callable) -> list[Any]:
"""Get the values a function reaches from outside its own scope. """Get the nonlocal variables accessed by a function.
Args: Args:
func: The function to check. func: The function to check.
Returns: Returns:
Every captured cell value, the globals the function names, and each List[Any]: The nonlocal variables accessed by the function.
value along an attribute path it loads. Over-approximates: a value can
come back without the function reaching it at runtime.
""" """
func = getattr(func, "__func__", func) # bound method -> function
wrapped = getattr(func, "__wrapped__", None)
if callable(wrapped):
func = getattr(wrapped, "__func__", wrapped)
if not isinstance(func, FunctionType):
return []
code = func.__code__
cells: dict[str, Any] = {}
for name, cell in zip(code.co_freevars, func.__closure__ or ()):
try: try:
cells[name] = cell.cell_contents code = inspect.getsource(func)
except ValueError: tree = ast.parse(textwrap.dedent(code))
continue # empty cell: a recursive def not yet bound visitor = FunctionNonLocals()
visitor.visit(tree)
# Every captured value counts, referenced or not: over-declaring costs an values: list[Any] = []
# introspection entry, under-declaring drops the subgraph's checkpoints and closure = (
# stream events. Checking each cell against the bytecode would cost more and inspect.getclosurevars(func.__wrapped__)
# only trade the cheap error for the expensive one. if hasattr(func, "__wrapped__") and callable(func.__wrapped__)
values: list[Any] = list(cells.values()) else inspect.getclosurevars(func)
global_ns = func.__globals__ )
globals_ = {name: global_ns[name] for name in code.co_names if name in global_ns} candidates = {**closure.globals, **closure.nonlocals}
if all(type(v) in _LEAF_TYPES for v in (*cells.values(), *globals_.values())): for k, v in candidates.items():
return values if k in visitor.nonlocals:
values.append(v)
# Nested code objects hold the references made by inner defs, lambdas and for kk in visitor.nonlocals:
# comprehensions, which resolve against the namespaces gathered above. if "." in kk and kk.startswith(k):
codes = [code] vv = v
for c in codes: for part in kk.split(".")[1:]:
codes.extend(k for k in c.co_consts if isinstance(k, CodeType)) if vv is None:
value: Any = None break
for instruction in dis.get_instructions(c):
opname = instruction.opname
if opname == "LOAD_GLOBAL":
value = globals_.get(instruction.argval)
elif opname == "LOAD_DEREF":
value = cells.get(instruction.argval)
elif opname in ("LOAD_ATTR", "LOAD_METHOD"):
value = getattr(value, instruction.argval, None)
else: else:
value = None # anything else ends the chain: `a, b.c` is not `a.c` try:
continue vv = getattr(vv, part)
if value is not None: except AttributeError:
values.append(value) break
else:
values.append(vv)
except (SyntaxError, TypeError, OSError, SystemError):
return []
return values return values
class FunctionNonLocals(ast.NodeVisitor):
"""Get the nonlocal variables accessed of a function."""
def __init__(self) -> None:
self.nonlocals: set[str] = set()
@override
def visit_FunctionDef(self, node: ast.FunctionDef) -> Any:
"""Visit a function definition.
Args:
node: The node to visit.
Returns:
Any: The result of the visit.
"""
visitor = NonLocals()
visitor.visit(node)
self.nonlocals.update(visitor.loads - visitor.stores)
@override
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> Any:
"""Visit an async function definition.
Args:
node: The node to visit.
Returns:
Any: The result of the visit.
"""
visitor = NonLocals()
visitor.visit(node)
self.nonlocals.update(visitor.loads - visitor.stores)
@override
def visit_Lambda(self, node: ast.Lambda) -> Any:
"""Visit a lambda function.
Args:
node: The node to visit.
Returns:
Any: The result of the visit.
"""
visitor = NonLocals()
visitor.visit(node)
self.nonlocals.update(visitor.loads - visitor.stores)
class NonLocals(ast.NodeVisitor):
"""Get nonlocal variables accessed."""
def __init__(self) -> None:
self.loads: set[str] = set()
self.stores: set[str] = set()
@override
def visit_Name(self, node: ast.Name) -> Any:
"""Visit a name node.
Args:
node: The node to visit.
Returns:
Any: The result of the visit.
"""
if isinstance(node.ctx, ast.Load):
self.loads.add(node.id)
elif isinstance(node.ctx, ast.Store):
self.stores.add(node.id)
@override
def visit_Attribute(self, node: ast.Attribute) -> Any:
"""Visit an attribute node.
Args:
node: The node to visit.
Returns:
Any: The result of the visit.
"""
if isinstance(node.ctx, ast.Load):
parent = node.value
attr_expr = node.attr
while isinstance(parent, ast.Attribute):
attr_expr = parent.attr + "." + attr_expr
parent = parent.value
if isinstance(parent, ast.Name):
self.loads.add(parent.id + "." + attr_expr)
self.loads.discard(parent.id)
elif isinstance(parent, ast.Call):
if isinstance(parent.func, ast.Name):
self.loads.add(parent.func.id)
else:
parent = parent.func
attr_expr = ""
while isinstance(parent, ast.Attribute):
if attr_expr:
attr_expr = parent.attr + "." + attr_expr
else:
attr_expr = parent.attr
parent = parent.value
if isinstance(parent, ast.Name):
self.loads.add(parent.id + "." + attr_expr)
def is_xxh3_128_hexdigest(value: str) -> bool: def is_xxh3_128_hexdigest(value: str) -> bool:
"""Check if the given string matches the format of xxh3_128_hexdigest.""" """Check if the given string matches the format of xxh3_128_hexdigest."""
return bool(re.fullmatch(r"[0-9a-f]{32}", value)) return bool(re.fullmatch(r"[0-9a-f]{32}", value))
+3 -3
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from abc import abstractmethod from abc import abstractmethod
from collections.abc import AsyncIterator, Callable, Iterator, Sequence from collections.abc import AsyncIterator, Callable, Iterator, Sequence
from typing import Any, Generic, Literal, overload from typing import Any, Generic, Literal, cast, overload
from langchain_core.runnables import Runnable, RunnableConfig from langchain_core.runnables import Runnable, RunnableConfig
from langchain_core.runnables.graph import Graph as DrawableGraph from langchain_core.runnables.graph import Graph as DrawableGraph
@@ -277,12 +277,12 @@ class StreamProtocol:
modes: set[StreamMode] modes: set[StreamMode]
__call__: Callable[[StreamChunk], None] __call__: Callable[[Self, StreamChunk], None]
def __init__( def __init__(
self, self,
__call__: Callable[[StreamChunk], None], __call__: Callable[[StreamChunk], None],
modes: set[StreamMode], modes: set[StreamMode],
) -> None: ) -> None:
self.__call__ = __call__ self.__call__ = cast(Callable[[Self, StreamChunk], None], __call__)
self.modes = modes self.modes = modes
+11 -57
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import AsyncIterator, Awaitable, Callable, Iterator, Mapping from collections.abc import AsyncIterator, Awaitable, Callable, Iterator, Mapping
from types import MappingProxyType, TracebackType from types import MappingProxyType, TracebackType
from typing import TYPE_CHECKING, Any, NoReturn from typing import TYPE_CHECKING, Any
from langchain_core._api import beta from langchain_core._api import beta
@@ -33,26 +33,6 @@ async def _adrive_until_done(pump: Callable[[], Awaitable[bool]]) -> None:
pass pass
def _raise_missing_projection(run: object, name: str) -> NoReturn:
"""Raise after normal attribute lookup fails for a projection.
Registered native projections are installed directly on the run instance
during `__init__`, so `__getattr__` is never called for them. At this point
the requested name is necessarily missing; the mux is inspected only to
include the valid registered projection names in the error message.
Read `_mux` directly from `__dict__` because it may not exist yet on a
partially initialized instance. Accessing `run._mux` in that case would
invoke `__getattr__` again and recurse indefinitely.
"""
mux = run.__dict__.get("_mux")
registered = sorted(mux.native_keys) if mux is not None else []
raise AttributeError(
f"{type(run).__name__!r} object has no attribute {name!r} "
f"(registered projections: {', '.join(registered) or 'none'})"
)
@beta(message="The v3 streaming protocol on Pregel is experimental.") @beta(message="The v3 streaming protocol on Pregel is experimental.")
class GraphRunStream: class GraphRunStream:
"""Sync run stream with caller-driven pumping. """Sync run stream with caller-driven pumping.
@@ -74,32 +54,15 @@ class GraphRunStream:
experimental and may change. experimental and may change.
""" """
# Native projections, attached dynamically by the `setattr` loop in # Native projections always registered by `stream_events(version="v3")`.
# `__init__` and declared here so type checkers see them. # Attached dynamically by the `setattr` loop in `__init__`; declared here
# # so type checkers see them. Opt-in native projections (`updates`,
# Always registered by `stream_events(version="v3")`: # `custom`, `checkpoints`, `debug`, `tasks`) are only present when their
# transformer is registered, so they are reached via `extensions[...]`.
values: StreamChannel[dict[str, Any]] values: StreamChannel[dict[str, Any]]
messages: StreamChannel[ChatModelStream] messages: StreamChannel[ChatModelStream]
lifecycle: StreamChannel[LifecyclePayload] lifecycle: StreamChannel[LifecyclePayload]
subgraphs: StreamChannel[SubgraphRunStream] subgraphs: StreamChannel[SubgraphRunStream]
# Registered on demand via `compile(transformers=...)` or
# `stream_events(transformers=...)`; reading one whose transformer was not
# registered raises AttributeError. Projections contributed by transformers
# outside this package are covered by `__getattr__` instead.
updates: StreamChannel[dict[str, Any]]
custom: StreamChannel[Any]
checkpoints: StreamChannel[dict[str, Any]]
debug: StreamChannel[dict[str, Any]]
tasks: StreamChannel[dict[str, Any]]
def __getattr__(self, name: str) -> StreamChannel[Any]:
"""Type the projections of transformers declared outside this package.
Projection names come from a registry, so no annotation here can name
them all. The cost is that a misspelling type-checks too, and fails at
runtime instead.
"""
_raise_missing_projection(self, name)
def __init__( def __init__(
self, self,
@@ -382,24 +345,15 @@ class AsyncGraphRunStream:
experimental and may change. experimental and may change.
""" """
# Native projections, attached dynamically by the `setattr` loop in # Native projections always registered by `astream_events(version="v3")`.
# `__init__` and declared here so type checkers see them. # Attached dynamically by the `setattr` loop in `__init__`; declared here
# # so type checkers see them. Opt-in native projections (`updates`,
# Always registered by `astream_events(version="v3")`: # `custom`, `checkpoints`, `debug`, `tasks`) are only present when their
# transformer is registered, so they are reached via `extensions[...]`.
values: StreamChannel[dict[str, Any]] values: StreamChannel[dict[str, Any]]
messages: StreamChannel[AsyncChatModelStream] messages: StreamChannel[AsyncChatModelStream]
lifecycle: StreamChannel[LifecyclePayload] lifecycle: StreamChannel[LifecyclePayload]
subgraphs: StreamChannel[AsyncSubgraphRunStream] subgraphs: StreamChannel[AsyncSubgraphRunStream]
# Registered on demand; see `GraphRunStream`.
updates: StreamChannel[dict[str, Any]]
custom: StreamChannel[Any]
checkpoints: StreamChannel[dict[str, Any]]
debug: StreamChannel[dict[str, Any]]
tasks: StreamChannel[dict[str, Any]]
def __getattr__(self, name: str) -> StreamChannel[Any]:
"""Type projections declared elsewhere. See `GraphRunStream`."""
_raise_missing_projection(self, name)
def __init__( def __init__(
self, self,
-40
View File
@@ -70,8 +70,6 @@ __all__ = (
"RetryPolicy", "RetryPolicy",
"TimeoutPolicy", "TimeoutPolicy",
"CachePolicy", "CachePolicy",
"TracePolicy",
"omit_payload",
"Interrupt", "Interrupt",
"StateUpdate", "StateUpdate",
"PregelTask", "PregelTask",
@@ -529,44 +527,6 @@ class CachePolicy(Generic[KeyFuncT]):
"""Time to live for the cache entry in seconds. If `None`, the entry never expires.""" """Time to live for the cache entry in seconds. If `None`, the entry never expires."""
@dataclass(**_DC_KWARGS)
class TracePolicy:
"""Configuration for how a node's run is traced.
Scope: this only transforms what the node's *own* run records. Child runs created
by a traced `bound` runnable and the root graph run are not affected. Plain
function nodes are traced with `trace=False`, so they have no such child runs.
Not intended to redact secrets. To redact inputs/outputs across all runs
(children included), use the LangSmith client's
`hide_inputs`/`hide_outputs`/`anonymizer` instead.
Each processor receives the node's raw input/output value (not a normalized
kwargs dict) and returns the value to record.
"""
process_inputs: Callable[[Any], Any] | None = None
"""Optional callable to transform the node's input before it is recorded on the
node's trace run. Can be used to omit or summarize large payloads
(e.g. message history). Not intended to affect the value passed to the node; avoid
mutating arguments in place."""
process_outputs: Callable[[Any], Any] | None = None
"""Optional callable to transform the node's output before it is recorded on the
node's trace run. Can be used to omit or summarize large payloads
(e.g. message history). Not intended to affect the value returned by the node; avoid
mutating arguments in place."""
def omit_payload(_value: Any) -> dict[str, Any]:
"""`TracePolicy` helper that records an empty payload, dropping the value entirely.
Use as `process_inputs` and/or `process_outputs` on a `TracePolicy` to keep a node's
span and its timing while omitting its inputs/outputs from the trace.
"""
return {}
_DEFAULT_INTERRUPT_ID = "placeholder-id" _DEFAULT_INTERRUPT_ID = "placeholder-id"
+2 -6
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "langgraph" name = "langgraph"
version = "1.2.11" version = "1.2.9"
description = "Building stateful, multi-actor applications with LLMs" description = "Building stateful, multi-actor applications with LLMs"
authors = [] authors = []
requires-python = ">=3.10" requires-python = ">=3.10"
@@ -89,12 +89,8 @@ langgraph-sdk = { path = "../sdk-py", editable = true }
langgraph-cli = { path = "../cli", editable = true } langgraph-cli = { path = "../cli", editable = true }
[tool.ruff] [tool.ruff]
lint.select = [ "E", "F", "I", "PLC0415", "RUF100", "TID251", "UP" ] lint.select = [ "E", "F", "I", "TID251", "UP" ]
lint.ignore = [ "E501" ] lint.ignore = [ "E501" ]
# PLC0415 (import-outside-top-level) is enforced in tests only. Library code
# still has deferred imports that have not been reviewed, so it stays exempt
# for now.
lint.per-file-ignores = { "langgraph/**" = ["PLC0415"] }
line-length = 88 line-length = 88
indent-width = 4 indent-width = 4
extend-include = ["*.ipynb"] extend-include = ["*.ipynb"]
+2 -1
View File
@@ -1,6 +1,5 @@
import os import os
import tempfile import tempfile
import time
from collections import defaultdict from collections import defaultdict
from functools import partial from functools import partial
from typing import Any from typing import Any
@@ -74,6 +73,8 @@ class MemorySaverAssertImmutable(InMemorySaver):
new_versions: ChannelVersions, new_versions: ChannelVersions,
) -> None: ) -> None:
if self.put_sleep: if self.put_sleep:
import time
time.sleep(self.put_sleep) time.sleep(self.put_sleep)
# assert checkpoint hasn't been modified since last written # assert checkpoint hasn't been modified since last written
thread_id = config["configurable"]["thread_id"] thread_id = config["configurable"]["thread_id"]
+8 -3
View File
@@ -2,16 +2,14 @@ import operator
from collections.abc import Sequence from collections.abc import Sequence
from typing import Annotated from typing import Annotated
import orjson
import pytest import pytest
from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage
from langgraph.checkpoint.memory import InMemorySaver from langgraph.checkpoint.memory import InMemorySaver
from langgraph.checkpoint.serde.types import _DeltaSnapshot from langgraph.checkpoint.serde.types import _DeltaSnapshot
from typing_extensions import NotRequired, TypedDict from typing_extensions import NotRequired, TypedDict
from langgraph._internal._constants import OVERWRITE
from langgraph._internal._typing import MISSING from langgraph._internal._typing import MISSING
from langgraph.channels.binop import BinaryOperatorAggregate, _get_overwrite from langgraph.channels.binop import BinaryOperatorAggregate
from langgraph.channels.delta import DeltaChannel from langgraph.channels.delta import DeltaChannel
from langgraph.channels.last_value import LastValue from langgraph.channels.last_value import LastValue
from langgraph.channels.topic import Topic from langgraph.channels.topic import Topic
@@ -196,6 +194,10 @@ def test_overwrite_dataclass_form_survives_json_roundtrip() -> None:
...}`) is indistinguishable from a literal channel value, and downstream ...}`) is indistinguishable from a literal channel value, and downstream
reducers raise `MESSAGE_COERCION_FAILURE` (or similar) on read. reducers raise `MESSAGE_COERCION_FAILURE` (or similar) on read.
""" """
import orjson
from langgraph._internal._constants import OVERWRITE
from langgraph.channels.binop import _get_overwrite
ow = Overwrite(value=[HumanMessage(content="new", id="h2")]) ow = Overwrite(value=[HumanMessage(content="new", id="h2")])
erased = orjson.loads(orjson.dumps(ow, default=lambda o: o.model_dump())) erased = orjson.loads(orjson.dumps(ow, default=lambda o: o.model_dump()))
@@ -211,6 +213,8 @@ def test_overwrite_sentinel_dict_still_recognised() -> None:
"""The pre-existing `{"__overwrite__": value}` dict form continues to be """The pre-existing `{"__overwrite__": value}` dict form continues to be
recognised. This is the canonical sentinel emitted by producers that do recognised. This is the canonical sentinel emitted by producers that do
not have an `Overwrite` dataclass available.""" not have an `Overwrite` dataclass available."""
from langgraph._internal._constants import OVERWRITE
from langgraph.channels.binop import _get_overwrite
is_overwrite, value = _get_overwrite({OVERWRITE: ["b"]}) is_overwrite, value = _get_overwrite({OVERWRITE: ["b"]})
assert is_overwrite assert is_overwrite
@@ -220,6 +224,7 @@ def test_overwrite_sentinel_dict_still_recognised() -> None:
def test_overwrite_non_matching_dict_not_recognised() -> None: def test_overwrite_non_matching_dict_not_recognised() -> None:
"""Dicts that resemble the erased shape but do not carry the """Dicts that resemble the erased shape but do not carry the
`__overwrite__` discriminator must not be misclassified as overwrites.""" `__overwrite__` discriminator must not be misclassified as overwrites."""
from langgraph.channels.binop import _get_overwrite
assert _get_overwrite({"value": ["b"]}) == (False, None) assert _get_overwrite({"value": ["b"]}) == (False, None)
assert _get_overwrite({"type": "human", "value": "hi"}) == (False, None) assert _get_overwrite({"type": "human", "value": "hi"}) == (False, None)
+4 -4
View File
@@ -24,7 +24,7 @@ class _TrackingCallback(BaseCallbackHandler):
def __init__(self) -> None: def __init__(self) -> None:
self.called = False self.called = False
def on_chain_start(self, *args, **kwargs) -> None: def on_chain_start(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
self.called = True self.called = True
@@ -55,7 +55,7 @@ async def test_with_config_configurable_preserved_on_invoke() -> None:
builder = StateGraph(dict) builder = StateGraph(dict)
captured: dict = {} captured: dict = {}
def node(state, config): def node(state, config): # noqa: ANN001
captured.update(config.get("configurable") or {}) captured.update(config.get("configurable") or {})
return state return state
@@ -79,7 +79,7 @@ async def test_with_config_metadata_preserved_on_invoke() -> None:
builder = StateGraph(dict) builder = StateGraph(dict)
captured: dict = {} captured: dict = {}
def node(state, config): def node(state, config): # noqa: ANN001
captured.update(config.get("metadata") or {}) captured.update(config.get("metadata") or {})
return state return state
@@ -104,7 +104,7 @@ async def test_with_config_tags_preserved_on_invoke() -> None:
builder = StateGraph(dict) builder = StateGraph(dict)
captured: list = [] captured: list = []
def node(state, config): def node(state, config): # noqa: ANN001
captured.extend(config.get("tags") or []) captured.extend(config.get("tags") or [])
return state return state
@@ -220,7 +220,7 @@ def _checkpointers() -> list[tuple[str, Any]]:
result: list[tuple[str, Any]] = [("InMemory", None)] result: list[tuple[str, Any]] = [("InMemory", None)]
if _POSTGRES_AVAILABLE: if _POSTGRES_AVAILABLE:
try: try:
import psycopg # noqa: PLC0415 import psycopg
psycopg.connect(_POSTGRES_URI).close() psycopg.connect(_POSTGRES_URI).close()
result.append(("Postgres", "postgres")) result.append(("Postgres", "postgres"))
@@ -616,117 +616,3 @@ async def test_add_messages_to_delta_migration_preserves_message_history_async()
assert [m.id for m in snap.values["messages"]] == ["h1", "a1"], ( assert [m.id for m in snap.values["messages"]] == ["h1", "a1"], (
f"async tip hydration mismatch: got {[m.id for m in snap.values['messages']]}" f"async tip hydration mismatch: got {[m.id for m in snap.values['messages']]}"
) )
# ---------------------------------------------------------------------------
# 8. First post-migration write, read back cold (regression for #8384)
#
# The migration boundary produces a checkpoint that carries BOTH a pre-delta
# plain-value blob AND the pending write that produced its (delta-era) child.
# That write is not subsumed by the blob — the blob is the value ENTERING that
# checkpoint. A saver whose ancestor walk skips the seed checkpoint's own
# writes silently drops the first post-migration write.
#
# The failure is invisible to the live `invoke` return value (computed
# in-memory before persistence), so these tests must assert on a COLD read.
# It is also invisible at `snapshot_frequency=1`, where every write is its own
# snapshot boundary and the walk never terminates on a plain value — hence the
# explicit default-frequency coverage.
# ---------------------------------------------------------------------------
def test_first_post_migration_write_survives_cold_read() -> None:
"""One non-snapshotting write after migrating a thread to `DeltaChannel`
must still be present when the state is read back from the checkpointer.
Regression for #8384: `invoke` returned the correct value while
`get_state` dropped the write permanently.
"""
checkpointer = InMemorySaver()
config = {"configurable": {"thread_id": "first-post-migration"}}
binop = _binop_graph(checkpointer)
binop.invoke({"items": ["a"]}, config)
delta = _delta_graph(checkpointer)
live = delta.invoke({"items": ["b"]}, config)
assert list(live["items"]) == ["a", "b"], "live invoke lost the write"
cold = delta.get_state(config)
assert list(cold.values["items"]) == ["a", "b"], (
"first post-migration write dropped on cold read: "
f"got {list(cold.values['items'])}"
)
async def test_first_post_migration_write_survives_cold_read_async() -> None:
"""Async variant of the #8384 regression."""
checkpointer = InMemorySaver()
config = {"configurable": {"thread_id": "first-post-migration-async"}}
binop = _binop_graph(checkpointer)
await binop.ainvoke({"items": ["a"]}, config)
delta = _delta_graph(checkpointer)
live = await delta.ainvoke({"items": ["b"]}, config)
assert list(live["items"]) == ["a", "b"], "live ainvoke lost the write"
cold = await delta.aget_state(config)
assert list(cold.values["items"]) == ["a", "b"], (
"first post-migration write dropped on cold read: "
f"got {list(cold.values['items'])}"
)
def test_post_migration_writes_match_base_saver_fallback() -> None:
"""Parity across the migration boundary WITH post-migration writes.
`test_base_saver_fallback_matches_optimized_override` only reads a
pre-migration chain, so the optimized override and the reference walk
never disagree there. Driving writes after the migration is what
separates them.
"""
def _run(saver: Any, thread: str) -> list[tuple[Any, list]]:
config = {"configurable": {"thread_id": thread}}
_drive(_binop_graph(saver), config, "u", 2)
delta = _delta_graph(saver)
_drive(delta, config, "d", 3)
return [
(s.next, list(s.values.get("items", [])))
for s in delta.get_state_history(config)
]
fast = _run(InMemorySaver(), "fast")
slow = _run(_ThirdPartyStyleSaver(), "slow")
assert fast == slow, (
"optimized override diverges from the base-saver fallback once "
f"post-migration writes exist; fast={fast}, slow={slow}"
)
# Guard the assertion above against both paths being wrong in the same way.
assert fast[0][1] == ["u0", "u1", "d0", "d1", "d2"], (
f"unexpected accumulated state: {fast[0][1]}"
)
def test_add_messages_migration_keeps_first_post_migration_message() -> None:
"""The `add_messages` -> `DeltaChannel` path is the one Deep Agents takes;
dropping the first post-migration write loses a real user message.
"""
checkpointer = InMemorySaver()
config = {"configurable": {"thread_id": "add-messages-first-write"}}
pre_graph = _add_messages_graph(checkpointer)
pre_graph.invoke({"messages": [HumanMessage(content="hello", id="h1")]}, config)
delta_graph = _delta_messages_graph(checkpointer)
delta_graph.invoke({"messages": [HumanMessage(content="second", id="h2")]}, config)
ids = [m.id for m in delta_graph.get_state(config).values["messages"]]
# h1 is the pre-migration seed, h2 the write that was being dropped; both
# have to survive, and in order.
assert ids == ["h1", "h2"], f"expected ['h1', 'h2'], got {ids}"
@@ -27,7 +27,6 @@ from typing_extensions import TypedDict
from langgraph.channels.delta import DeltaChannel from langgraph.channels.delta import DeltaChannel
from langgraph.graph import START, StateGraph from langgraph.graph import START, StateGraph
from langgraph.graph.message import _messages_delta_reducer from langgraph.graph.message import _messages_delta_reducer
from langgraph.types import StateUpdate
pytestmark = pytest.mark.anyio pytestmark = pytest.mark.anyio
@@ -278,6 +277,7 @@ def test_bulk_update_state_multi_task_per_superstep_delta_channel() -> None:
different `StateUpdate`s targeting the same node otherwise both share different `StateUpdate`s targeting the same node otherwise both share
the deterministic interrupt-derived id and collide in the saver. the deterministic interrupt-derived id and collide in the saver.
""" """
from langgraph.types import StateUpdate
saver = InMemorySaver() saver = InMemorySaver()
graph = _build_graph(saver) graph = _build_graph(saver)
+4 -4
View File
@@ -88,13 +88,13 @@ def test_constants_deprecation() -> None:
LangGraphDeprecatedSinceV10, LangGraphDeprecatedSinceV10,
match="Importing Send from langgraph.constants is deprecated. Please use 'from langgraph.types import Send' instead.", match="Importing Send from langgraph.constants is deprecated. Please use 'from langgraph.types import Send' instead.",
): ):
from langgraph.constants import Send # noqa: PLC0415, F401 from langgraph.constants import Send # noqa: F401
with pytest.warns( with pytest.warns(
LangGraphDeprecatedSinceV10, LangGraphDeprecatedSinceV10,
match="Importing Interrupt from langgraph.constants is deprecated. Please use 'from langgraph.types import Interrupt' instead.", match="Importing Interrupt from langgraph.constants is deprecated. Please use 'from langgraph.types import Interrupt' instead.",
): ):
from langgraph.constants import Interrupt # noqa: PLC0415, F401 from langgraph.constants import Interrupt # noqa: F401
def test_pregel_types_deprecation() -> None: def test_pregel_types_deprecation() -> None:
@@ -102,7 +102,7 @@ def test_pregel_types_deprecation() -> None:
LangGraphDeprecatedSinceV10, LangGraphDeprecatedSinceV10,
match="Importing from langgraph.pregel.types is deprecated. Please use 'from langgraph.types import ...' instead.", match="Importing from langgraph.pregel.types is deprecated. Please use 'from langgraph.types import ...' instead.",
): ):
from langgraph.pregel.types import StateSnapshot # noqa: PLC0415, F401 from langgraph.pregel.types import StateSnapshot # noqa: F401
def test_config_schema_deprecation() -> None: def test_config_schema_deprecation() -> None:
@@ -195,7 +195,7 @@ def test_deprecated_import() -> None:
LangGraphDeprecatedSinceV10, LangGraphDeprecatedSinceV10,
match="Importing PREVIOUS from langgraph.constants is deprecated. This constant is now private and should not be used directly.", match="Importing PREVIOUS from langgraph.constants is deprecated. This constant is now private and should not be used directly.",
): ):
from langgraph.constants import PREVIOUS # noqa: PLC0415, F401 from langgraph.constants import PREVIOUS # noqa: F401
@pytest.mark.filterwarnings( @pytest.mark.filterwarnings(
+1 -1
View File
@@ -13,7 +13,6 @@ from langgraph.callbacks import (
GraphCallbackHandler, GraphCallbackHandler,
GraphInterruptEvent, GraphInterruptEvent,
GraphResumeEvent, GraphResumeEvent,
_GraphCallbackManager,
) )
from langgraph.graph import START, StateGraph from langgraph.graph import START, StateGraph
from langgraph.types import Command, Interrupt, interrupt from langgraph.types import Command, Interrupt, interrupt
@@ -287,6 +286,7 @@ def test_non_graph_handler_via_add_handler_does_not_crash() -> None:
GraphCallbackHandler. They must be silently accepted graph lifecycle GraphCallbackHandler. They must be silently accepted graph lifecycle
events will simply not be dispatched to them. events will simply not be dispatched to them.
""" """
from langgraph.callbacks import _GraphCallbackManager
manager = _GraphCallbackManager() manager = _GraphCallbackManager()
plain_handler = _LangChainCustomEventHandler() plain_handler = _LangChainCustomEventHandler()
+45 -16
View File
@@ -2,26 +2,11 @@ import json
import operator import operator
import re import re
import time import time
from copy import deepcopy
from dataclasses import replace from dataclasses import replace
from typing import Annotated, Any, Literal, cast from typing import Annotated, Any, Literal, cast
import pytest import pytest
from langchain_core.callbacks import CallbackManagerForLLMRun from langchain_core.messages import AIMessage, AnyMessage, ToolCall
from langchain_core.language_models.fake import FakeStreamingListLLM
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import (
AIMessage,
AnyMessage,
BaseMessage,
HumanMessage,
ToolCall,
ToolMessage,
)
from langchain_core.outputs import ChatGeneration, ChatResult
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnableConfig, RunnableMap, RunnablePick from langchain_core.runnables import RunnableConfig, RunnableMap, RunnablePick
from langchain_core.tools import tool from langchain_core.tools import tool
from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION
@@ -499,6 +484,9 @@ def test_conditional_state_graph(
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langchain_core.language_models.fake import FakeStreamingListLLM
from langchain_core.prompts import PromptTemplate
from langchain_core.tools import tool
class AgentState(TypedDict, total=False): class AgentState(TypedDict, total=False):
input: Annotated[str, UntrackedValue] input: Annotated[str, UntrackedValue]
@@ -1273,6 +1261,8 @@ def test_conditional_state_graph(
def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.tools import tool
@tool() @tool()
def search_api(query: str) -> str: def search_api(query: str) -> str:
@@ -1636,6 +1626,17 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
def test_state_graph_packets( def test_state_graph_packets(
sync_checkpointer: BaseCheckpointSaver, mocker: MockerFixture sync_checkpointer: BaseCheckpointSaver, mocker: MockerFixture
) -> None: ) -> None:
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import (
AIMessage,
BaseMessage,
HumanMessage,
ToolCall,
ToolMessage,
)
from langchain_core.tools import tool
class AgentState(TypedDict): class AgentState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages] messages: Annotated[list[BaseMessage], add_messages]
@@ -2380,6 +2381,15 @@ def test_message_graph(
deterministic_uuids: MockerFixture, deterministic_uuids: MockerFixture,
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from copy import deepcopy
from langchain_core.callbacks import CallbackManagerForLLMRun
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
from langchain_core.outputs import ChatGeneration, ChatResult
from langchain_core.tools import tool
class FakeFunctionChatModel(FakeMessagesListChatModel): class FakeFunctionChatModel(FakeMessagesListChatModel):
def bind_functions(self, functions: list): def bind_functions(self, functions: list):
@@ -3089,6 +3099,20 @@ def test_root_graph(
deterministic_uuids: MockerFixture, deterministic_uuids: MockerFixture,
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from copy import deepcopy
from langchain_core.callbacks import CallbackManagerForLLMRun
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import (
AIMessage,
BaseMessage,
HumanMessage,
ToolMessage,
)
from langchain_core.outputs import ChatGeneration, ChatResult
from langchain_core.tools import tool
class FakeFunctionChatModel(FakeMessagesListChatModel): class FakeFunctionChatModel(FakeMessagesListChatModel):
def bind_functions(self, functions: list): def bind_functions(self, functions: list):
@@ -5813,6 +5837,7 @@ def test_send_to_nested_graphs(sync_checkpointer: BaseCheckpointSaver) -> None:
def test_send_react_interrupt( def test_send_react_interrupt(
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langchain_core.messages import AIMessage, HumanMessage, ToolCall, ToolMessage
ai_message = AIMessage( ai_message = AIMessage(
"", "",
@@ -6203,6 +6228,7 @@ def test_send_react_interrupt(
def test_send_react_interrupt_control( def test_send_react_interrupt_control(
sync_checkpointer: BaseCheckpointSaver, snapshot: SnapshotAssertion sync_checkpointer: BaseCheckpointSaver, snapshot: SnapshotAssertion
) -> None: ) -> None:
from langchain_core.messages import AIMessage, HumanMessage, ToolCall, ToolMessage
ai_message = AIMessage( ai_message = AIMessage(
"", "",
@@ -6429,6 +6455,9 @@ def test_send_react_interrupt_control(
def test_weather_subgraph( def test_weather_subgraph(
sync_checkpointer: BaseCheckpointSaver, snapshot: SnapshotAssertion sync_checkpointer: BaseCheckpointSaver, snapshot: SnapshotAssertion
) -> None: ) -> None:
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
# setup subgraph # setup subgraph
+29 -16
View File
@@ -9,22 +9,8 @@ from typing import (
) )
import pytest import pytest
from langchain_core.agents import AgentAction, AgentFinish from langchain_core.messages import AnyMessage, ToolCall
from langchain_core.language_models.fake import FakeStreamingListLLM
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import (
AIMessage,
AnyMessage,
BaseMessage,
HumanMessage,
ToolCall,
ToolMessage,
)
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnableConfig, RunnablePick from langchain_core.runnables import RunnableConfig, RunnablePick
from langchain_core.tools import tool
from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION
from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.prebuilt.chat_agent_executor import create_react_agent from langgraph.prebuilt.chat_agent_executor import create_react_agent
@@ -36,7 +22,6 @@ from langgraph._internal._constants import PULL, PUSH
from langgraph.channels.last_value import LastValue from langgraph.channels.last_value import LastValue
from langgraph.channels.untracked_value import UntrackedValue from langgraph.channels.untracked_value import UntrackedValue
from langgraph.constants import END, START from langgraph.constants import END, START
from langgraph.graph import MessagesState
from langgraph.graph.message import add_messages from langgraph.graph.message import add_messages
from langgraph.graph.state import StateGraph from langgraph.graph.state import StateGraph
from langgraph.pregel import NodeBuilder, Pregel from langgraph.pregel import NodeBuilder, Pregel
@@ -494,6 +479,10 @@ async def test_fork_always_re_runs_nodes(
async def test_conditional_graph_state(async_checkpointer: BaseCheckpointSaver) -> None: async def test_conditional_graph_state(async_checkpointer: BaseCheckpointSaver) -> None:
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.language_models.fake import FakeStreamingListLLM
from langchain_core.prompts import PromptTemplate
from langchain_core.tools import tool
class AgentState(TypedDict): class AgentState(TypedDict):
input: Annotated[str, UntrackedValue] input: Annotated[str, UntrackedValue]
@@ -1028,6 +1017,8 @@ async def test_conditional_graph_state(async_checkpointer: BaseCheckpointSaver)
async def test_prebuilt_tool_chat() -> None: async def test_prebuilt_tool_chat() -> None:
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.tools import tool
model = FakeChatModel( model = FakeChatModel(
messages=[ messages=[
@@ -1367,6 +1358,16 @@ async def test_prebuilt_tool_chat() -> None:
async def test_state_graph_packets(async_checkpointer: BaseCheckpointSaver) -> None: async def test_state_graph_packets(async_checkpointer: BaseCheckpointSaver) -> None:
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import (
AIMessage,
BaseMessage,
HumanMessage,
ToolMessage,
)
from langchain_core.tools import tool
class AgentState(TypedDict): class AgentState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages] messages: Annotated[list[BaseMessage], add_messages]
@@ -2071,6 +2072,11 @@ async def test_state_graph_packets(async_checkpointer: BaseCheckpointSaver) -> N
async def test_message_graph(async_checkpointer: BaseCheckpointSaver) -> None: async def test_message_graph(async_checkpointer: BaseCheckpointSaver) -> None:
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.tools import tool
class FakeFunctionChatModel(FakeMessagesListChatModel): class FakeFunctionChatModel(FakeMessagesListChatModel):
def bind_functions(self, functions: list): def bind_functions(self, functions: list):
@@ -3531,6 +3537,13 @@ async def test_send_to_nested_graphs(async_checkpointer: BaseCheckpointSaver) ->
async def test_weather_subgraph( async def test_weather_subgraph(
async_checkpointer: BaseCheckpointSaver, async_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import AIMessage, ToolCall
from langchain_core.tools import tool
from langgraph.graph import MessagesState
# setup subgraph # setup subgraph
+27 -18
View File
@@ -4,39 +4,25 @@ import gc
import json import json
import logging import logging
import operator import operator
import random
import threading import threading
import time import time
import uuid import uuid
from collections import Counter, defaultdict, deque from collections import Counter, deque
from collections.abc import Sequence from collections.abc import Sequence
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field from dataclasses import dataclass, field
from random import randrange from random import randrange
from typing import Annotated, Any, Literal, get_type_hints from typing import Annotated, Any, Literal, get_type_hints
from unittest.mock import patch
import pytest import pytest
from langchain_core.language_models import GenericFakeChatModel from langchain_core.language_models import GenericFakeChatModel
from langchain_core.language_models.fake import FakeStreamingListLLM from langchain_core.messages import AIMessage, AnyMessage, HumanMessage, RemoveMessage
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import (
AIMessage,
AnyMessage,
BaseMessage,
HumanMessage,
RemoveMessage,
)
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.runnables import ( from langchain_core.runnables import (
RunnableConfig, RunnableConfig,
RunnableLambda, RunnableLambda,
RunnablePassthrough, RunnablePassthrough,
) )
from langchain_core.runnables.graph import Edge from langchain_core.runnables.graph import Edge
from langchain_core.tools import tool
from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION
from langgraph.cache.base import BaseCache from langgraph.cache.base import BaseCache
from langgraph.checkpoint.base import ( from langgraph.checkpoint.base import (
@@ -70,9 +56,8 @@ from langgraph.pregel import (
NodeBuilder, NodeBuilder,
Pregel, Pregel,
) )
from langgraph.pregel._loop import PregelLoop, SyncPregelLoop from langgraph.pregel._loop import SyncPregelLoop
from langgraph.pregel._runner import PregelRunner from langgraph.pregel._runner import PregelRunner
from langgraph.runtime import RunControl
from langgraph.types import ( from langgraph.types import (
CachePolicy, CachePolicy,
Command, Command,
@@ -140,6 +125,7 @@ def test_graph_validation() -> None:
def test_request_drain_allows_inflight_call_scheduling( def test_request_drain_allows_inflight_call_scheduling(
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langgraph.runtime import RunControl
@task @task
def child(x: int) -> int: def child(x: int) -> int:
@@ -1783,6 +1769,9 @@ def test_conditional_state_graph_with_list_edge_inputs(snapshot: SnapshotAsserti
def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion) -> None: def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion) -> None:
from langchain_core.language_models.fake import FakeStreamingListLLM
from langchain_core.prompts import PromptTemplate
from langchain_core.tools import tool
class BaseState(TypedDict): class BaseState(TypedDict):
input: str input: str
@@ -3780,6 +3769,12 @@ def test_checkpoint_metadata(sync_checkpointer: BaseCheckpointSaver) -> None:
previous checkpoint config for each step in the run. previous checkpoint config for each step in the run.
""" """
# set up test # set up test
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import AIMessage, AnyMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
# graph state # graph state
class BaseState(TypedDict): class BaseState(TypedDict):
@@ -3945,6 +3940,7 @@ def test_checkpoint_metadata(sync_checkpointer: BaseCheckpointSaver) -> None:
def test_remove_message_via_state_update( def test_remove_message_via_state_update(
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage
workflow = StateGraph(state_schema=Annotated[list[AnyMessage], add_messages]) # type: ignore[arg-type] workflow = StateGraph(state_schema=Annotated[list[AnyMessage], add_messages]) # type: ignore[arg-type]
workflow.add_node( workflow.add_node(
@@ -3977,6 +3973,7 @@ def test_remove_message_via_state_update(
def test_remove_message_from_node(): def test_remove_message_from_node():
from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage
workflow = StateGraph(state_schema=Annotated[list[AnyMessage], add_messages]) # type: ignore[arg-type] workflow = StateGraph(state_schema=Annotated[list[AnyMessage], add_messages]) # type: ignore[arg-type]
workflow.add_node( workflow.add_node(
@@ -4002,6 +3999,7 @@ def test_remove_message_from_node():
def test_xray_lance(snapshot: SnapshotAssertion): def test_xray_lance(snapshot: SnapshotAssertion):
from langchain_core.messages import AnyMessage, HumanMessage
class Analyst(BaseModel): class Analyst(BaseModel):
affiliation: str = Field( affiliation: str = Field(
@@ -4485,6 +4483,7 @@ def test_debug_subgraphs(
def test_debug_nested_subgraphs( def test_debug_nested_subgraphs(
sync_checkpointer: BaseCheckpointSaver, durability: Durability sync_checkpointer: BaseCheckpointSaver, durability: Durability
): ):
from collections import defaultdict
class State(TypedDict): class State(TypedDict):
messages: Annotated[list[str], operator.add] messages: Annotated[list[str], operator.add]
@@ -4744,6 +4743,8 @@ def test_runnable_passthrough_node_graph() -> None:
def test_parent_command( def test_parent_command(
sync_checkpointer: BaseCheckpointSaver, subgraph_persist: bool sync_checkpointer: BaseCheckpointSaver, subgraph_persist: bool
) -> None: ) -> None:
from langchain_core.messages import BaseMessage
from langchain_core.tools import tool
@tool(return_direct=True) @tool(return_direct=True)
def get_user_name() -> Command: def get_user_name() -> Command:
@@ -5163,6 +5164,7 @@ def test_command_with_static_breakpoints(
def test_multistep_plan(sync_checkpointer: BaseCheckpointSaver): def test_multistep_plan(sync_checkpointer: BaseCheckpointSaver):
from langchain_core.messages import AnyMessage
class State(TypedDict, total=False): class State(TypedDict, total=False):
plan: list[str | list[str]] plan: list[str | list[str]]
@@ -5908,6 +5910,9 @@ def test_no_redundant_put_writes_for_cached_task(
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
"""Cached @tasks on resume must not trigger redundant put_writes.""" """Cached @tasks on resume must not trigger redundant put_writes."""
from unittest.mock import patch
from langgraph.pregel._loop import PregelLoop
@task @task
def setup(x: int) -> int: def setup(x: int) -> int:
@@ -6970,6 +6975,7 @@ def test_configurable_propagates_to_stream_metadata() -> None:
def test_stream_mode_messages_command() -> None: def test_stream_mode_messages_command() -> None:
from langchain_core.messages import HumanMessage
def my_node(state): def my_node(state):
return {"messages": HumanMessage(content="foo")} return {"messages": HumanMessage(content="foo")}
@@ -7237,6 +7243,7 @@ def test_get_stream_writer() -> None:
def test_stream_messages_dedupe_inputs() -> None: def test_stream_messages_dedupe_inputs() -> None:
from langchain_core.messages import AIMessage
def call_model(state): def call_model(state):
return {"messages": AIMessage("hi", id="1")} return {"messages": AIMessage("hi", id="1")}
@@ -7274,6 +7281,7 @@ def test_stream_messages_dedupe_inputs() -> None:
def test_stream_messages_dedupe_state(sync_checkpointer: BaseCheckpointSaver) -> None: def test_stream_messages_dedupe_state(sync_checkpointer: BaseCheckpointSaver) -> None:
from langchain_core.messages import AIMessage
to_emit = [AIMessage("bye", id="1"), AIMessage("bye again", id="2")] to_emit = [AIMessage("bye", id="1"), AIMessage("bye again", id="2")]
@@ -8245,6 +8253,7 @@ def test_get_graph_loop(snapshot: SnapshotAssertion) -> None:
def test_get_graph_self_loop(snapshot: SnapshotAssertion) -> None: def test_get_graph_self_loop(snapshot: SnapshotAssertion) -> None:
import random
subgraph_builder = StateGraph(MessagesState) subgraph_builder = StateGraph(MessagesState)
subgraph_builder.add_node("agent", lambda x: x) subgraph_builder.add_node("agent", lambda x: x)
+29 -18
View File
@@ -7,7 +7,7 @@ import operator
import random import random
import sys import sys
import uuid import uuid
from collections import Counter, defaultdict, deque from collections import Counter, deque
from dataclasses import replace from dataclasses import replace
from time import perf_counter from time import perf_counter
from typing import ( from typing import (
@@ -21,20 +21,8 @@ from uuid import UUID
import pytest import pytest
from langchain_core.language_models import GenericFakeChatModel from langchain_core.language_models import GenericFakeChatModel
from langchain_core.language_models.fake_chat_models import ( from langchain_core.messages import HumanMessage
FakeMessagesListChatModel,
)
from langchain_core.messages import (
AIMessage,
AnyMessage,
BaseMessage,
HumanMessage,
ToolCall,
ToolMessage,
)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig, RunnableLambda, RunnablePassthrough from langchain_core.runnables import RunnableConfig, RunnableLambda, RunnablePassthrough
from langchain_core.tools import tool
from langchain_core.utils.aiter import aclosing from langchain_core.utils.aiter import aclosing
from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION from langchain_core.version import VERSION as LANGCHAIN_CORE_VERSION
from langgraph.cache.base import BaseCache from langgraph.cache.base import BaseCache
@@ -57,7 +45,6 @@ from typing_extensions import NotRequired, TypedDict
from langgraph._internal._constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL from langgraph._internal._constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL
from langgraph._internal._queue import AsyncQueue from langgraph._internal._queue import AsyncQueue
from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.binop import BinaryOperatorAggregate
from langgraph.channels.delta import DeltaChannel
from langgraph.channels.last_value import LastValue from langgraph.channels.last_value import LastValue
from langgraph.channels.topic import Topic from langgraph.channels.topic import Topic
from langgraph.errors import ( from langgraph.errors import (
@@ -68,11 +55,10 @@ from langgraph.errors import (
) )
from langgraph.func import entrypoint, task from langgraph.func import entrypoint, task
from langgraph.graph import END, START, StateGraph from langgraph.graph import END, START, StateGraph
from langgraph.graph.message import MessagesState, _messages_delta_reducer, add_messages from langgraph.graph.message import MessagesState, add_messages
from langgraph.pregel import NodeBuilder, Pregel from langgraph.pregel import NodeBuilder, Pregel
from langgraph.pregel._loop import AsyncPregelLoop, PregelLoop from langgraph.pregel._loop import AsyncPregelLoop
from langgraph.pregel._runner import PregelRunner from langgraph.pregel._runner import PregelRunner
from langgraph.runtime import RunControl
from langgraph.types import ( from langgraph.types import (
CachePolicy, CachePolicy,
Command, Command,
@@ -236,6 +222,7 @@ async def test_checkpoint_errors() -> None:
async def test_request_drain_allows_inflight_acall_scheduling( async def test_request_drain_allows_inflight_acall_scheduling(
async_checkpointer: BaseCheckpointSaver, async_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langgraph.runtime import RunControl
@task @task
async def child(x: int) -> int: async def child(x: int) -> int:
@@ -2881,6 +2868,7 @@ async def test_send_dedupe_on_resume(
async def test_send_react_interrupt(async_checkpointer: BaseCheckpointSaver) -> None: async def test_send_react_interrupt(async_checkpointer: BaseCheckpointSaver) -> None:
from langchain_core.messages import AIMessage, HumanMessage, ToolCall, ToolMessage
ai_message = AIMessage( ai_message = AIMessage(
"", "",
@@ -3271,6 +3259,7 @@ async def test_send_react_interrupt(async_checkpointer: BaseCheckpointSaver) ->
async def test_send_react_interrupt_control( async def test_send_react_interrupt_control(
async_checkpointer: BaseCheckpointSaver, snapshot: SnapshotAssertion async_checkpointer: BaseCheckpointSaver, snapshot: SnapshotAssertion
) -> None: ) -> None:
from langchain_core.messages import AIMessage, HumanMessage, ToolCall, ToolMessage
ai_message = AIMessage( ai_message = AIMessage(
"", "",
@@ -5549,6 +5538,12 @@ async def test_checkpoint_metadata(async_checkpointer: BaseCheckpointSaver) -> N
previous checkpoint config for each step in the run. previous checkpoint config for each step in the run.
""" """
# set up test # set up test
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import AIMessage, AnyMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
# graph state # graph state
class BaseState(TypedDict): class BaseState(TypedDict):
@@ -5949,6 +5944,7 @@ async def test_debug_subgraphs(
async def test_debug_nested_subgraphs( async def test_debug_nested_subgraphs(
async_checkpointer: BaseCheckpointSaver, durability: Durability async_checkpointer: BaseCheckpointSaver, durability: Durability
) -> None: ) -> None:
from collections import defaultdict
class State(TypedDict): class State(TypedDict):
messages: Annotated[list[str], operator.add] messages: Annotated[list[str], operator.add]
@@ -6065,6 +6061,8 @@ async def test_debug_nested_subgraphs(
async def test_parent_command( async def test_parent_command(
async_checkpointer: BaseCheckpointSaver, subgraph_persist: bool async_checkpointer: BaseCheckpointSaver, subgraph_persist: bool
) -> None: ) -> None:
from langchain_core.messages import BaseMessage
from langchain_core.tools import tool
@tool(return_direct=True) @tool(return_direct=True)
def get_user_name() -> Command: def get_user_name() -> Command:
@@ -6132,6 +6130,10 @@ async def test_parent_command(
async def test_delta_channel_durability_exit_stores_snapshot_async() -> None: async def test_delta_channel_durability_exit_stores_snapshot_async() -> None:
"""DeltaChannel must reload from an async durability='exit' checkpoint.""" """DeltaChannel must reload from an async durability='exit' checkpoint."""
from langchain_core.messages import AIMessage
from langgraph.channels.delta import DeltaChannel
from langgraph.graph.message import _messages_delta_reducer
class State(TypedDict): class State(TypedDict):
messages: Annotated[list, DeltaChannel(_messages_delta_reducer)] messages: Annotated[list, DeltaChannel(_messages_delta_reducer)]
@@ -6418,6 +6420,7 @@ async def test_command_with_static_breakpoints(
async def test_multistep_plan(async_checkpointer: BaseCheckpointSaver) -> None: async def test_multistep_plan(async_checkpointer: BaseCheckpointSaver) -> None:
from langchain_core.messages import AnyMessage
class State(TypedDict, total=False): class State(TypedDict, total=False):
plan: list[str | list[str]] plan: list[str | list[str]]
@@ -6755,6 +6758,7 @@ async def test_multiple_interrupts_functional(
async_checkpointer: BaseCheckpointSaver, async_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
"""Test multiple interrupts with functional API.""" """Test multiple interrupts with functional API."""
from langgraph.func import entrypoint, task
counter = 0 counter = 0
@@ -7670,6 +7674,7 @@ async def test_configurable_propagates_to_stream_metadata() -> None:
async def test_stream_mode_messages_command() -> None: async def test_stream_mode_messages_command() -> None:
from langchain_core.messages import HumanMessage
async def my_node(state): async def my_node(state):
return {"messages": HumanMessage(content="foo")} return {"messages": HumanMessage(content="foo")}
@@ -7718,6 +7723,7 @@ async def test_stream_mode_messages_command() -> None:
async def test_stream_messages_dedupe_inputs() -> None: async def test_stream_messages_dedupe_inputs() -> None:
from langchain_core.messages import AIMessage
async def call_model(state): async def call_model(state):
return {"messages": AIMessage("hi", id="1")} return {"messages": AIMessage("hi", id="1")}
@@ -7757,6 +7763,7 @@ async def test_stream_messages_dedupe_inputs() -> None:
async def test_stream_messages_dedupe_state( async def test_stream_messages_dedupe_state(
async_checkpointer: BaseCheckpointSaver, async_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langchain_core.messages import AIMessage
to_emit = [AIMessage("bye", id="1"), AIMessage("bye again", id="2")] to_emit = [AIMessage("bye", id="1"), AIMessage("bye again", id="2")]
@@ -8135,6 +8142,9 @@ async def test_no_redundant_put_writes_for_cached_task(
async_checkpointer: BaseCheckpointSaver, async_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
"""Cached @tasks on resume must not trigger redundant put_writes.""" """Cached @tasks on resume must not trigger redundant put_writes."""
from unittest.mock import patch
from langgraph.pregel._loop import PregelLoop
@task @task
async def setup(x: int) -> int: async def setup(x: int) -> int:
@@ -8636,6 +8646,7 @@ async def test_batch_update_as_input(
async def test_draw_invalid(): async def test_draw_invalid():
from langchain_core.messages import BaseMessage
class AgentState(TypedDict): class AgentState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages] messages: Annotated[list[BaseMessage], add_messages]
+4 -3
View File
@@ -4,13 +4,10 @@ import ipaddress
import pathlib import pathlib
import re import re
import sys import sys
import typing
import uuid import uuid
from enum import Enum from enum import Enum
from typing import Annotated, Literal, Optional from typing import Annotated, Literal, Optional
import pydantic
import typing_extensions
from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.checkpoint.base import BaseCheckpointSaver
from pydantic import ( from pydantic import (
BaseModel, BaseModel,
@@ -35,6 +32,10 @@ from tests.any_str import AnyStr
def test_is_supported_by_pydantic() -> None: def test_is_supported_by_pydantic() -> None:
"""Test if types are supported by pydantic.""" """Test if types are supported by pydantic."""
import typing
import pydantic
import typing_extensions
class TypedDictExtensions(typing_extensions.TypedDict): class TypedDictExtensions(typing_extensions.TypedDict):
x: int x: int
+5 -3
View File
@@ -10,14 +10,12 @@ from langchain_core.messages import AnyMessage, BaseMessage
from langchain_core.runnables import RunnableConfig from langchain_core.runnables import RunnableConfig
from langchain_core.runnables.graph import Edge as DrawableEdge from langchain_core.runnables.graph import Edge as DrawableEdge
from langchain_core.runnables.graph import Node as DrawableNode from langchain_core.runnables.graph import Node as DrawableNode
from langgraph.checkpoint.memory import InMemorySaver
from langgraph_sdk.client import get_client, get_sync_client
from langgraph_sdk.schema import StreamPart from langgraph_sdk.schema import StreamPart
from pydantic import BaseModel from pydantic import BaseModel
from typing_extensions import TypedDict from typing_extensions import TypedDict
from langgraph.errors import GraphInterrupt from langgraph.errors import GraphInterrupt
from langgraph.graph import END, START, MessagesState, StateGraph, add_messages from langgraph.graph import StateGraph, add_messages
from langgraph.pregel import Pregel from langgraph.pregel import Pregel
from langgraph.pregel.remote import RemoteGraph from langgraph.pregel.remote import RemoteGraph
from langgraph.types import Interrupt, StateSnapshot from langgraph.types import Interrupt, StateSnapshot
@@ -1099,6 +1097,10 @@ def test_stream_context_base_model():
) )
@pytest.mark.anyio @pytest.mark.anyio
async def test_langgraph_cloud_integration(): async def test_langgraph_cloud_integration():
from langgraph.checkpoint.memory import InMemorySaver
from langgraph_sdk.client import get_client, get_sync_client
from langgraph.graph import END, START, MessagesState, StateGraph
# create RemotePregel instance # create RemotePregel instance
client = get_client(url="http://localhost:8123") client = get_client(url="http://localhost:8123")
+1 -1
View File
@@ -4,7 +4,6 @@ from unittest.mock import AsyncMock, MagicMock
import pytest import pytest
from langgraph.pregel import remote as remote_mod
from langgraph.pregel._remote_run_stream import ( from langgraph.pregel._remote_run_stream import (
_AsyncRemoteGraphRunStream, _AsyncRemoteGraphRunStream,
_ChannelProjection, _ChannelProjection,
@@ -578,6 +577,7 @@ def test_stream_events_v3_strips_checkpoint_keys_from_configurable():
def test_stream_events_v3_merges_tracing_headers_when_distributed_tracing( def test_stream_events_v3_merges_tracing_headers_when_distributed_tracing(
monkeypatch, monkeypatch,
): ):
from langgraph.pregel import remote as remote_mod
sync_client = MagicMock() sync_client = MagicMock()
sync_client.threads.stream.return_value = MagicMock() sync_client.threads.stream.return_value = MagicMock()
+5 -3
View File
@@ -11,9 +11,7 @@ from typing import Annotated, Any
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from uuid import uuid4 from uuid import uuid4
import httpx
import pytest import pytest
import requests
from langchain_core.callbacks import AsyncCallbackManagerForLLMRun, BaseCallbackHandler from langchain_core.callbacks import AsyncCallbackManagerForLLMRun, BaseCallbackHandler
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage, HumanMessage from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage, HumanMessage
@@ -65,7 +63,6 @@ from langgraph.types import (
RetryPolicy, RetryPolicy,
Send, Send,
TimeoutPolicy, TimeoutPolicy,
interrupt,
) )
NEEDS_CONTEXTVARS = pytest.mark.skipif( NEEDS_CONTEXTVARS = pytest.mark.skipif(
@@ -174,6 +171,8 @@ def test_checkpoint_ns_for_parent_command() -> None:
def test_should_retry_default_retry_on(): def test_should_retry_default_retry_on():
"""Test the default retry_on function.""" """Test the default retry_on function."""
import httpx
import requests
# Create a RetryPolicy with default_retry_on # Create a RetryPolicy with default_retry_on
policy = RetryPolicy() policy = RetryPolicy()
@@ -2199,6 +2198,7 @@ def test_graph_error_handler_does_not_swallow_interrupt_concurrent():
"""When a graph error handler is configured and a node calls interrupt() """When a graph error handler is configured and a node calls interrupt()
concurrently with other nodes, the interrupt must still be raised not concurrently with other nodes, the interrupt must still be raised not
silently swallowed.""" silently swallowed."""
from langgraph.types import interrupt
class State(TypedDict): class State(TypedDict):
foo: str foo: str
@@ -2587,6 +2587,8 @@ async def test_set_node_defaults_timeout():
.compile() .compile()
) )
from langgraph.errors import NodeTimeoutError
with pytest.raises(NodeTimeoutError): with pytest.raises(NodeTimeoutError):
await graph.ainvoke({"foo": ""}) await graph.ainvoke({"foo": ""})
+3 -2
View File
@@ -6,11 +6,9 @@ from typing import Any
import pytest import pytest
from langgraph.checkpoint.memory import MemorySaver from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
from typing_extensions import TypedDict from typing_extensions import TypedDict
from langgraph._internal._constants import CONFIG_KEY_RUNTIME
from langgraph.errors import GraphDrained from langgraph.errors import GraphDrained
from langgraph.graph import END, START, StateGraph from langgraph.graph import END, START, StateGraph
from langgraph.runtime import ( from langgraph.runtime import (
@@ -1179,6 +1177,9 @@ def test_foreign_object_in_runtime_slot_is_coerced() -> None:
`merge` when no per-run `context` is provided. `store` is resolved `merge` when no per-run `context` is provided. `store` is resolved
separately, so it is not read off the foreign object in the coercion. separately, so it is not read off the foreign object in the coercion.
""" """
from langgraph.store.memory import InMemoryStore
from langgraph._internal._constants import CONFIG_KEY_RUNTIME
store = InMemoryStore() store = InMemoryStore()
graph_level_context = {"source": "graph-level"} graph_level_context = {"source": "graph-level"}

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