From c989f1c8980c4e7a6cd0c30431d6a362e53e1fd3 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:42:25 -0400 Subject: [PATCH 01/22] langgraph: remove support for `thread_ts` (old alias for `checkpoint_id`) (#5295) * remove support for thread_ts * docs and tests --- .../langgraph/checkpoint/postgres/__init__.py | 5 +---- .../langgraph/checkpoint/postgres/aio.py | 4 +--- libs/checkpoint-postgres/tests/test_async.py | 3 +-- libs/checkpoint-postgres/tests/test_sync.py | 3 +-- libs/checkpoint-sqlite/tests/test_aiosqlite.py | 3 +-- libs/checkpoint-sqlite/tests/test_sqlite.py | 2 +- libs/checkpoint/README.md | 2 +- libs/checkpoint/langgraph/checkpoint/base/__init__.py | 7 ++----- libs/checkpoint/tests/test_memory.py | 3 +-- 9 files changed, 10 insertions(+), 22 deletions(-) diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py index 691654e6b..e31e53d90 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/__init__.py @@ -284,10 +284,7 @@ class PostgresSaver(BasePostgresSaver): configurable = config["configurable"].copy() thread_id = configurable.pop("thread_id") checkpoint_ns = configurable.pop("checkpoint_ns") - checkpoint_id = configurable.pop( - "checkpoint_id", configurable.pop("thread_ts", None) - ) - + checkpoint_id = configurable.pop("checkpoint_id", None) copy = checkpoint.copy() next_config = { "configurable": { diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py index 9fc7673b2..6e9116c0a 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/aio.py @@ -240,9 +240,7 @@ class AsyncPostgresSaver(BasePostgresSaver): configurable = config["configurable"].copy() thread_id = configurable.pop("thread_id") checkpoint_ns = configurable.pop("checkpoint_ns") - checkpoint_id = configurable.pop( - "checkpoint_id", configurable.pop("thread_ts", None) - ) + checkpoint_id = configurable.pop("checkpoint_id", None) copy = checkpoint.copy() next_config = { diff --git a/libs/checkpoint-postgres/tests/test_async.py b/libs/checkpoint-postgres/tests/test_async.py index 9027307f6..fb3af3317 100644 --- a/libs/checkpoint-postgres/tests/test_async.py +++ b/libs/checkpoint-postgres/tests/test_async.py @@ -161,8 +161,7 @@ def test_data(): config_1: RunnableConfig = { "configurable": { "thread_id": "thread-1", - # for backwards compatibility testing - "thread_ts": "1", + "checkpoint_id": "1", "checkpoint_ns": "", } } diff --git a/libs/checkpoint-postgres/tests/test_sync.py b/libs/checkpoint-postgres/tests/test_sync.py index 3c212135d..e6d2720e4 100644 --- a/libs/checkpoint-postgres/tests/test_sync.py +++ b/libs/checkpoint-postgres/tests/test_sync.py @@ -143,8 +143,7 @@ def test_data(): config_1: RunnableConfig = { "configurable": { "thread_id": "thread-1", - # for backwards compatibility testing - "thread_ts": "1", + "checkpoint_id": "1", "checkpoint_ns": "", } } diff --git a/libs/checkpoint-sqlite/tests/test_aiosqlite.py b/libs/checkpoint-sqlite/tests/test_aiosqlite.py index 5a471aef4..02dedd31a 100644 --- a/libs/checkpoint-sqlite/tests/test_aiosqlite.py +++ b/libs/checkpoint-sqlite/tests/test_aiosqlite.py @@ -19,8 +19,7 @@ class TestAsyncSqliteSaver: self.config_1: RunnableConfig = { "configurable": { "thread_id": "thread-1", - # for backwards compatibility testing - "thread_ts": "1", + "checkpoint_id": "1", "checkpoint_ns": "", } } diff --git a/libs/checkpoint-sqlite/tests/test_sqlite.py b/libs/checkpoint-sqlite/tests/test_sqlite.py index b672c54d9..d2159a5ea 100644 --- a/libs/checkpoint-sqlite/tests/test_sqlite.py +++ b/libs/checkpoint-sqlite/tests/test_sqlite.py @@ -21,7 +21,7 @@ class TestSqliteSaver: "configurable": { "thread_id": "thread-1", # for backwards compatibility testing - "thread_ts": "1", + "checkpoint_id": "1", "checkpoint_ns": "", } } diff --git a/libs/checkpoint/README.md b/libs/checkpoint/README.md index 4877a17b9..d82cfcb1c 100644 --- a/libs/checkpoint/README.md +++ b/libs/checkpoint/README.md @@ -36,7 +36,7 @@ Each checkpointer should conform to `langgraph.checkpoint.base.BaseCheckpointSav - `.put` - Store a checkpoint with its configuration and metadata. - `.put_writes` - Store intermediate writes linked to a checkpoint (i.e. pending writes). -- `.get_tuple` - Fetch a checkpoint tuple using for a given configuration (`thread_id` and `thread_ts`). +- `.get_tuple` - Fetch a checkpoint tuple using for a given configuration (`thread_id` and `checkpoint_id`). - `.list` - List checkpoints that match a given configuration and filter criteria. If the checkpointer will be used with asynchronous graph execution (i.e. executing the graph via `.ainvoke`, `.astream`, `.abatch`), checkpointer must implement asynchronous versions of the above methods (`.aput`, `.aput_writes`, `.aget_tuple`, `.alist`). diff --git a/libs/checkpoint/langgraph/checkpoint/base/__init__.py b/libs/checkpoint/langgraph/checkpoint/base/__init__.py index 9719118d2..a5704e13f 100644 --- a/libs/checkpoint/langgraph/checkpoint/base/__init__.py +++ b/libs/checkpoint/langgraph/checkpoint/base/__init__.py @@ -375,10 +375,8 @@ class EmptyChannelError(Exception): def get_checkpoint_id(config: RunnableConfig) -> str | None: - """Get checkpoint ID in a backwards-compatible manner (fallback on thread_ts).""" - return config["configurable"].get( - "checkpoint_id", config["configurable"].get("thread_ts") - ) + """Get checkpoint ID.""" + return config["configurable"].get("checkpoint_id") def get_checkpoint_metadata( @@ -413,7 +411,6 @@ WRITES_IDX_MAP = {ERROR: -1, SCHEDULED: -2, INTERRUPT: -3, RESUME: -4} EXCLUDED_METADATA_KEYS = { "thread_id", - "thread_ts", "checkpoint_id", "checkpoint_ns", "checkpoint_map", diff --git a/libs/checkpoint/tests/test_memory.py b/libs/checkpoint/tests/test_memory.py index 4893a3d59..9c67457e6 100644 --- a/libs/checkpoint/tests/test_memory.py +++ b/libs/checkpoint/tests/test_memory.py @@ -22,8 +22,7 @@ class TestMemorySaver: "configurable": { "thread_id": "thread-1", "checkpoint_ns": "", - # for backwards compatibility testing - "thread_ts": "1", + "checkpoint_id": "1", } } self.config_2: RunnableConfig = { From 8c4e698c5aca386cd17143f55bf4af35f8191603 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:48:53 -0400 Subject: [PATCH 02/22] langgraph[change]: solidify public/private differentiations (#5252) * public interfaces for channels * public interfaces for func * public interfaces for graph * pi for managed * first pass public interface for top level modules * first pass at private for utils -> _internal * private interface for pregel * scratchpad/stream protocol move * docs update * backwards compat for runnable * deprecation warning for send and interrupt * deprecation for pregel import --- docs/docs/tutorials/tot/tot.ipynb | 2 +- libs/cli/pyproject.toml | 2 +- libs/cli/uv.lock | 8 +- libs/langgraph/bench/fanout_to_subgraph.py | 3 +- libs/langgraph/bench/sequential.py | 2 +- .../langgraph/langgraph/_internal/__init__.py | 4 + .../{utils/cache.py => _internal/_cache.py} | 0 .../{utils/config.py => _internal/_config.py} | 1 - .../{utils/fields.py => _internal/_fields.py} | 0 .../{utils/future.py => _internal/_future.py} | 0 .../pydantic.py => _internal/_pydantic.py} | 0 .../{utils/queue.py => _internal/_queue.py} | 3 - libs/langgraph/langgraph/_internal/_retry.py | 29 + .../runnable.py => _internal/_runnable.py} | 12 +- .../langgraph/{ => _internal}/_typing.py | 0 libs/langgraph/langgraph/channels/__init__.py | 24 +- .../langgraph/langgraph/channels/any_value.py | 2 + libs/langgraph/langgraph/channels/base.py | 11 +- libs/langgraph/langgraph/channels/binop.py | 2 + .../langgraph/channels/ephemeral_value.py | 2 + .../langgraph/channels/last_value.py | 2 + .../langgraph/channels/named_barrier_value.py | 2 + libs/langgraph/langgraph/channels/py.typed | 0 libs/langgraph/langgraph/channels/topic.py | 6 +- .../langgraph/channels/untracked_value.py | 2 + libs/langgraph/langgraph/constants.py | 34 +- libs/langgraph/langgraph/errors.py | 14 +- libs/langgraph/langgraph/func/__init__.py | 22 +- libs/langgraph/langgraph/func/py.typed | 0 libs/langgraph/langgraph/graph/__init__.py | 6 +- .../langgraph/graph/{branch.py => _branch.py} | 24 +- libs/langgraph/langgraph/graph/_node.py | 84 + libs/langgraph/langgraph/graph/message.py | 8 +- libs/langgraph/langgraph/graph/py.typed | 0 libs/langgraph/langgraph/graph/state.py | 138 +- libs/langgraph/langgraph/graph/ui.py | 11 +- libs/langgraph/langgraph/managed/__init__.py | 2 +- libs/langgraph/langgraph/managed/base.py | 4 +- .../langgraph/managed/is_last_step.py | 4 +- libs/langgraph/langgraph/managed/py.typed | 0 libs/langgraph/langgraph/pregel/__init__.py | 3058 +--------------- .../langgraph/pregel/{algo.py => _algo.py} | 14 +- .../langgraph/pregel/{call.py => _call.py} | 14 +- .../pregel/{checkpoint.py => _checkpoint.py} | 0 .../langgraph/pregel/{draw.py => _draw.py} | 10 +- .../pregel/{executor.py => _executor.py} | 2 +- .../langgraph/pregel/{io.py => _io.py} | 2 +- .../langgraph/pregel/{log.py => _log.py} | 0 .../langgraph/pregel/{loop.py => _loop.py} | 29 +- .../pregel/{messages.py => _messages.py} | 9 +- .../langgraph/pregel/{read.py => _read.py} | 8 +- .../langgraph/pregel/{retry.py => _retry.py} | 2 +- .../pregel/{runner.py => _runner.py} | 10 +- .../langgraph/langgraph/pregel/_scratchpad.py | 18 + .../langgraph/pregel/{utils.py => _utils.py} | 4 +- .../pregel/{validate.py => _validate.py} | 2 +- .../langgraph/pregel/{write.py => _write.py} | 5 +- libs/langgraph/langgraph/pregel/debug.py | 7 +- libs/langgraph/langgraph/pregel/main.py | 3061 +++++++++++++++++ libs/langgraph/langgraph/pregel/protocol.py | 26 +- libs/langgraph/langgraph/pregel/py.typed | 0 libs/langgraph/langgraph/pregel/remote.py | 30 +- libs/langgraph/langgraph/pregel/types.py | 11 + libs/langgraph/langgraph/types.py | 90 +- libs/langgraph/langgraph/typing.py | 16 +- libs/langgraph/langgraph/utils/py.typed | 0 libs/langgraph/langgraph/version.py | 2 + libs/langgraph/langgraph/warnings.py | 13 + .../tests/__snapshots__/test_large_cases.ambr | 121 +- .../tests/__snapshots__/test_pregel.ambr | 217 +- libs/langgraph/tests/test_algo.py | 4 +- .../tests/test_checkpoint_migration.py | 4 +- libs/langgraph/tests/test_config_async.py | 2 +- libs/langgraph/tests/test_deprecation.py | 24 +- libs/langgraph/tests/test_pregel.py | 11 +- libs/langgraph/tests/test_pregel_async.py | 24 +- libs/langgraph/tests/test_pydantic.py | 2 +- libs/langgraph/tests/test_remote_graph.py | 3 +- libs/langgraph/tests/test_retry.py | 2 +- libs/langgraph/tests/test_runnable.py | 6 +- libs/langgraph/tests/test_utils.py | 10 +- .../{langgraph => }/utils/__init__.py | 0 libs/langgraph/utils/runnable.py | 2 + libs/langgraph/uv.lock | 809 ++--- .../langgraph/prebuilt/chat_agent_executor.py | 2 +- libs/prebuilt/langgraph/prebuilt/tool_node.py | 2 +- .../langgraph/prebuilt/tool_validator.py | 2 +- libs/prebuilt/tests/memory_assert.py | 2 +- libs/prebuilt/tests/test_react_agent.py | 2 +- 89 files changed, 4085 insertions(+), 4079 deletions(-) create mode 100644 libs/langgraph/langgraph/_internal/__init__.py rename libs/langgraph/langgraph/{utils/cache.py => _internal/_cache.py} (100%) rename libs/langgraph/langgraph/{utils/config.py => _internal/_config.py} (99%) rename libs/langgraph/langgraph/{utils/fields.py => _internal/_fields.py} (100%) rename libs/langgraph/langgraph/{utils/future.py => _internal/_future.py} (100%) rename libs/langgraph/langgraph/{utils/pydantic.py => _internal/_pydantic.py} (100%) rename libs/langgraph/langgraph/{utils/queue.py => _internal/_queue.py} (99%) create mode 100644 libs/langgraph/langgraph/_internal/_retry.py rename libs/langgraph/langgraph/{utils/runnable.py => _internal/_runnable.py} (99%) rename libs/langgraph/langgraph/{ => _internal}/_typing.py (100%) delete mode 100644 libs/langgraph/langgraph/channels/py.typed delete mode 100644 libs/langgraph/langgraph/func/py.typed rename libs/langgraph/langgraph/graph/{branch.py => _branch.py} (96%) create mode 100644 libs/langgraph/langgraph/graph/_node.py delete mode 100644 libs/langgraph/langgraph/graph/py.typed delete mode 100644 libs/langgraph/langgraph/managed/py.typed rename libs/langgraph/langgraph/pregel/{algo.py => _algo.py} (99%) rename libs/langgraph/langgraph/pregel/{call.py => _call.py} (96%) rename libs/langgraph/langgraph/pregel/{checkpoint.py => _checkpoint.py} (100%) rename libs/langgraph/langgraph/pregel/{draw.py => _draw.py} (97%) rename libs/langgraph/langgraph/pregel/{executor.py => _executor.py} (98%) rename libs/langgraph/langgraph/pregel/{io.py => _io.py} (99%) rename libs/langgraph/langgraph/pregel/{log.py => _log.py} (100%) rename libs/langgraph/langgraph/pregel/{loop.py => _loop.py} (98%) rename libs/langgraph/langgraph/pregel/{messages.py => _messages.py} (97%) rename libs/langgraph/langgraph/pregel/{read.py => _read.py} (97%) rename libs/langgraph/langgraph/pregel/{retry.py => _retry.py} (99%) rename libs/langgraph/langgraph/pregel/{runner.py => _runner.py} (98%) create mode 100644 libs/langgraph/langgraph/pregel/_scratchpad.py rename libs/langgraph/langgraph/pregel/{utils.py => _utils.py} (97%) rename libs/langgraph/langgraph/pregel/{validate.py => _validate.py} (98%) rename libs/langgraph/langgraph/pregel/{write.py => _write.py} (98%) create mode 100644 libs/langgraph/langgraph/pregel/main.py delete mode 100644 libs/langgraph/langgraph/pregel/py.typed delete mode 100644 libs/langgraph/langgraph/utils/py.typed rename libs/langgraph/{langgraph => }/utils/__init__.py (100%) create mode 100644 libs/langgraph/utils/runnable.py diff --git a/docs/docs/tutorials/tot/tot.ipynb b/docs/docs/tutorials/tot/tot.ipynb index 29fe435bc..cb6e967a0 100644 --- a/docs/docs/tutorials/tot/tot.ipynb +++ b/docs/docs/tutorials/tot/tot.ipynb @@ -282,8 +282,8 @@ "from langgraph.graph import StateGraph\n", "\n", "from langchain_core.runnables import RunnableConfig\n", - "from langgraph.constants import Send\n", "from langgraph.checkpoint.memory import MemorySaver\n", + "from langgraph.types import Send\n", "\n", "\n", "def update_candidates(\n", diff --git a/libs/cli/pyproject.toml b/libs/cli/pyproject.toml index 4cb4a561a..839f714bd 100644 --- a/libs/cli/pyproject.toml +++ b/libs/cli/pyproject.toml @@ -19,7 +19,7 @@ dependencies = [ [project.optional-dependencies] inmem = [ "langgraph-api>=0.2.67 ; python_version >= '3.11'", - "langgraph-runtime-inmem>=0.3.0 ; python_version >= '3.11'", + "langgraph-runtime-inmem>=0.3.4 ; python_version >= '3.11'", "python-dotenv>=0.8.0", ] diff --git a/libs/cli/uv.lock b/libs/cli/uv.lock index b256a721c..bf7ad385f 100644 --- a/libs/cli/uv.lock +++ b/libs/cli/uv.lock @@ -531,7 +531,7 @@ dev = [ requires-dist = [ { name = "click", specifier = ">=8.1.7" }, { name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.67" }, - { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3.0" }, + { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3.4" }, { name = "langgraph-sdk", marker = "python_full_version >= '3.11'", specifier = ">=0.1.0" }, { name = "python-dotenv", marker = "extra == 'inmem'", specifier = ">=0.8.0" }, ] @@ -564,7 +564,7 @@ wheels = [ [[package]] name = "langgraph-runtime-inmem" -version = "0.3.3" +version = "0.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "blockbuster", marker = "python_full_version >= '3.11'" }, @@ -574,9 +574,9 @@ dependencies = [ { name = "starlette", marker = "python_full_version >= '3.11'" }, { name = "structlog", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/c6/c6c515df38179517a187b11cc9506caac9bc7c25ab0adc1e21b463e338fd/langgraph_runtime_inmem-0.3.3.tar.gz", hash = "sha256:1b5bc8b05989f48c64c826d826e576a6e77f42349eca3382f38f8ed1d1f026e4", size = 77443, upload-time = "2025-06-24T19:50:36.235Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/17/7ff669ff44a53ab342903c2996fff75a77494af9fe56abcfbca64fe2342b/langgraph_runtime_inmem-0.3.4.tar.gz", hash = "sha256:eda7828f3ea07126e5265024b74a3fa9bf611633ad83ba3296ab9f51d89b7c0c", size = 77424, upload-time = "2025-07-01T14:45:07.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/87/1b47cf8a9a9ab8e6460203921e05f59e1e95dc0636e64a7429cd0e579a79/langgraph_runtime_inmem-0.3.3-py3-none-any.whl", hash = "sha256:ed485d520870a96a4a2e81188f2dbf993e7b96c5c65804bcdfdcb39df71be3ce", size = 29146, upload-time = "2025-06-24T19:50:35.083Z" }, + { url = "https://files.pythonhosted.org/packages/95/0e/39c13ca7229a9425a0e5744a1d3817f80d38dd5ca7703494fb9cf836ba45/langgraph_runtime_inmem-0.3.4-py3-none-any.whl", hash = "sha256:dcb9ac68ac90b3fb1ddaf666d14a367ab70e69d5bb5589b77a72c318e29104ae", size = 29139, upload-time = "2025-07-01T14:45:06.472Z" }, ] [[package]] diff --git a/libs/langgraph/bench/fanout_to_subgraph.py b/libs/langgraph/bench/fanout_to_subgraph.py index ade894acc..f71a43077 100644 --- a/libs/langgraph/bench/fanout_to_subgraph.py +++ b/libs/langgraph/bench/fanout_to_subgraph.py @@ -3,8 +3,9 @@ from typing import Annotated from typing_extensions import TypedDict -from langgraph.constants import END, START, Send +from langgraph.constants import END, START from langgraph.graph.state import StateGraph +from langgraph.types import Send def fanout_to_subgraph() -> StateGraph: diff --git a/libs/langgraph/bench/sequential.py b/libs/langgraph/bench/sequential.py index bfdad823e..886f51ba3 100644 --- a/libs/langgraph/bench/sequential.py +++ b/libs/langgraph/bench/sequential.py @@ -1,7 +1,7 @@ """Create a sequential no-op graph consisting of a few hundred nodes.""" +from langgraph._internal._runnable import RunnableCallable from langgraph.graph import MessagesState, StateGraph -from langgraph.utils.runnable import RunnableCallable def create_sequential(number_nodes: int) -> StateGraph: diff --git a/libs/langgraph/langgraph/_internal/__init__.py b/libs/langgraph/langgraph/_internal/__init__.py new file mode 100644 index 000000000..2e71cdc23 --- /dev/null +++ b/libs/langgraph/langgraph/_internal/__init__.py @@ -0,0 +1,4 @@ +"""Internal modules for LangGraph. + +This module is not part of the public API, and thus stability is not guaranteed. +""" diff --git a/libs/langgraph/langgraph/utils/cache.py b/libs/langgraph/langgraph/_internal/_cache.py similarity index 100% rename from libs/langgraph/langgraph/utils/cache.py rename to libs/langgraph/langgraph/_internal/_cache.py diff --git a/libs/langgraph/langgraph/utils/config.py b/libs/langgraph/langgraph/_internal/_config.py similarity index 99% rename from libs/langgraph/langgraph/utils/config.py rename to libs/langgraph/langgraph/_internal/_config.py index ace98cd91..1c1428bb0 100644 --- a/libs/langgraph/langgraph/utils/config.py +++ b/libs/langgraph/langgraph/_internal/_config.py @@ -19,7 +19,6 @@ from langchain_core.runnables.config import ( ) from langgraph.checkpoint.base import CheckpointMetadata -from langgraph.config import get_config, get_store, get_stream_writer # noqa from langgraph.constants import ( CONF, CONFIG_KEY_CHECKPOINT_ID, diff --git a/libs/langgraph/langgraph/utils/fields.py b/libs/langgraph/langgraph/_internal/_fields.py similarity index 100% rename from libs/langgraph/langgraph/utils/fields.py rename to libs/langgraph/langgraph/_internal/_fields.py diff --git a/libs/langgraph/langgraph/utils/future.py b/libs/langgraph/langgraph/_internal/_future.py similarity index 100% rename from libs/langgraph/langgraph/utils/future.py rename to libs/langgraph/langgraph/_internal/_future.py diff --git a/libs/langgraph/langgraph/utils/pydantic.py b/libs/langgraph/langgraph/_internal/_pydantic.py similarity index 100% rename from libs/langgraph/langgraph/utils/pydantic.py rename to libs/langgraph/langgraph/_internal/_pydantic.py diff --git a/libs/langgraph/langgraph/utils/queue.py b/libs/langgraph/langgraph/_internal/_queue.py similarity index 99% rename from libs/langgraph/langgraph/utils/queue.py rename to libs/langgraph/langgraph/_internal/_queue.py index c0717fe34..b495e15c7 100644 --- a/libs/langgraph/langgraph/utils/queue.py +++ b/libs/langgraph/langgraph/_internal/_queue.py @@ -128,6 +128,3 @@ class SyncQueue: return len(self._queue) __class_getitem__ = classmethod(types.GenericAlias) - - -__all__ = ["AsyncQueue", "SyncQueue"] diff --git a/libs/langgraph/langgraph/_internal/_retry.py b/libs/langgraph/langgraph/_internal/_retry.py new file mode 100644 index 000000000..8d4e41fd7 --- /dev/null +++ b/libs/langgraph/langgraph/_internal/_retry.py @@ -0,0 +1,29 @@ +def default_retry_on(exc: Exception) -> bool: + import httpx + import requests + + if isinstance(exc, ConnectionError): + return True + if isinstance(exc, httpx.HTTPStatusError): + return 500 <= exc.response.status_code < 600 + if isinstance(exc, requests.HTTPError): + return 500 <= exc.response.status_code < 600 if exc.response else True + if isinstance( + exc, + ( + ValueError, + TypeError, + ArithmeticError, + ImportError, + LookupError, + NameError, + SyntaxError, + RuntimeError, + ReferenceError, + StopIteration, + StopAsyncIteration, + OSError, + ), + ): + return False + return True diff --git a/libs/langgraph/langgraph/utils/runnable.py b/libs/langgraph/langgraph/_internal/_runnable.py similarity index 99% rename from libs/langgraph/langgraph/utils/runnable.py rename to libs/langgraph/langgraph/_internal/_runnable.py index 3ca48aec3..fe24301f6 100644 --- a/libs/langgraph/langgraph/utils/runnable.py +++ b/libs/langgraph/langgraph/_internal/_runnable.py @@ -42,6 +42,12 @@ from langchain_core.runnables.utils import Input, Output from langchain_core.tracers.langchain import LangChainTracer from typing_extensions import TypeGuard +from langgraph._internal._config import ( + ensure_config, + get_async_callback_manager_for_config, + get_callback_manager_for_config, + patch_config, +) from langgraph.constants import ( CONF, CONFIG_KEY_PREVIOUS, @@ -50,12 +56,6 @@ from langgraph.constants import ( ) from langgraph.store.base import BaseStore from langgraph.types import StreamWriter -from langgraph.utils.config import ( - ensure_config, - get_async_callback_manager_for_config, - get_callback_manager_for_config, - patch_config, -) try: from langchain_core.tracers._streaming import _StreamingCallbackHandler diff --git a/libs/langgraph/langgraph/_typing.py b/libs/langgraph/langgraph/_internal/_typing.py similarity index 100% rename from libs/langgraph/langgraph/_typing.py rename to libs/langgraph/langgraph/_internal/_typing.py diff --git a/libs/langgraph/langgraph/channels/__init__.py b/libs/langgraph/langgraph/channels/__init__.py index cdb193484..a69c230b5 100644 --- a/libs/langgraph/langgraph/channels/__init__.py +++ b/libs/langgraph/langgraph/channels/__init__.py @@ -1,15 +1,27 @@ from langgraph.channels.any_value import AnyValue +from langgraph.channels.base import BaseChannel from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.ephemeral_value import EphemeralValue -from langgraph.channels.last_value import LastValue +from langgraph.channels.last_value import LastValue, LastValueAfterFinish +from langgraph.channels.named_barrier_value import ( + NamedBarrierValue, + NamedBarrierValueAfterFinish, +) from langgraph.channels.topic import Topic from langgraph.channels.untracked_value import UntrackedValue -__all__ = [ +__all__ = ( + # base + "BaseChannel", + # value types + "AnyValue", "LastValue", - "Topic", - "BinaryOperatorAggregate", + "LastValueAfterFinish", "UntrackedValue", "EphemeralValue", - "AnyValue", -] + "BinaryOperatorAggregate", + "NamedBarrierValue", + "NamedBarrierValueAfterFinish", + # topics + "Topic", +) diff --git a/libs/langgraph/langgraph/channels/any_value.py b/libs/langgraph/langgraph/channels/any_value.py index ec597dacb..18b008a34 100644 --- a/libs/langgraph/langgraph/channels/any_value.py +++ b/libs/langgraph/langgraph/channels/any_value.py @@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError +__all__ = ("AnyValue",) + class AnyValue(Generic[Value], BaseChannel[Value, Value, Value]): """Stores the last value received, assumes that if multiple values are diff --git a/libs/langgraph/langgraph/channels/base.py b/libs/langgraph/langgraph/channels/base.py index 4d6335bc1..5f6ae3f1a 100644 --- a/libs/langgraph/langgraph/channels/base.py +++ b/libs/langgraph/langgraph/channels/base.py @@ -5,12 +5,14 @@ from typing import Any, Generic, TypeVar from typing_extensions import Self from langgraph.constants import MISSING -from langgraph.errors import EmptyChannelError, InvalidUpdateError +from langgraph.errors import EmptyChannelError Value = TypeVar("Value") Update = TypeVar("Update") C = TypeVar("C") +__all__ = ("BaseChannel",) + class BaseChannel(Generic[Value, Update, C], ABC): """Base class for all channels.""" @@ -99,10 +101,3 @@ class BaseChannel(Generic[Value, Update, C], ABC): Returns True if the channel was updated, False otherwise. """ return False - - -__all__ = [ - "BaseChannel", - "EmptyChannelError", - "InvalidUpdateError", -] diff --git a/libs/langgraph/langgraph/channels/binop.py b/libs/langgraph/langgraph/channels/binop.py index e974c5fba..6b34b5533 100644 --- a/libs/langgraph/langgraph/channels/binop.py +++ b/libs/langgraph/langgraph/channels/binop.py @@ -8,6 +8,8 @@ from langgraph.channels.base import BaseChannel, Value from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError +__all__ = ("BinaryOperatorAggregate",) + # Adapted from typing_extensions def _strip_extras(t): # type: ignore[no-untyped-def] diff --git a/libs/langgraph/langgraph/channels/ephemeral_value.py b/libs/langgraph/langgraph/channels/ephemeral_value.py index 7448be106..98d4f41dd 100644 --- a/libs/langgraph/langgraph/channels/ephemeral_value.py +++ b/libs/langgraph/langgraph/channels/ephemeral_value.py @@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError, InvalidUpdateError +__all__ = ("EphemeralValue",) + class EphemeralValue(Generic[Value], BaseChannel[Value, Value, Value]): """Stores the value received in the step immediately preceding, clears after.""" diff --git a/libs/langgraph/langgraph/channels/last_value.py b/libs/langgraph/langgraph/channels/last_value.py index 59c8d3c1b..1c07fc7ab 100644 --- a/libs/langgraph/langgraph/channels/last_value.py +++ b/libs/langgraph/langgraph/channels/last_value.py @@ -12,6 +12,8 @@ from langgraph.errors import ( create_error_message, ) +__all__ = ("LastValue", "LastValueAfterFinish") + class LastValue(Generic[Value], BaseChannel[Value, Value, Value]): """Stores the last value received, can receive at most one value per step.""" diff --git a/libs/langgraph/langgraph/channels/named_barrier_value.py b/libs/langgraph/langgraph/channels/named_barrier_value.py index e5e96a7fb..7f9c8baa0 100644 --- a/libs/langgraph/langgraph/channels/named_barrier_value.py +++ b/libs/langgraph/langgraph/channels/named_barrier_value.py @@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError, InvalidUpdateError +__all__ = ("NamedBarrierValue", "NamedBarrierValueAfterFinish") + class NamedBarrierValue(Generic[Value], BaseChannel[Value, Value, set[Value]]): """A channel that waits until all named values are received before making the value available.""" diff --git a/libs/langgraph/langgraph/channels/py.typed b/libs/langgraph/langgraph/channels/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/langgraph/langgraph/channels/topic.py b/libs/langgraph/langgraph/channels/topic.py index fa96bcd59..4b9113570 100644 --- a/libs/langgraph/langgraph/channels/topic.py +++ b/libs/langgraph/langgraph/channels/topic.py @@ -9,8 +9,10 @@ from langgraph.channels.base import BaseChannel, Value from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError +__all__ = ("Topic",) -def flatten(values: Sequence[Value | list[Value]]) -> Iterator[Value]: + +def _flatten(values: Sequence[Value | list[Value]]) -> Iterator[Value]: for value in values: if isinstance(value, list): yield from value @@ -77,7 +79,7 @@ class Topic( if not self.accumulate: updated = bool(self.values) self.values = list[Value]() - if flat_values := tuple(flatten(values)): + if flat_values := tuple(_flatten(values)): updated = True self.values.extend(flat_values) return updated diff --git a/libs/langgraph/langgraph/channels/untracked_value.py b/libs/langgraph/langgraph/channels/untracked_value.py index e0c9cb676..e339920dc 100644 --- a/libs/langgraph/langgraph/channels/untracked_value.py +++ b/libs/langgraph/langgraph/channels/untracked_value.py @@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError, InvalidUpdateError +__all__ = ("UntrackedValue",) + class UntrackedValue(Generic[Value], BaseChannel[Value, Value, Value]): """Stores the last value received, never checkpointed.""" diff --git a/libs/langgraph/langgraph/constants.py b/libs/langgraph/langgraph/constants.py index ac53718e1..e0b5452ba 100644 --- a/libs/langgraph/langgraph/constants.py +++ b/libs/langgraph/langgraph/constants.py @@ -1,23 +1,43 @@ import sys -from collections.abc import Mapping -from types import MappingProxyType from typing import Any, Literal, cast +from warnings import warn -from langgraph.types import Interrupt, Send # noqa: F401 +from langgraph.warnings import LangGraphDeprecatedSinceV10 -# Interrupt, Send re-exported for backwards compatibility +__all__ = ( + "TAG_NOSTREAM", + "TAG_HIDDEN", + "START", + "END", + "SELF", + "PREVIOUS", +) + + +def __getattr__(name: str) -> Any: + if name in ["Send", "Interrupt"]: + warn( + f"Importing {name} from langgraph.constants is deprecated. " + f"Please use 'from langgraph.types import {name}' instead.", + LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + + from importlib import import_module + + module = import_module("langgraph.types") + return getattr(module, name) + + raise AttributeError(f"module has no attribute '{name}'") # --- Empty read-only containers --- -EMPTY_MAP: Mapping[str, Any] = MappingProxyType({}) EMPTY_SEQ: tuple[str, ...] = tuple() MISSING = object() # --- Public constants --- TAG_NOSTREAM = sys.intern("nostream") """Tag to disable streaming for a chat model.""" -TAG_NOSTREAM_ALT = sys.intern("langsmith:nostream") -"""Tag to disable streaming for a chat model. (Deprecated in favour of "nostream")""" TAG_HIDDEN = sys.intern("langsmith:hidden") """Tag to hide a node/edge from certain tracing/streaming environments.""" START = sys.intern("__start__") diff --git a/libs/langgraph/langgraph/errors.py b/libs/langgraph/langgraph/errors.py index 6213ff68e..e1121c1bf 100644 --- a/libs/langgraph/langgraph/errors.py +++ b/libs/langgraph/langgraph/errors.py @@ -2,10 +2,22 @@ from collections.abc import Sequence from enum import Enum from typing import Any +# EmptyChannelError is re-exported from langgraph.channels.base from langgraph.checkpoint.base import EmptyChannelError # noqa: F401 from langgraph.types import Command, Interrupt -# EmptyChannelError re-exported for backwards compatibility +__all__ = ( + "EmptyChannelError", + "ErrorCode", + "GraphRecursionError", + "InvalidUpdateError", + "GraphBubbleUp", + "GraphInterrupt", + "NodeInterrupt", + "ParentCommand", + "EmptyInputError", + "TaskNotFound", +) class ErrorCode(Enum): diff --git a/libs/langgraph/langgraph/func/__init__.py b/libs/langgraph/langgraph/func/__init__.py index ba0b3c4cb..90d43c191 100644 --- a/libs/langgraph/langgraph/func/__init__.py +++ b/libs/langgraph/langgraph/func/__init__.py @@ -19,14 +19,14 @@ from typing import ( from typing_extensions import Unpack -from langgraph._typing import UNSET, DeprecatedKwargs +from langgraph._internal._typing import UNSET, DeprecatedKwargs from langgraph.cache.base import BaseCache from langgraph.channels.ephemeral_value import EphemeralValue from langgraph.channels.last_value import LastValue from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.constants import CACHE_NS_WRITES, END, PREVIOUS, START from langgraph.pregel import Pregel -from langgraph.pregel.call import ( +from langgraph.pregel._call import ( P, SyncAsyncFuture, T, @@ -34,14 +34,16 @@ from langgraph.pregel.call import ( get_runnable_for_entrypoint, identifier, ) -from langgraph.pregel.read import PregelNode -from langgraph.pregel.write import ChannelWrite, ChannelWriteEntry +from langgraph.pregel._read import PregelNode +from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry from langgraph.store.base import BaseStore from langgraph.types import _DC_KWARGS, CachePolicy, RetryPolicy, StreamMode from langgraph.warnings import LangGraphDeprecatedSinceV05 +__all__ = ("task", "entrypoint") -class TaskFunction(Generic[P, T]): + +class _TaskFunction(Generic[P, T]): def __init__( self, func: Callable[P, T], @@ -97,14 +99,14 @@ def task( **kwargs: Unpack[DeprecatedKwargs], ) -> Callable[ [Callable[P, Awaitable[T]] | Callable[P, T]], - TaskFunction[P, T], + _TaskFunction[P, T], ]: ... @overload def task( __func_or_none__: Callable[P, Awaitable[T]] | Callable[P, T], -) -> TaskFunction[P, T]: ... +) -> _TaskFunction[P, T]: ... def task( @@ -115,8 +117,8 @@ def task( cache_policy: CachePolicy[Callable[P, str | bytes]] | None = None, **kwargs: Unpack[DeprecatedKwargs], ) -> ( - Callable[[Callable[P, Awaitable[T]] | Callable[P, T]], TaskFunction[P, T]] - | TaskFunction[P, T] + Callable[[Callable[P, Awaitable[T]] | Callable[P, T]], _TaskFunction[P, T]] + | _TaskFunction[P, T] ): """Define a LangGraph task using the `task` decorator. @@ -195,7 +197,7 @@ def task( def decorator( func: Callable[P, Awaitable[T]] | Callable[P, T], ) -> Callable[P, concurrent.futures.Future[T]] | Callable[P, asyncio.Future[T]]: - return TaskFunction( + return _TaskFunction( func, retry_policy=retry_policies, cache_policy=cache_policy, name=name ) diff --git a/libs/langgraph/langgraph/func/py.typed b/libs/langgraph/langgraph/func/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/langgraph/langgraph/graph/__init__.py b/libs/langgraph/langgraph/graph/__init__.py index 2581713c3..7bea3fc82 100644 --- a/libs/langgraph/langgraph/graph/__init__.py +++ b/libs/langgraph/langgraph/graph/__init__.py @@ -2,11 +2,11 @@ from langgraph.constants import END, START from langgraph.graph.message import MessageGraph, MessagesState, add_messages from langgraph.graph.state import StateGraph -__all__ = [ +__all__ = ( "END", "START", "StateGraph", - "MessageGraph", "add_messages", "MessagesState", -] + "MessageGraph", +) diff --git a/libs/langgraph/langgraph/graph/branch.py b/libs/langgraph/langgraph/graph/_branch.py similarity index 96% rename from libs/langgraph/langgraph/graph/branch.py rename to libs/langgraph/langgraph/graph/_branch.py index f120167d6..90bb68b88 100644 --- a/libs/langgraph/langgraph/graph/branch.py +++ b/libs/langgraph/langgraph/graph/_branch.py @@ -26,15 +26,15 @@ from langchain_core.runnables import ( RunnableLambda, ) -from langgraph.constants import END, START -from langgraph.errors import InvalidUpdateError -from langgraph.pregel.write import PASSTHROUGH, ChannelWrite, ChannelWriteEntry -from langgraph.types import Send -from langgraph.utils.runnable import ( +from langgraph._internal._runnable import ( RunnableCallable, ) +from langgraph.constants import END, START +from langgraph.errors import InvalidUpdateError +from langgraph.pregel._write import PASSTHROUGH, ChannelWrite, ChannelWriteEntry +from langgraph.types import Send -Writer = Callable[ +_Writer = Callable[ [Sequence[Union[str, Send]], bool], Sequence[Union[ChannelWriteEntry, Send]], ] @@ -82,7 +82,7 @@ def _get_branch_path_input_schema( return input -class Branch(NamedTuple): +class BranchSpec(NamedTuple): path: Runnable[Any, Hashable | list[Hashable]] ends: dict[Hashable, str] | None input_schema: type[Any] | None = None @@ -93,7 +93,7 @@ class Branch(NamedTuple): path: Runnable[Any, Hashable | list[Hashable]], path_map: dict[Hashable, str] | list[str] | None, infer_schema: bool = False, - ) -> Branch: + ) -> BranchSpec: # coerce path_map to a dictionary path_map_: dict[Hashable, str] | None = None try: @@ -123,7 +123,7 @@ class Branch(NamedTuple): def run( self, - writer: Writer, + writer: _Writer, reader: Callable[[RunnableConfig], Any] | None = None, ) -> RunnableCallable: return ChannelWrite.register_writer( @@ -152,7 +152,7 @@ class Branch(NamedTuple): config: RunnableConfig, *, reader: Callable[[RunnableConfig], Any] | None, - writer: Writer, + writer: _Writer, ) -> Runnable: if reader: value = reader(config) @@ -175,7 +175,7 @@ class Branch(NamedTuple): config: RunnableConfig, *, reader: Callable[[RunnableConfig], Any] | None, - writer: Writer, + writer: _Writer, ) -> Runnable: if reader: value = reader(config) @@ -194,7 +194,7 @@ class Branch(NamedTuple): def _finish( self, - writer: Writer, + writer: _Writer, input: Any, result: Any, config: RunnableConfig, diff --git a/libs/langgraph/langgraph/graph/_node.py b/libs/langgraph/langgraph/graph/_node.py new file mode 100644 index 000000000..f63d29dbe --- /dev/null +++ b/libs/langgraph/langgraph/graph/_node.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any, NamedTuple, Protocol, Union + +from langchain_core.runnables import Runnable, RunnableConfig +from typing_extensions import TypeAlias + +from langgraph.constants import EMPTY_SEQ +from langgraph.store.base import BaseStore +from langgraph.types import CachePolicy, RetryPolicy, StreamWriter +from langgraph.typing import StateT_contra + + +class _Node(Protocol[StateT_contra]): + def __call__(self, state: StateT_contra) -> Any: ... + + +class _NodeWithConfig(Protocol[StateT_contra]): + def __call__(self, state: StateT_contra, config: RunnableConfig) -> Any: ... + + +class _NodeWithWriter(Protocol[StateT_contra]): + def __call__(self, state: StateT_contra, *, writer: StreamWriter) -> Any: ... + + +class _NodeWithStore(Protocol[StateT_contra]): + def __call__(self, state: StateT_contra, *, store: BaseStore) -> Any: ... + + +class _NodeWithWriterStore(Protocol[StateT_contra]): + def __call__( + self, state: StateT_contra, *, writer: StreamWriter, store: BaseStore + ) -> Any: ... + + +class _NodeWithConfigWriter(Protocol[StateT_contra]): + def __call__( + self, state: StateT_contra, *, config: RunnableConfig, writer: StreamWriter + ) -> Any: ... + + +class _NodeWithConfigStore(Protocol[StateT_contra]): + def __call__( + self, state: StateT_contra, *, config: RunnableConfig, store: BaseStore + ) -> Any: ... + + +class _NodeWithConfigWriterStore(Protocol[StateT_contra]): + def __call__( + self, + state: StateT_contra, + *, + config: RunnableConfig, + writer: StreamWriter, + store: BaseStore, + ) -> Any: ... + + +# TODO: we probably don't want to explicitly support the config / store signatures once +# we move to adding a context arg. Maybe what we do is we add support for kwargs with param spec +# this is purely for typing purposes though, so can easily change in the coming weeks. +StateNode: TypeAlias = Union[ + _Node[StateT_contra], + _NodeWithConfig[StateT_contra], + _NodeWithWriter[StateT_contra], + _NodeWithStore[StateT_contra], + _NodeWithWriterStore[StateT_contra], + _NodeWithConfigWriter[StateT_contra], + _NodeWithConfigStore[StateT_contra], + _NodeWithConfigWriterStore[StateT_contra], + Runnable[StateT_contra, Any], +] + + +# TODO: use a dataclass generic on NodeInputType +class StateNodeSpec(NamedTuple): + runnable: StateNode + metadata: dict[str, Any] | None + input_schema: type[Any] + retry_policy: RetryPolicy | Sequence[RetryPolicy] | None + cache_policy: CachePolicy | None + ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ + defer: bool = False diff --git a/libs/langgraph/langgraph/graph/message.py b/libs/langgraph/langgraph/graph/message.py index e50bcfec2..fc3355eb6 100644 --- a/libs/langgraph/langgraph/graph/message.py +++ b/libs/langgraph/langgraph/graph/message.py @@ -27,6 +27,12 @@ from typing_extensions import TypedDict from langgraph.constants import CONF, CONFIG_KEY_SEND from langgraph.graph.state import StateGraph +__all__ = ( + "add_messages", + "MessagesState", + "MessageGraph", +) + Messages = Union[list[MessageLikeRepresentation], MessageLikeRepresentation] REMOVE_ALL_MESSAGES = "__remove_all__" @@ -315,7 +321,7 @@ def push_message( from langgraph.config import get_config from langgraph.constants import NS_SEP - from langgraph.pregel.messages import StreamMessagesHandler + from langgraph.pregel._messages import StreamMessagesHandler config = get_config() message = next(x for x in convert_to_messages([message])) diff --git a/libs/langgraph/langgraph/graph/py.typed b/libs/langgraph/langgraph/graph/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 151b1b278..6ad7dce00 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -14,8 +14,6 @@ from typing import ( Callable, Generic, Literal, - NamedTuple, - Protocol, Union, cast, get_args, @@ -26,9 +24,16 @@ from typing import ( from langchain_core.runnables import Runnable, RunnableConfig from pydantic import BaseModel -from typing_extensions import Self, TypeAlias, Unpack +from typing_extensions import Self, Unpack -from langgraph._typing import UNSET, DeprecatedKwargs +from langgraph._internal._fields import ( + get_cached_annotated_keys, + get_field_default, + get_update_as_tuples, +) +from langgraph._internal._pydantic import create_model +from langgraph._internal._runnable import coerce_to_runnable +from langgraph._internal._typing import UNSET, DeprecatedKwargs from langgraph.cache.base import BaseCache from langgraph.channels.base import BaseChannel from langgraph.channels.binop import BinaryOperatorAggregate @@ -56,14 +61,15 @@ from langgraph.errors import ( ParentCommand, create_error_message, ) -from langgraph.graph.branch import Branch +from langgraph.graph._branch import BranchSpec +from langgraph.graph._node import StateNode, StateNodeSpec from langgraph.managed.base import ( ManagedValueSpec, is_managed_value, ) from langgraph.pregel import Pregel -from langgraph.pregel.read import ChannelRead, PregelNode -from langgraph.pregel.write import ( +from langgraph.pregel._read import ChannelRead, PregelNode +from langgraph.pregel._write import ( ChannelWrite, ChannelWriteEntry, ChannelWriteTupleEntry, @@ -76,20 +82,16 @@ from langgraph.types import ( Command, RetryPolicy, Send, - StreamWriter, ) -from langgraph.typing import InputT, OutputT, StateT, StateT_contra -from langgraph.utils.fields import ( - get_cached_annotated_keys, - get_field_default, - get_update_as_tuples, -) -from langgraph.utils.pydantic import create_model -from langgraph.utils.runnable import coerce_to_runnable +from langgraph.typing import InputT, OutputT, StateT from langgraph.warnings import LangGraphDeprecatedSinceV05 +__all__ = ("StateGraph", "CompiledStateGraph") + logger = logging.getLogger(__name__) +_CHANNEL_BRANCH_TO = "branch:to:{}" + def _warn_invalid_state_schema(schema: type[Any] | Any) -> None: if isinstance(schema, type): @@ -103,67 +105,6 @@ def _warn_invalid_state_schema(schema: type[Any] | Any) -> None: ) -class _StateNode(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra) -> Any: ... - - -class _NodeWithConfig(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra, config: RunnableConfig) -> Any: ... - - -class _NodeWithWriter(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra, *, writer: StreamWriter) -> Any: ... - - -class _NodeWithStore(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra, *, store: BaseStore) -> Any: ... - - -class _NodeWithWriterStore(Protocol[StateT_contra]): - def __call__( - self, state: StateT_contra, *, writer: StreamWriter, store: BaseStore - ) -> Any: ... - - -class _NodeWithConfigWriter(Protocol[StateT_contra]): - def __call__( - self, state: StateT_contra, *, config: RunnableConfig, writer: StreamWriter - ) -> Any: ... - - -class _NodeWithConfigStore(Protocol[StateT_contra]): - def __call__( - self, state: StateT_contra, *, config: RunnableConfig, store: BaseStore - ) -> Any: ... - - -class _NodeWithConfigWriterStore(Protocol[StateT_contra]): - def __call__( - self, - state: StateT_contra, - *, - config: RunnableConfig, - writer: StreamWriter, - store: BaseStore, - ) -> Any: ... - - -# TODO: we probably don't want to explicitly support the config / store signatures once -# we move to adding a context arg. Maybe what we do is we add support for kwargs with param spec -# this is purely for typing purposes though, so can easily change in the coming weeks. -StateNode: TypeAlias = Union[ - _StateNode[StateT_contra], - _NodeWithConfig[StateT_contra], - _NodeWithWriter[StateT_contra], - _NodeWithStore[StateT_contra], - _NodeWithWriterStore[StateT_contra], - _NodeWithConfigWriter[StateT_contra], - _NodeWithConfigStore[StateT_contra], - _NodeWithConfigWriterStore[StateT_contra], - Runnable[StateT_contra, Any], -] - - def _get_node_name(node: StateNode) -> str: try: return getattr(node, "__name__", node.__class__.__name__) @@ -171,20 +112,6 @@ def _get_node_name(node: StateNode) -> str: raise TypeError(f"Unsupported node type: {type(node)}") -class StateNodeSpec(NamedTuple): - # TODO: rename this callable, also move away from NamedTuple so that we can use - # a generic StateNode, so maybe a dataclass - runnable: StateNode - metadata: dict[str, Any] | None - # TODO: rename to input_schema, though we really just want to modify this structure to - # be a dataclass - input: type[Any] - retry_policy: RetryPolicy | Sequence[RetryPolicy] | None - cache_policy: CachePolicy | None - ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ - defer: bool = False - - class StateGraph(Generic[StateT, InputT, OutputT]): """A graph whose nodes communicate by reading and writing to a shared state. The signature of each node is State -> Partial. @@ -239,7 +166,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): edges: set[tuple[str, str]] nodes: dict[str, StateNodeSpec] - branches: defaultdict[str, dict[str, Branch]] + branches: defaultdict[str, dict[str, BranchSpec]] channels: dict[str, BaseChannel] managed: dict[str, ManagedValueSpec] schemas: dict[type[Any], dict[str, BaseChannel | ManagedValueSpec]] @@ -538,7 +465,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): self.nodes[node] = StateNodeSpec( coerce_to_runnable(action, name=node, trace=False), metadata, - input=input_schema or self.state_schema, + input_schema=input_schema or self.state_schema, retry_policy=retry_policy, cache_policy=cache_policy, ends=ends, @@ -641,7 +568,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): f"Branch with name `{path.name}` already exists for node `{source}`" ) # save it - self.branches[source][name] = Branch.from_path(path, path_map, True) + self.branches[source][name] = BranchSpec.from_path(path, path_map, True) if schema := self.branches[source][name].input_schema: self._add_schema(schema) return self @@ -994,7 +921,7 @@ class CompiledStateGraph( writers=[ChannelWrite(write_entries)], ) elif node is not None: - input_schema = node.input if node else self.builder._state_schema + input_schema = node.input_schema if node else self.builder._state_schema input_channels = list(self.builder.schemas[input_schema]) is_single_input = len(input_channels) == 1 and "__root__" in input_channels if input_schema in self.schema_to_mapper: @@ -1003,7 +930,7 @@ class CompiledStateGraph( mapper = _pick_mapper(input_channels, input_schema) self.schema_to_mapper[input_schema] = mapper - branch_channel = CHANNEL_BRANCH_TO.format(key) + branch_channel = _CHANNEL_BRANCH_TO.format(key) self.channels[branch_channel] = ( LastValueAfterFinish(Any) if node.defer @@ -1031,7 +958,7 @@ class CompiledStateGraph( if end != END: self.nodes[starts].writers.append( ChannelWrite( - (ChannelWriteEntry(CHANNEL_BRANCH_TO.format(end), None),) + (ChannelWriteEntry(_CHANNEL_BRANCH_TO.format(end), None),) ) ) elif end != END: @@ -1052,7 +979,7 @@ class CompiledStateGraph( ) def attach_branch( - self, start: str, name: str, branch: Branch, *, with_reader: bool = True + self, start: str, name: str, branch: BranchSpec, *, with_reader: bool = True ) -> None: def get_writes( packets: Sequence[str | Send], static: bool = False @@ -1060,7 +987,7 @@ class CompiledStateGraph( writes = [ ( ChannelWriteEntry( - p if p == END else CHANNEL_BRANCH_TO.format(p), None + p if p == END else _CHANNEL_BRANCH_TO.format(p), None ) if not isinstance(p, Send) else p @@ -1075,7 +1002,7 @@ class CompiledStateGraph( if with_reader: # get schema schema = branch.input_schema or ( - self.builder.nodes[start].input + self.builder.nodes[start].input_schema if start in self.builder.nodes else self.builder.state_schema ) @@ -1237,12 +1164,12 @@ def _control_branch(value: Any) -> Sequence[tuple[str, Any]]: if isinstance(command.goto, Send): rtn.append((TASKS, command.goto)) elif isinstance(command.goto, str): - rtn.append((CHANNEL_BRANCH_TO.format(command.goto), None)) + rtn.append((_CHANNEL_BRANCH_TO.format(command.goto), None)) else: rtn.extend( (TASKS, go) if isinstance(go, Send) - else (CHANNEL_BRANCH_TO.format(go), None) + else (_CHANNEL_BRANCH_TO.format(go), None) for go in command.goto ) return rtn @@ -1253,12 +1180,12 @@ def _control_static( ) -> Sequence[tuple[str, Any, str | None]]: if isinstance(ends, dict): return [ - (k if k == END else CHANNEL_BRANCH_TO.format(k), None, label) + (k if k == END else _CHANNEL_BRANCH_TO.format(k), None, label) for k, label in ends.items() ] else: return [ - (e if e == END else CHANNEL_BRANCH_TO.format(e), None, None) for e in ends + (e if e == END else _CHANNEL_BRANCH_TO.format(e), None, None) for e in ends ] @@ -1415,6 +1342,3 @@ def _get_schema( if k in channels and isinstance(channels[k], BaseChannel) }, ) - - -CHANNEL_BRANCH_TO = "branch:to:{}" diff --git a/libs/langgraph/langgraph/graph/ui.py b/libs/langgraph/langgraph/graph/ui.py index 181587740..e829eff2e 100644 --- a/libs/langgraph/langgraph/graph/ui.py +++ b/libs/langgraph/langgraph/graph/ui.py @@ -6,8 +6,17 @@ from uuid import uuid4 from langchain_core.messages import AnyMessage from typing_extensions import TypedDict +from langgraph.config import get_config, get_stream_writer from langgraph.constants import CONF, CONFIG_KEY_SEND -from langgraph.utils.config import get_config, get_stream_writer + +__all__ = ( + "UIMessage", + "RemoveUIMessage", + "AnyUIMessage", + "push_ui_message", + "delete_ui_message", + "ui_message_reducer", +) class UIMessage(TypedDict): diff --git a/libs/langgraph/langgraph/managed/__init__.py b/libs/langgraph/langgraph/managed/__init__.py index 966348e6f..2d50f323b 100644 --- a/libs/langgraph/langgraph/managed/__init__.py +++ b/libs/langgraph/langgraph/managed/__init__.py @@ -1,3 +1,3 @@ from langgraph.managed.is_last_step import IsLastStep, RemainingSteps -__all__ = ["IsLastStep", "RemainingSteps"] +__all__ = ("IsLastStep", "RemainingSteps") diff --git a/libs/langgraph/langgraph/managed/base.py b/libs/langgraph/langgraph/managed/base.py index aa8f507b6..3b5de24d5 100644 --- a/libs/langgraph/langgraph/managed/base.py +++ b/libs/langgraph/langgraph/managed/base.py @@ -8,11 +8,13 @@ from typing import ( from typing_extensions import TypeGuard -from langgraph.types import PregelScratchpad +from langgraph.pregel._scratchpad import PregelScratchpad V = TypeVar("V") U = TypeVar("U") +__all__ = ("ManagedValueSpec", "ManagedValueMapping") + class ManagedValue(ABC, Generic[V]): @staticmethod diff --git a/libs/langgraph/langgraph/managed/is_last_step.py b/libs/langgraph/langgraph/managed/is_last_step.py index ccfaea038..6ffa4df16 100644 --- a/libs/langgraph/langgraph/managed/is_last_step.py +++ b/libs/langgraph/langgraph/managed/is_last_step.py @@ -1,7 +1,9 @@ from typing import Annotated from langgraph.managed.base import ManagedValue -from langgraph.types import PregelScratchpad +from langgraph.pregel._scratchpad import PregelScratchpad + +__all__ = ("IsLastStep", "RemainingStepsManager") class IsLastStepManager(ManagedValue[bool]): diff --git a/libs/langgraph/langgraph/managed/py.typed b/libs/langgraph/langgraph/managed/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/langgraph/langgraph/pregel/__init__.py b/libs/langgraph/langgraph/pregel/__init__.py index 67c938611..90eb44b85 100644 --- a/libs/langgraph/langgraph/pregel/__init__.py +++ b/libs/langgraph/langgraph/pregel/__init__.py @@ -1,3057 +1,3 @@ -from __future__ import annotations +from langgraph.pregel.main import NodeBuilder, Pregel -import asyncio -import concurrent -import concurrent.futures -import queue -import weakref -from collections import defaultdict, deque -from collections.abc import AsyncIterator, Iterator, Mapping, Sequence -from functools import partial -from typing import Any, Callable, Generic, Union, cast, get_type_hints -from uuid import UUID, uuid5 - -from langchain_core.globals import get_debug -from langchain_core.runnables import ( - RunnableSequence, -) -from langchain_core.runnables.base import Input, Output -from langchain_core.runnables.config import ( - RunnableConfig, - get_async_callback_manager_for_config, - get_callback_manager_for_config, -) -from langchain_core.runnables.graph import Graph -from pydantic import BaseModel -from typing_extensions import Self - -from langgraph.cache.base import BaseCache -from langgraph.channels.base import BaseChannel -from langgraph.channels.topic import Topic -from langgraph.checkpoint.base import ( - BaseCheckpointSaver, - Checkpoint, - CheckpointTuple, -) -from langgraph.config import get_config -from langgraph.constants import ( - CACHE_NS_WRITES, - CONF, - CONFIG_KEY_CACHE, - CONFIG_KEY_CHECKPOINT_DURING, - CONFIG_KEY_CHECKPOINT_ID, - CONFIG_KEY_CHECKPOINT_NS, - CONFIG_KEY_CHECKPOINTER, - CONFIG_KEY_NODE_FINISHED, - CONFIG_KEY_READ, - CONFIG_KEY_RUNNER_SUBMIT, - CONFIG_KEY_SEND, - CONFIG_KEY_STORE, - CONFIG_KEY_STREAM, - CONFIG_KEY_STREAM_WRITER, - CONFIG_KEY_TASK_ID, - CONFIG_KEY_THREAD_ID, - END, - ERROR, - INPUT, - INTERRUPT, - NS_END, - NS_SEP, - NULL_TASK_ID, - PUSH, - TASKS, -) -from langgraph.errors import ( - ErrorCode, - GraphRecursionError, - InvalidUpdateError, - create_error_message, -) -from langgraph.managed.base import ManagedValueSpec -from langgraph.pregel.algo import ( - PregelTaskWrites, - _scratchpad, - apply_writes, - local_read, - prepare_next_tasks, -) -from langgraph.pregel.call import identifier -from langgraph.pregel.checkpoint import ( - channels_from_checkpoint, - copy_checkpoint, - create_checkpoint, - empty_checkpoint, -) -from langgraph.pregel.debug import get_bolded_text, get_colored_text, tasks_w_writes -from langgraph.pregel.draw import draw_graph -from langgraph.pregel.io import map_input, read_channels -from langgraph.pregel.loop import AsyncPregelLoop, StreamProtocol, SyncPregelLoop -from langgraph.pregel.messages import StreamMessagesHandler -from langgraph.pregel.protocol import PregelProtocol -from langgraph.pregel.read import DEFAULT_BOUND, PregelNode -from langgraph.pregel.retry import RetryPolicy -from langgraph.pregel.runner import PregelRunner -from langgraph.pregel.utils import get_new_channel_versions -from langgraph.pregel.validate import validate_graph, validate_keys -from langgraph.pregel.write import ChannelWrite, ChannelWriteEntry -from langgraph.store.base import BaseStore -from langgraph.types import ( - All, - CachePolicy, - Checkpointer, - Interrupt, - Send, - StateSnapshot, - StateUpdate, - StreamChunk, - StreamMode, -) -from langgraph.typing import InputT, OutputT, StateT -from langgraph.utils.config import ( - ensure_config, - merge_configs, - patch_checkpoint_map, - patch_config, - patch_configurable, - recast_checkpoint_ns, -) -from langgraph.utils.pydantic import create_model -from langgraph.utils.queue import AsyncQueue, SyncQueue # type: ignore[attr-defined] -from langgraph.utils.runnable import ( - Runnable, - RunnableLike, - RunnableSeq, - coerce_to_runnable, -) - -try: - from langchain_core.tracers._streaming import _StreamingCallbackHandler -except ImportError: - _StreamingCallbackHandler = None # type: ignore - -WriteValue = Union[Callable[[Input], Output], Any] - - -class NodeBuilder: - __slots__ = ( - "_channels", - "_triggers", - "_tags", - "_metadata", - "_writes", - "_bound", - "_retry_policy", - "_cache_policy", - ) - - _channels: str | list[str] - _triggers: list[str] - _tags: list[str] - _metadata: dict[str, Any] - _writes: list[ChannelWriteEntry] - _bound: Runnable - _retry_policy: list[RetryPolicy] - _cache_policy: CachePolicy | None - - def __init__( - self, - ) -> None: - self._channels = [] - self._triggers = [] - self._tags = [] - self._metadata = {} - self._writes = [] - self._bound = DEFAULT_BOUND - self._retry_policy = [] - self._cache_policy = None - - def subscribe_only( - self, - channel: str, - ) -> Self: - """Subscribe to a single channel.""" - if not self._channels: - self._channels = channel - else: - raise ValueError( - "Cannot subscribe to single channels when other channels are already subscribed to" - ) - - self._triggers.append(channel) - - return self - - def subscribe_to( - self, - *channels: str, - read: bool = True, - ) -> Self: - """Add channels to subscribe to. Node will be invoked when any of these - channels are updated, with a dict of the channel values as input. - - Args: - channels: Channel name(s) to subscribe to - read: If True, the channels will be included in the input to the node. - Otherwise, they will trigger the node without being sent in input. - - Returns: - Self for chaining - """ - if isinstance(self._channels, str): - raise ValueError( - "Cannot subscribe to channels when subscribed to a single channel" - ) - if read: - if not self._channels: - self._channels = list(channels) - else: - self._channels.extend(channels) - - if isinstance(channels, str): - self._triggers.append(channels) - else: - self._triggers.extend(channels) - - return self - - def read_from( - self, - *channels: str, - ) -> Self: - """Adds the specified channels to read from, without subscribing to them.""" - assert isinstance(self._channels, list), ( - "Cannot read additional channels when subscribed to single channels" - ) - self._channels.extend(channels) - return self - - def do( - self, - node: RunnableLike, - ) -> Self: - """Adds the specified node.""" - if self._bound is not DEFAULT_BOUND: - self._bound = RunnableSeq( - self._bound, coerce_to_runnable(node, name=None, trace=True) - ) - else: - self._bound = coerce_to_runnable(node, name=None, trace=True) - return self - - def write_to( - self, - *channels: str | ChannelWriteEntry, - **kwargs: WriteValue, - ) -> Self: - """Add channel writes. - - Args: - *channels: Channel names to write to - **kwargs: Channel name and value mappings - - Returns: - Self for chaining - """ - self._writes.extend( - ChannelWriteEntry(c) if isinstance(c, str) else c for c in channels - ) - self._writes.extend( - ChannelWriteEntry(k, mapper=v) - if callable(v) - else ChannelWriteEntry(k, value=v) - for k, v in kwargs.items() - ) - - return self - - def meta(self, *tags: str, **metadata: Any) -> Self: - """Add tags or metadata to the node.""" - self._tags.extend(tags) - self._metadata.update(metadata) - return self - - def add_retry_policies(self, *policies: RetryPolicy) -> Self: - """Adds retry policies to the node.""" - self._retry_policy.extend(policies) - return self - - def add_cache_policy(self, policy: CachePolicy) -> Self: - """Adds cache policies to the node.""" - self._cache_policy = policy - return self - - def build(self) -> PregelNode: - """Builds the node.""" - return PregelNode( - channels=self._channels, - triggers=self._triggers, - tags=self._tags, - metadata=self._metadata, - writers=[ChannelWrite(self._writes)], - bound=self._bound, - retry_policy=self._retry_policy, - cache_policy=self._cache_policy, - ) - - -class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, OutputT]): - """Pregel manages the runtime behavior for LangGraph applications. - - ## Overview - - Pregel combines [**actors**](https://en.wikipedia.org/wiki/Actor_model) - and **channels** into a single application. - **Actors** read data from channels and write data to channels. - Pregel organizes the execution of the application into multiple steps, - following the **Pregel Algorithm**/**Bulk Synchronous Parallel** model. - - Each step consists of three phases: - - - **Plan**: Determine which **actors** to execute in this step. For example, - in the first step, select the **actors** that subscribe to the special - **input** channels; in subsequent steps, - select the **actors** that subscribe to channels updated in the previous step. - - **Execution**: Execute all selected **actors** in parallel, - until all complete, or one fails, or a timeout is reached. During this - phase, channel updates are invisible to actors until the next step. - - **Update**: Update the channels with the values written by the **actors** - in this step. - - Repeat until no **actors** are selected for execution, or a maximum number of - steps is reached. - - ## Actors - - An **actor** is a `PregelNode`. - It subscribes to channels, reads data from them, and writes data to them. - It can be thought of as an **actor** in the Pregel algorithm. - `PregelNodes` implement LangChain's - Runnable interface. - - ## Channels - - Channels are used to communicate between actors (`PregelNodes`). - Each channel has a value type, an update type, and an update function – which - takes a sequence of updates and - modifies the stored value. Channels can be used to send data from one chain to - another, or to send data from a chain to itself in a future step. LangGraph - provides a number of built-in channels: - - ### Basic channels: LastValue and Topic - - - `LastValue`: The default channel, stores the last value sent to the channel, - useful for input and output values, or for sending data from one step to the next - - `Topic`: A configurable PubSub Topic, useful for sending multiple values - between *actors*, or for accumulating output. Can be configured to deduplicate - values, and/or to accumulate values over the course of multiple steps. - - ### Advanced channels: Context and BinaryOperatorAggregate - - - `Context`: exposes the value of a context manager, managing its lifecycle. - Useful for accessing external resources that require setup and/or teardown. eg. - `client = Context(httpx.Client)` - - `BinaryOperatorAggregate`: stores a persistent value, updated by applying - a binary operator to the current value and each update - sent to the channel, useful for computing aggregates over multiple steps. eg. - `total = BinaryOperatorAggregate(int, operator.add)` - - ## Examples - - Most users will interact with Pregel via a - [StateGraph (Graph API)][langgraph.graph.StateGraph] or via an - [entrypoint (Functional API)][langgraph.func.entrypoint]. - - However, for **advanced** use cases, Pregel can be used directly. If you're - not sure whether you need to use Pregel directly, then the answer is probably no - – you should use the Graph API or Functional API instead. These are higher-level - interfaces that will compile down to Pregel under the hood. - - Here are some examples to give you a sense of how it works: - - Example: Single node application - ```python - from langgraph.channels import EphemeralValue - from langgraph.pregel import Pregel, NodeBuilder - - node1 = ( - NodeBuilder().subscribe_only("a") - .do(lambda x: x + x) - .write_to("b") - ) - - app = Pregel( - nodes={"node1": node1}, - channels={ - "a": EphemeralValue(str), - "b": EphemeralValue(str), - }, - input_channels=["a"], - output_channels=["b"], - ) - - app.invoke({"a": "foo"}) - ``` - - ```con - {'b': 'foofoo'} - ``` - - Example: Using multiple nodes and multiple output channels - ```python - from langgraph.channels import LastValue, EphemeralValue - from langgraph.pregel import Pregel, NodeBuilder - - node1 = ( - NodeBuilder().subscribe_only("a") - .do(lambda x: x + x) - .write_to("b") - ) - - node2 = ( - NodeBuilder().subscribe_to("b") - .do(lambda x: x["b"] + x["b"]) - .write_to("c") - ) - - - app = Pregel( - nodes={"node1": node1, "node2": node2}, - channels={ - "a": EphemeralValue(str), - "b": LastValue(str), - "c": EphemeralValue(str), - }, - input_channels=["a"], - output_channels=["b", "c"], - ) - - app.invoke({"a": "foo"}) - ``` - - ```con - {'b': 'foofoo', 'c': 'foofoofoofoo'} - ``` - - Example: Using a Topic channel - ```python - from langgraph.channels import LastValue, EphemeralValue, Topic - from langgraph.pregel import Pregel, NodeBuilder - - node1 = ( - NodeBuilder().subscribe_only("a") - .do(lambda x: x + x) - .write_to("b", "c") - ) - - node2 = ( - NodeBuilder().subscribe_only("b") - .do(lambda x: x + x) - .write_to("c") - ) - - - app = Pregel( - nodes={"node1": node1, "node2": node2}, - channels={ - "a": EphemeralValue(str), - "b": EphemeralValue(str), - "c": Topic(str, accumulate=True), - }, - input_channels=["a"], - output_channels=["c"], - ) - - app.invoke({"a": "foo"}) - ``` - - ```pycon - {'c': ['foofoo', 'foofoofoofoo']} - ``` - - Example: Using a BinaryOperatorAggregate channel - ```python - from langgraph.channels import EphemeralValue, BinaryOperatorAggregate - from langgraph.pregel import Pregel, NodeBuilder - - - node1 = ( - NodeBuilder().subscribe_only("a") - .do(lambda x: x + x) - .write_to("b", "c") - ) - - node2 = ( - NodeBuilder().subscribe_only("b") - .do(lambda x: x + x) - .write_to("c") - ) - - - def reducer(current, update): - if current: - return current + " | " + update - else: - return update - - app = Pregel( - nodes={"node1": node1, "node2": node2}, - channels={ - "a": EphemeralValue(str), - "b": EphemeralValue(str), - "c": BinaryOperatorAggregate(str, operator=reducer), - }, - input_channels=["a"], - output_channels=["c"] - ) - - app.invoke({"a": "foo"}) - ``` - - ```con - {'c': 'foofoo | foofoofoofoo'} - ``` - - Example: Introducing a cycle - This example demonstrates how to introduce a cycle in the graph, by having - a chain write to a channel it subscribes to. Execution will continue - until a None value is written to the channel. - - ```python - from langgraph.channels import EphemeralValue - from langgraph.pregel import Pregel, NodeBuilder, ChannelWriteEntry - - example_node = ( - NodeBuilder().subscribe_only("value") - .do(lambda x: x + x if len(x) < 10 else None) - .write_to(ChannelWriteEntry(channel="value", skip_none=True)) - ) - - app = Pregel( - nodes={"example_node": example_node}, - channels={ - "value": EphemeralValue(str), - }, - input_channels=["value"], - output_channels=["value"] - ) - - app.invoke({"value": "a"}) - ``` - - ```con - {'value': 'aaaaaaaaaaaaaaaa'} - ``` - """ - - nodes: dict[str, PregelNode] - - channels: dict[str, BaseChannel | ManagedValueSpec] - - stream_mode: StreamMode = "values" - """Mode to stream output, defaults to 'values'.""" - - stream_eager: bool = False - """Whether to force emitting stream events eagerly, automatically turned on - for stream_mode "messages" and "custom".""" - - output_channels: str | Sequence[str] - - stream_channels: str | Sequence[str] | None = None - """Channels to stream, defaults to all channels not in reserved channels""" - - interrupt_after_nodes: All | Sequence[str] - - interrupt_before_nodes: All | Sequence[str] - - input_channels: str | Sequence[str] - - step_timeout: float | None = None - """Maximum time to wait for a step to complete, in seconds. Defaults to None.""" - - debug: bool - """Whether to print debug information during execution. Defaults to False.""" - - checkpointer: Checkpointer = None - """Checkpointer used to save and load graph state. Defaults to None.""" - - store: BaseStore | None = None - """Memory store to use for SharedValues. Defaults to None.""" - - cache: BaseCache | None = None - """Cache to use for storing node results. Defaults to None.""" - - retry_policy: Sequence[RetryPolicy] = () - """Retry policies to use when running tasks. Empty set disables retries.""" - - cache_policy: CachePolicy | None = None - """Cache policy to use for all nodes. Can be overridden by individual nodes. - Defaults to None.""" - - config_type: type[Any] | None = None - - config: RunnableConfig | None = None - - name: str = "LangGraph" - - trigger_to_nodes: Mapping[str, Sequence[str]] - - def __init__( - self, - *, - nodes: dict[str, PregelNode | NodeBuilder], - channels: dict[str, BaseChannel | ManagedValueSpec] | None, - auto_validate: bool = True, - stream_mode: StreamMode = "values", - stream_eager: bool = False, - output_channels: str | Sequence[str], - stream_channels: str | Sequence[str] | None = None, - interrupt_after_nodes: All | Sequence[str] = (), - interrupt_before_nodes: All | Sequence[str] = (), - input_channels: str | Sequence[str], - step_timeout: float | None = None, - debug: bool | None = None, - checkpointer: BaseCheckpointSaver | None = None, - store: BaseStore | None = None, - cache: BaseCache | None = None, - retry_policy: RetryPolicy | Sequence[RetryPolicy] = (), - cache_policy: CachePolicy | None = None, - config_type: type[Any] | None = None, - config: RunnableConfig | None = None, - trigger_to_nodes: Mapping[str, Sequence[str]] | None = None, - name: str = "LangGraph", - ) -> None: - self.nodes = { - k: v.build() if isinstance(v, NodeBuilder) else v for k, v in nodes.items() - } - self.channels = channels or {} - if TASKS in self.channels and not isinstance(self.channels[TASKS], Topic): - raise ValueError( - f"Channel '{TASKS}' is reserved and cannot be used in the graph." - ) - else: - self.channels[TASKS] = Topic(Send, accumulate=False) - self.stream_mode = stream_mode - self.stream_eager = stream_eager - self.output_channels = output_channels - self.stream_channels = stream_channels - self.interrupt_after_nodes = interrupt_after_nodes - self.interrupt_before_nodes = interrupt_before_nodes - self.input_channels = input_channels - self.step_timeout = step_timeout - self.debug = debug if debug is not None else get_debug() - self.checkpointer = checkpointer - self.store = store - self.cache = cache - self.retry_policy = ( - (retry_policy,) if isinstance(retry_policy, RetryPolicy) else retry_policy - ) - self.cache_policy = cache_policy - self.config_type = config_type - self.config = config - self.trigger_to_nodes = trigger_to_nodes or {} - self.name = name - if auto_validate: - self.validate() - - def get_graph( - self, config: RunnableConfig | None = None, *, xray: int | bool = False - ) -> Graph: - """Return a drawable representation of the computation graph.""" - # gather subgraphs - if xray: - subgraphs = { - k: v.get_graph( - config, - xray=xray if isinstance(xray, bool) or xray <= 0 else xray - 1, - ) - for k, v in self.get_subgraphs() - } - else: - subgraphs = {} - - return draw_graph( - merge_configs(self.config, config), - nodes=self.nodes, - specs=self.channels, - input_channels=self.input_channels, - interrupt_after_nodes=self.interrupt_after_nodes, - interrupt_before_nodes=self.interrupt_before_nodes, - trigger_to_nodes=self.trigger_to_nodes, - checkpointer=self.checkpointer, - subgraphs=subgraphs, - ) - - async def aget_graph( - self, config: RunnableConfig | None = None, *, xray: int | bool = False - ) -> Graph: - """Return a drawable representation of the computation graph.""" - - # gather subgraphs - if xray: - subpregels: dict[str, PregelProtocol] = { - k: v async for k, v in self.aget_subgraphs() - } - subgraphs = { - k: v - for k, v in zip( - subpregels, - await asyncio.gather( - *( - p.aget_graph( - config, - xray=xray - if isinstance(xray, bool) or xray <= 0 - else xray - 1, - ) - for p in subpregels.values() - ) - ), - ) - } - else: - subgraphs = {} - - return draw_graph( - merge_configs(self.config, config), - nodes=self.nodes, - specs=self.channels, - input_channels=self.input_channels, - interrupt_after_nodes=self.interrupt_after_nodes, - interrupt_before_nodes=self.interrupt_before_nodes, - trigger_to_nodes=self.trigger_to_nodes, - checkpointer=self.checkpointer, - subgraphs=subgraphs, - ) - - def _repr_mimebundle_(self, **kwargs: Any) -> dict[str, Any]: - """Mime bundle used by Jupyter to display the graph""" - return { - "text/plain": repr(self), - "image/png": self.get_graph().draw_mermaid_png(), - } - - def copy(self, update: dict[str, Any] | None = None) -> Self: - attrs = {k: v for k, v in self.__dict__.items() if k != "__orig_class__"} - attrs.update(update or {}) - return self.__class__(**attrs) - - def with_config(self, config: RunnableConfig | None = None, **kwargs: Any) -> Self: - """Create a copy of the Pregel object with an updated config.""" - return self.copy( - {"config": merge_configs(self.config, config, cast(RunnableConfig, kwargs))} - ) - - def validate(self) -> Self: - validate_graph( - self.nodes, - {k: v for k, v in self.channels.items() if isinstance(v, BaseChannel)}, - {k: v for k, v in self.channels.items() if not isinstance(v, BaseChannel)}, - self.input_channels, - self.output_channels, - self.stream_channels, - self.interrupt_after_nodes, - self.interrupt_before_nodes, - ) - self.trigger_to_nodes = _trigger_to_nodes(self.nodes) - return self - - def config_schema(self, *, include: Sequence[str] | None = None) -> type[BaseModel]: - include = include or [] - fields = { - **({"configurable": (self.config_type, None)} if self.config_type else {}), - **{ - field_name: (field_type, None) - for field_name, field_type in get_type_hints(RunnableConfig).items() - if field_name in [i for i in include if i != "configurable"] - }, - } - return create_model(self.get_name("Config"), field_definitions=fields) - - def get_config_jsonschema( - self, *, include: Sequence[str] | None = None - ) -> dict[str, Any]: - schema = self.config_schema(include=include) - if hasattr(schema, "model_json_schema"): - return schema.model_json_schema() - else: - return schema.schema() - - @property - def InputType(self) -> Any: - if isinstance(self.input_channels, str): - channel = self.channels[self.input_channels] - if isinstance(channel, BaseChannel): - return channel.UpdateType - - def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]: - config = merge_configs(self.config, config) - if isinstance(self.input_channels, str): - return super().get_input_schema(config) - else: - return create_model( - self.get_name("Input"), - field_definitions={ - k: (c.UpdateType, None) - for k in self.input_channels or self.channels.keys() - if (c := self.channels[k]) and isinstance(c, BaseChannel) - }, - ) - - def get_input_jsonschema( - self, config: RunnableConfig | None = None - ) -> dict[str, Any]: - schema = self.get_input_schema(config) - if hasattr(schema, "model_json_schema"): - return schema.model_json_schema() - else: - return schema.schema() - - @property - def OutputType(self) -> Any: - if isinstance(self.output_channels, str): - channel = self.channels[self.output_channels] - if isinstance(channel, BaseChannel): - return channel.ValueType - - def get_output_schema( - self, config: RunnableConfig | None = None - ) -> type[BaseModel]: - config = merge_configs(self.config, config) - if isinstance(self.output_channels, str): - return super().get_output_schema(config) - else: - return create_model( - self.get_name("Output"), - field_definitions={ - k: (c.ValueType, None) - for k in self.output_channels - if (c := self.channels[k]) and isinstance(c, BaseChannel) - }, - ) - - def get_output_jsonschema( - self, config: RunnableConfig | None = None - ) -> dict[str, Any]: - schema = self.get_output_schema(config) - if hasattr(schema, "model_json_schema"): - return schema.model_json_schema() - else: - return schema.schema() - - @property - def stream_channels_list(self) -> Sequence[str]: - stream_channels = self.stream_channels_asis - return ( - [stream_channels] if isinstance(stream_channels, str) else stream_channels - ) - - @property - def stream_channels_asis(self) -> str | Sequence[str]: - return self.stream_channels or [ - k for k in self.channels if isinstance(self.channels[k], BaseChannel) - ] - - def get_subgraphs( - self, *, namespace: str | None = None, recurse: bool = False - ) -> Iterator[tuple[str, PregelProtocol]]: - """Get the subgraphs of the graph. - - Args: - namespace: The namespace to filter the subgraphs by. - recurse: Whether to recurse into the subgraphs. - If False, only the immediate subgraphs will be returned. - - Returns: - Iterator[tuple[str, PregelProtocol]]: An iterator of the (namespace, subgraph) pairs. - """ - for name, node in self.nodes.items(): - # filter by prefix - if namespace is not None: - if not namespace.startswith(name): - continue - - # find the subgraph, if any - graph = node.subgraphs[0] if node.subgraphs else None - - # if found, yield recursively - if graph: - if name == namespace: - yield name, graph - return # we found it, stop searching - if namespace is None: - yield name, graph - if recurse and isinstance(graph, Pregel): - if namespace is not None: - namespace = namespace[len(name) + 1 :] - yield from ( - (f"{name}{NS_SEP}{n}", s) - for n, s in graph.get_subgraphs( - namespace=namespace, recurse=recurse - ) - ) - - async def aget_subgraphs( - self, *, namespace: str | None = None, recurse: bool = False - ) -> AsyncIterator[tuple[str, PregelProtocol]]: - """Get the subgraphs of the graph. - - Args: - namespace: The namespace to filter the subgraphs by. - recurse: Whether to recurse into the subgraphs. - If False, only the immediate subgraphs will be returned. - - Returns: - AsyncIterator[tuple[str, PregelProtocol]]: An iterator of the (namespace, subgraph) pairs. - """ - for name, node in self.get_subgraphs(namespace=namespace, recurse=recurse): - yield name, node - - def _migrate_checkpoint(self, checkpoint: Checkpoint) -> None: - """Migrate a saved checkpoint to new channel layout.""" - if checkpoint["v"] < 4 and checkpoint.get("pending_sends"): - pending_sends: list[Send] = checkpoint.pop("pending_sends") - checkpoint["channel_values"][TASKS] = pending_sends - checkpoint["channel_versions"][TASKS] = max( - checkpoint["channel_versions"].values() - ) - - def _prepare_state_snapshot( - self, - config: RunnableConfig, - saved: CheckpointTuple | None, - recurse: BaseCheckpointSaver | None = None, - apply_pending_writes: bool = False, - ) -> StateSnapshot: - if not saved: - return StateSnapshot( - values={}, - next=(), - config=config, - metadata=None, - created_at=None, - parent_config=None, - tasks=(), - interrupts=(), - ) - - # migrate checkpoint if needed - self._migrate_checkpoint(saved.checkpoint) - - step = saved.metadata.get("step", -1) + 1 - stop = step + 2 - channels, managed = channels_from_checkpoint( - self.channels, - saved.checkpoint, - ) - # tasks for this checkpoint - next_tasks = prepare_next_tasks( - saved.checkpoint, - saved.pending_writes or [], - self.nodes, - channels, - managed, - saved.config, - step, - stop, - for_execution=True, - store=self.store, - checkpointer=( - self.checkpointer - if isinstance(self.checkpointer, BaseCheckpointSaver) - else None - ), - manager=None, - ) - # get the subgraphs - subgraphs = dict(self.get_subgraphs()) - parent_ns = saved.config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - task_states: dict[str, RunnableConfig | StateSnapshot] = {} - for task in next_tasks.values(): - if task.name not in subgraphs: - continue - # assemble checkpoint_ns for this task - task_ns = f"{task.name}{NS_END}{task.id}" - if parent_ns: - task_ns = f"{parent_ns}{NS_SEP}{task_ns}" - if not recurse: - # set config as signal that subgraph checkpoints exist - config = { - CONF: { - "thread_id": saved.config[CONF]["thread_id"], - CONFIG_KEY_CHECKPOINT_NS: task_ns, - } - } - task_states[task.id] = config - else: - # get the state of the subgraph - config = { - CONF: { - CONFIG_KEY_CHECKPOINTER: recurse, - "thread_id": saved.config[CONF]["thread_id"], - CONFIG_KEY_CHECKPOINT_NS: task_ns, - } - } - task_states[task.id] = subgraphs[task.name].get_state( - config, subgraphs=True - ) - # apply pending writes - if null_writes := [ - w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID - ]: - apply_writes( - saved.checkpoint, - channels, - [PregelTaskWrites((), INPUT, null_writes, [])], - None, - self.trigger_to_nodes, - ) - if apply_pending_writes and saved.pending_writes: - for tid, k, v in saved.pending_writes: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) - if tasks := [t for t in next_tasks.values() if t.writes]: - apply_writes( - saved.checkpoint, channels, tasks, None, self.trigger_to_nodes - ) - tasks_with_writes = tasks_w_writes( - next_tasks.values(), - saved.pending_writes, - task_states, - self.stream_channels_asis, - ) - # assemble the state snapshot - return StateSnapshot( - read_channels(channels, self.stream_channels_asis), - tuple(t.name for t in next_tasks.values() if not t.writes), - patch_checkpoint_map(saved.config, saved.metadata), - saved.metadata, - saved.checkpoint["ts"], - patch_checkpoint_map(saved.parent_config, saved.metadata), - tasks_with_writes, - tuple([i for task in tasks_with_writes for i in task.interrupts]), - ) - - async def _aprepare_state_snapshot( - self, - config: RunnableConfig, - saved: CheckpointTuple | None, - recurse: BaseCheckpointSaver | None = None, - apply_pending_writes: bool = False, - ) -> StateSnapshot: - if not saved: - return StateSnapshot( - values={}, - next=(), - config=config, - metadata=None, - created_at=None, - parent_config=None, - tasks=(), - interrupts=(), - ) - - # migrate checkpoint if needed - self._migrate_checkpoint(saved.checkpoint) - - step = saved.metadata.get("step", -1) + 1 - stop = step + 2 - channels, managed = channels_from_checkpoint( - self.channels, - saved.checkpoint, - ) - # tasks for this checkpoint - next_tasks = prepare_next_tasks( - saved.checkpoint, - saved.pending_writes or [], - self.nodes, - channels, - managed, - saved.config, - step, - stop, - for_execution=True, - store=self.store, - checkpointer=( - self.checkpointer - if isinstance(self.checkpointer, BaseCheckpointSaver) - else None - ), - manager=None, - ) - # get the subgraphs - subgraphs = {n: g async for n, g in self.aget_subgraphs()} - parent_ns = saved.config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - task_states: dict[str, RunnableConfig | StateSnapshot] = {} - for task in next_tasks.values(): - if task.name not in subgraphs: - continue - # assemble checkpoint_ns for this task - task_ns = f"{task.name}{NS_END}{task.id}" - if parent_ns: - task_ns = f"{parent_ns}{NS_SEP}{task_ns}" - if not recurse: - # set config as signal that subgraph checkpoints exist - config = { - CONF: { - "thread_id": saved.config[CONF]["thread_id"], - CONFIG_KEY_CHECKPOINT_NS: task_ns, - } - } - task_states[task.id] = config - else: - # get the state of the subgraph - config = { - CONF: { - CONFIG_KEY_CHECKPOINTER: recurse, - "thread_id": saved.config[CONF]["thread_id"], - CONFIG_KEY_CHECKPOINT_NS: task_ns, - } - } - task_states[task.id] = await subgraphs[task.name].aget_state( - config, subgraphs=True - ) - # apply pending writes - if null_writes := [ - w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID - ]: - apply_writes( - saved.checkpoint, - channels, - [PregelTaskWrites((), INPUT, null_writes, [])], - None, - self.trigger_to_nodes, - ) - if apply_pending_writes and saved.pending_writes: - for tid, k, v in saved.pending_writes: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) - if tasks := [t for t in next_tasks.values() if t.writes]: - apply_writes( - saved.checkpoint, channels, tasks, None, self.trigger_to_nodes - ) - - tasks_with_writes = tasks_w_writes( - next_tasks.values(), - saved.pending_writes, - task_states, - self.stream_channels_asis, - ) - # assemble the state snapshot - return StateSnapshot( - read_channels(channels, self.stream_channels_asis), - tuple(t.name for t in next_tasks.values() if not t.writes), - patch_checkpoint_map(saved.config, saved.metadata), - saved.metadata, - saved.checkpoint["ts"], - patch_checkpoint_map(saved.parent_config, saved.metadata), - tasks_with_writes, - tuple([i for task in tasks_with_writes for i in task.interrupts]), - ) - - def get_state( - self, config: RunnableConfig, *, subgraphs: bool = False - ) -> StateSnapshot: - """Get the current state of the graph.""" - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if not checkpointer: - raise ValueError("No checkpointer set") - - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): - return pregel.get_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - subgraphs=subgraphs, - ) - else: - raise ValueError(f"Subgraph {recast} not found") - - config = merge_configs(self.config, config) if self.config else config - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config = merge_configs( - config, {CONF: {CONFIG_KEY_CHECKPOINT_NS: recast_checkpoint_ns(ns)}} - ) - thread_id = config[CONF][CONFIG_KEY_THREAD_ID] - if not isinstance(thread_id, str): - config[CONF][CONFIG_KEY_THREAD_ID] = str(thread_id) - - saved = checkpointer.get_tuple(config) - return self._prepare_state_snapshot( - config, - saved, - recurse=checkpointer if subgraphs else None, - apply_pending_writes=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], - ) - - async def aget_state( - self, config: RunnableConfig, *, subgraphs: bool = False - ) -> StateSnapshot: - """Get the current state of the graph.""" - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if not checkpointer: - raise ValueError("No checkpointer set") - - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): - return await pregel.aget_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - subgraphs=subgraphs, - ) - else: - raise ValueError(f"Subgraph {recast} not found") - - config = merge_configs(self.config, config) if self.config else config - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config = merge_configs( - config, {CONF: {CONFIG_KEY_CHECKPOINT_NS: recast_checkpoint_ns(ns)}} - ) - thread_id = config[CONF][CONFIG_KEY_THREAD_ID] - if not isinstance(thread_id, str): - config[CONF][CONFIG_KEY_THREAD_ID] = str(thread_id) - - saved = await checkpointer.aget_tuple(config) - return await self._aprepare_state_snapshot( - config, - saved, - recurse=checkpointer if subgraphs else None, - apply_pending_writes=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], - ) - - def get_state_history( - self, - config: RunnableConfig, - *, - filter: dict[str, Any] | None = None, - before: RunnableConfig | None = None, - limit: int | None = None, - ) -> Iterator[StateSnapshot]: - """Get the history of the state of the graph.""" - config = ensure_config(config) - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if not checkpointer: - raise ValueError("No checkpointer set") - - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): - yield from pregel.get_state_history( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - filter=filter, - before=before, - limit=limit, - ) - return - else: - raise ValueError(f"Subgraph {recast} not found") - - config = merge_configs( - self.config, - config, - { - CONF: { - CONFIG_KEY_CHECKPOINT_NS: checkpoint_ns, - CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID]), - } - }, - ) - # eagerly consume list() to avoid holding up the db cursor - for checkpoint_tuple in list( - checkpointer.list(config, before=before, limit=limit, filter=filter) - ): - yield self._prepare_state_snapshot( - checkpoint_tuple.config, checkpoint_tuple - ) - - async def aget_state_history( - self, - config: RunnableConfig, - *, - filter: dict[str, Any] | None = None, - before: RunnableConfig | None = None, - limit: int | None = None, - ) -> AsyncIterator[StateSnapshot]: - """Asynchronously get the history of the state of the graph.""" - config = ensure_config(config) - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if not checkpointer: - raise ValueError("No checkpointer set") - - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): - async for state in pregel.aget_state_history( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - filter=filter, - before=before, - limit=limit, - ): - yield state - return - else: - raise ValueError(f"Subgraph {recast} not found") - - config = merge_configs( - self.config, - config, - { - CONF: { - CONFIG_KEY_CHECKPOINT_NS: checkpoint_ns, - CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID]), - } - }, - ) - # eagerly consume list() to avoid holding up the db cursor - for checkpoint_tuple in [ - c - async for c in checkpointer.alist( - config, before=before, limit=limit, filter=filter - ) - ]: - yield await self._aprepare_state_snapshot( - checkpoint_tuple.config, checkpoint_tuple - ) - - def bulk_update_state( - self, - config: RunnableConfig, - supersteps: Sequence[Sequence[StateUpdate]], - ) -> RunnableConfig: - """Apply updates to the graph state in bulk. Requires a checkpointer to be set. - - Args: - config: The config to apply the updates to. - supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state. - Each update is a tuple of the form `(values, as_node, task_id)` where task_id is optional. - - Raises: - ValueError: If no checkpointer is set or no updates are provided. - InvalidUpdateError: If an invalid update is provided. - - Returns: - RunnableConfig: The updated config. - """ - - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if not checkpointer: - raise ValueError("No checkpointer set") - - if len(supersteps) == 0: - raise ValueError("No supersteps provided") - - if any(len(u) == 0 for u in supersteps): - raise ValueError("No updates provided") - - # delegate to subgraph - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): - return pregel.bulk_update_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - supersteps, - ) - else: - raise ValueError(f"Subgraph {recast} not found") - - def perform_superstep( - input_config: RunnableConfig, updates: Sequence[StateUpdate] - ) -> RunnableConfig: - # get last checkpoint - config = ensure_config(self.config, input_config) - saved = checkpointer.get_tuple(config) - if saved is not None: - self._migrate_checkpoint(saved.checkpoint) - checkpoint = ( - copy_checkpoint(saved.checkpoint) if saved else empty_checkpoint() - ) - checkpoint_previous_versions = ( - saved.checkpoint["channel_versions"].copy() if saved else {} - ) - step = saved.metadata.get("step", -1) if saved else -1 - # merge configurable fields with previous checkpoint config - checkpoint_config = patch_configurable( - config, - { - CONFIG_KEY_CHECKPOINT_NS: config[CONF].get( - CONFIG_KEY_CHECKPOINT_NS, "" - ) - }, - ) - if saved: - checkpoint_config = patch_configurable(config, saved.config[CONF]) - channels, managed = channels_from_checkpoint( - self.channels, - checkpoint, - ) - values, as_node = updates[0][:2] - - # no values as END, just clear all tasks - if values is None and as_node == END: - if len(updates) > 1: - raise InvalidUpdateError( - "Cannot apply multiple updates when clearing state" - ) - - if saved is not None: - # tasks for this checkpoint - next_tasks = prepare_next_tasks( - checkpoint, - saved.pending_writes or [], - self.nodes, - channels, - managed, - saved.config, - step + 1, - step + 3, - for_execution=True, - store=self.store, - checkpointer=checkpointer, - manager=None, - ) - # apply null writes - if null_writes := [ - w[1:] - for w in saved.pending_writes or [] - if w[0] == NULL_TASK_ID - ]: - apply_writes( - checkpoint, - channels, - [PregelTaskWrites((), INPUT, null_writes, [])], - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - # apply writes from tasks that already ran - for tid, k, v in saved.pending_writes or []: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) - # clear all current tasks - apply_writes( - checkpoint, - channels, - next_tasks.values(), - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - # save checkpoint - next_config = checkpointer.put( - checkpoint_config, - create_checkpoint(checkpoint, channels, step), - { - "source": "update", - "step": step + 1, - "parents": saved.metadata.get("parents", {}) if saved else {}, - }, - get_new_channel_versions( - checkpoint_previous_versions, - checkpoint["channel_versions"], - ), - ) - return patch_checkpoint_map( - next_config, saved.metadata if saved else None - ) - - # act as an input - if as_node == INPUT: - if len(updates) > 1: - raise InvalidUpdateError( - "Cannot apply multiple updates when updating as input" - ) - - if input_writes := deque(map_input(self.input_channels, values)): - apply_writes( - checkpoint, - channels, - [PregelTaskWrites((), INPUT, input_writes, [])], - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - - # apply input write to channels - next_step = ( - step + 1 - if saved and saved.metadata.get("step") is not None - else -1 - ) - next_config = checkpointer.put( - checkpoint_config, - create_checkpoint(checkpoint, channels, next_step), - { - "source": "input", - "step": next_step, - "parents": saved.metadata.get("parents", {}) - if saved - else {}, - }, - get_new_channel_versions( - checkpoint_previous_versions, - checkpoint["channel_versions"], - ), - ) - - # store the writes - checkpointer.put_writes( - next_config, - input_writes, - str(uuid5(UUID(checkpoint["id"]), INPUT)), - ) - - return patch_checkpoint_map( - next_config, saved.metadata if saved else None - ) - else: - raise InvalidUpdateError( - f"Received no input writes for {self.input_channels}" - ) - - # copy checkpoint - if as_node == "__copy__": - if len(updates) > 1: - raise InvalidUpdateError( - "Cannot copy checkpoint with multiple updates" - ) - - if saved is None: - raise InvalidUpdateError("Cannot copy a non-existent checkpoint") - - next_checkpoint = create_checkpoint(checkpoint, None, step) - - # copy checkpoint - next_config = checkpointer.put( - saved.parent_config - or patch_configurable( - saved.config, {CONFIG_KEY_CHECKPOINT_ID: None} - ), - next_checkpoint, - { - "source": "fork", - "step": step + 1, - "parents": saved.metadata.get("parents", {}), - }, - {}, - ) - - # we want to both clone a checkpoint and update state in one go. - # reuse the same task ID if possible. - if isinstance(values, list) and len(values) > 0: - # figure out the task IDs for the next update checkpoint - next_tasks = prepare_next_tasks( - next_checkpoint, - saved.pending_writes or [], - self.nodes, - channels, - managed, - next_config, - step + 2, - step + 4, - for_execution=True, - store=self.store, - checkpointer=checkpointer, - manager=None, - ) - - tasks_group_by = defaultdict(list) - user_group_by: dict[str, list[StateUpdate]] = defaultdict(list) - - for task in next_tasks.values(): - tasks_group_by[task.name].append(task.id) - - for item in values: - if not isinstance(item, Sequence): - raise InvalidUpdateError( - f"Invalid update item: {item} when copying checkpoint" - ) - - values, as_node = item[:2] - - user_group = user_group_by[as_node] - tasks_group = tasks_group_by[as_node] - - target_idx = len(user_group) - task_id = ( - tasks_group[target_idx] - if target_idx < len(tasks_group) - else None - ) - - user_group_by[as_node].append( - StateUpdate(values=values, as_node=as_node, task_id=task_id) - ) - - return perform_superstep( - patch_checkpoint_map(next_config, saved.metadata), - [item for lst in user_group_by.values() for item in lst], - ) - - return patch_checkpoint_map(next_config, saved.metadata) - - # apply pending writes, if not on specific checkpoint - if ( - CONFIG_KEY_CHECKPOINT_ID not in config[CONF] - and saved is not None - and saved.pending_writes - ): - # tasks for this checkpoint - next_tasks = prepare_next_tasks( - checkpoint, - saved.pending_writes, - self.nodes, - channels, - managed, - saved.config, - step + 1, - step + 3, - for_execution=True, - store=self.store, - checkpointer=checkpointer, - manager=None, - ) - # apply null writes - if null_writes := [ - w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID - ]: - apply_writes( - checkpoint, - channels, - [PregelTaskWrites((), INPUT, null_writes, [])], - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - # apply writes - for tid, k, v in saved.pending_writes: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) - if tasks := [t for t in next_tasks.values() if t.writes]: - apply_writes( - checkpoint, - channels, - tasks, - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - valid_updates: list[tuple[str, dict[str, Any] | None, str | None]] = [] - if len(updates) == 1: - values, as_node, task_id = updates[0] - # find last node that updated the state, if not provided - if as_node is None and len(self.nodes) == 1: - as_node = tuple(self.nodes)[0] - elif as_node is None and not any( - v - for vv in checkpoint["versions_seen"].values() - for v in vv.values() - ): - if ( - isinstance(self.input_channels, str) - and self.input_channels in self.nodes - ): - as_node = self.input_channels - elif as_node is None: - last_seen_by_node = sorted( - (v, n) - for n, seen in checkpoint["versions_seen"].items() - if n in self.nodes - for v in seen.values() - ) - # if two nodes updated the state at the same time, it's ambiguous - if last_seen_by_node: - if len(last_seen_by_node) == 1: - as_node = last_seen_by_node[0][1] - elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]: - as_node = last_seen_by_node[-1][1] - if as_node is None: - raise InvalidUpdateError("Ambiguous update, specify as_node") - if as_node not in self.nodes: - raise InvalidUpdateError(f"Node {as_node} does not exist") - valid_updates.append((as_node, values, task_id)) - else: - for values, as_node, task_id in updates: - if as_node is None: - raise InvalidUpdateError( - "as_node is required when applying multiple updates" - ) - if as_node not in self.nodes: - raise InvalidUpdateError(f"Node {as_node} does not exist") - - valid_updates.append((as_node, values, task_id)) - - run_tasks: list[PregelTaskWrites] = [] - run_task_ids: list[str] = [] - - for as_node, values, provided_task_id in valid_updates: - # create task to run all writers of the chosen node - writers = self.nodes[as_node].flat_writers - if not writers: - raise InvalidUpdateError(f"Node {as_node} has no writers") - writes: deque[tuple[str, Any]] = deque() - task = PregelTaskWrites((), as_node, writes, [INTERRUPT]) - task_id = provided_task_id or str( - uuid5(UUID(checkpoint["id"]), INTERRUPT) - ) - run_tasks.append(task) - run_task_ids.append(task_id) - run = RunnableSequence(*writers) if len(writers) > 1 else writers[0] - # execute task - run.invoke( - values, - patch_config( - config, - run_name=self.name + "UpdateState", - configurable={ - # deque.extend is thread-safe - CONFIG_KEY_SEND: writes.extend, - CONFIG_KEY_TASK_ID: task_id, - CONFIG_KEY_READ: partial( - local_read, - _scratchpad( - None, - [], - task_id, - "", - None, - step, - step + 2, - ), - channels, - managed, - task, - ), - }, - ), - ) - # save task writes - for task_id, task in zip(run_task_ids, run_tasks): - # channel writes are saved to current checkpoint - channel_writes = [w for w in task.writes if w[0] != PUSH] - if saved and channel_writes: - checkpointer.put_writes(checkpoint_config, channel_writes, task_id) - # apply to checkpoint and save - apply_writes( - checkpoint, - channels, - run_tasks, - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - checkpoint = create_checkpoint(checkpoint, channels, step + 1) - next_config = checkpointer.put( - checkpoint_config, - checkpoint, - { - "source": "update", - "step": step + 1, - "parents": saved.metadata.get("parents", {}) if saved else {}, - }, - get_new_channel_versions( - checkpoint_previous_versions, checkpoint["channel_versions"] - ), - ) - for task_id, task in zip(run_task_ids, run_tasks): - # save push writes - if push_writes := [w for w in task.writes if w[0] == PUSH]: - checkpointer.put_writes(next_config, push_writes, task_id) - - return patch_checkpoint_map(next_config, saved.metadata if saved else None) - - current_config = patch_configurable( - config, {CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID])} - ) - for superstep in supersteps: - current_config = perform_superstep(current_config, superstep) - return current_config - - async def abulk_update_state( - self, - config: RunnableConfig, - supersteps: Sequence[Sequence[StateUpdate]], - ) -> RunnableConfig: - """Asynchronously apply updates to the graph state in bulk. Requires a checkpointer to be set. - - Args: - config: The config to apply the updates to. - supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state. - Each update is a tuple of the form `(values, as_node, task_id)` where task_id is optional. - - Raises: - ValueError: If no checkpointer is set or no updates are provided. - InvalidUpdateError: If an invalid update is provided. - - Returns: - RunnableConfig: The updated config. - """ - - checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( - CONFIG_KEY_CHECKPOINTER, self.checkpointer - ) - if not checkpointer: - raise ValueError("No checkpointer set") - - if len(supersteps) == 0: - raise ValueError("No supersteps provided") - - if any(len(u) == 0 for u in supersteps): - raise ValueError("No updates provided") - - # delegate to subgraph - if ( - checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") - ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: - # remove task_ids from checkpoint_ns - recast = recast_checkpoint_ns(checkpoint_ns) - # find the subgraph with the matching name - async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): - return await pregel.abulk_update_state( - patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), - supersteps, - ) - else: - raise ValueError(f"Subgraph {recast} not found") - - async def aperform_superstep( - input_config: RunnableConfig, updates: Sequence[StateUpdate] - ) -> RunnableConfig: - # get last checkpoint - config = ensure_config(self.config, input_config) - saved = await checkpointer.aget_tuple(config) - if saved is not None: - self._migrate_checkpoint(saved.checkpoint) - checkpoint = ( - copy_checkpoint(saved.checkpoint) if saved else empty_checkpoint() - ) - checkpoint_previous_versions = ( - saved.checkpoint["channel_versions"].copy() if saved else {} - ) - step = saved.metadata.get("step", -1) if saved else -1 - # merge configurable fields with previous checkpoint config - checkpoint_config = patch_configurable( - config, - { - CONFIG_KEY_CHECKPOINT_NS: config[CONF].get( - CONFIG_KEY_CHECKPOINT_NS, "" - ) - }, - ) - if saved: - checkpoint_config = patch_configurable(config, saved.config[CONF]) - channels, managed = channels_from_checkpoint( - self.channels, - checkpoint, - ) - values, as_node = updates[0][:2] - # no values, just clear all tasks - if values is None and as_node == END: - if len(updates) > 1: - raise InvalidUpdateError( - "Cannot apply multiple updates when clearing state" - ) - if saved is not None: - # tasks for this checkpoint - next_tasks = prepare_next_tasks( - checkpoint, - saved.pending_writes or [], - self.nodes, - channels, - managed, - saved.config, - step + 1, - step + 3, - for_execution=True, - store=self.store, - checkpointer=checkpointer, - manager=None, - ) - # apply null writes - if null_writes := [ - w[1:] - for w in saved.pending_writes or [] - if w[0] == NULL_TASK_ID - ]: - apply_writes( - checkpoint, - channels, - [PregelTaskWrites((), INPUT, null_writes, [])], - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - # apply writes from tasks that already ran - for tid, k, v in saved.pending_writes or []: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) - # clear all current tasks - apply_writes( - checkpoint, - channels, - next_tasks.values(), - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - # save checkpoint - next_config = await checkpointer.aput( - checkpoint_config, - create_checkpoint(checkpoint, channels, step), - { - "source": "update", - "step": step + 1, - "parents": saved.metadata.get("parents", {}) if saved else {}, - }, - get_new_channel_versions( - checkpoint_previous_versions, checkpoint["channel_versions"] - ), - ) - return patch_checkpoint_map( - next_config, saved.metadata if saved else None - ) - - # act as an input - if as_node == INPUT: - if len(updates) > 1: - raise InvalidUpdateError( - "Cannot apply multiple updates when updating as input" - ) - - if input_writes := deque(map_input(self.input_channels, values)): - apply_writes( - checkpoint, - channels, - [PregelTaskWrites((), INPUT, input_writes, [])], - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - - # apply input write to channels - next_step = ( - step + 1 - if saved and saved.metadata.get("step") is not None - else -1 - ) - next_config = await checkpointer.aput( - checkpoint_config, - create_checkpoint(checkpoint, channels, next_step), - { - "source": "input", - "step": next_step, - "parents": saved.metadata.get("parents", {}) - if saved - else {}, - }, - get_new_channel_versions( - checkpoint_previous_versions, - checkpoint["channel_versions"], - ), - ) - - # store the writes - await checkpointer.aput_writes( - next_config, - input_writes, - str(uuid5(UUID(checkpoint["id"]), INPUT)), - ) - - return patch_checkpoint_map( - next_config, saved.metadata if saved else None - ) - else: - raise InvalidUpdateError( - f"Received no input writes for {self.input_channels}" - ) - - # no values, copy checkpoint - if as_node == "__copy__": - if len(updates) > 1: - raise InvalidUpdateError( - "Cannot copy checkpoint with multiple updates" - ) - - if saved is None: - raise InvalidUpdateError("Cannot copy a non-existent checkpoint") - - next_checkpoint = create_checkpoint(checkpoint, None, step) - - # copy checkpoint - next_config = await checkpointer.aput( - saved.parent_config - or patch_configurable( - saved.config, {CONFIG_KEY_CHECKPOINT_ID: None} - ), - next_checkpoint, - { - "source": "fork", - "step": step + 1, - "parents": saved.metadata.get("parents", {}), - }, - {}, - ) - - # we want to both clone a checkpoint and update state in one go. - # reuse the same task ID if possible. - if isinstance(values, list) and len(values) > 0: - # figure out the task IDs for the next update checkpoint - next_tasks = prepare_next_tasks( - next_checkpoint, - saved.pending_writes or [], - self.nodes, - channels, - managed, - next_config, - step + 2, - step + 4, - for_execution=True, - store=self.store, - checkpointer=checkpointer, - manager=None, - ) - - tasks_group_by = defaultdict(list) - user_group_by: dict[str, list[StateUpdate]] = defaultdict(list) - - for task in next_tasks.values(): - tasks_group_by[task.name].append(task.id) - - for item in values: - if not isinstance(item, Sequence): - raise InvalidUpdateError( - f"Invalid update item: {item} when copying checkpoint" - ) - - values, as_node = item[:2] - user_group = user_group_by[as_node] - tasks_group = tasks_group_by[as_node] - - target_idx = len(user_group) - task_id = ( - tasks_group[target_idx] - if target_idx < len(tasks_group) - else None - ) - - user_group_by[as_node].append( - StateUpdate(values=values, as_node=as_node, task_id=task_id) - ) - - return await aperform_superstep( - patch_checkpoint_map(next_config, saved.metadata), - [item for lst in user_group_by.values() for item in lst], - ) - - return patch_checkpoint_map( - next_config, saved.metadata if saved else None - ) - # apply pending writes, if not on specific checkpoint - if ( - CONFIG_KEY_CHECKPOINT_ID not in config[CONF] - and saved is not None - and saved.pending_writes - ): - # tasks for this checkpoint - next_tasks = prepare_next_tasks( - checkpoint, - saved.pending_writes, - self.nodes, - channels, - managed, - saved.config, - step + 1, - step + 3, - for_execution=True, - store=self.store, - checkpointer=checkpointer, - manager=None, - ) - # apply null writes - if null_writes := [ - w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID - ]: - apply_writes( - checkpoint, - channels, - [PregelTaskWrites((), INPUT, null_writes, [])], - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - for tid, k, v in saved.pending_writes: - if k in (ERROR, INTERRUPT): - continue - if tid not in next_tasks: - continue - next_tasks[tid].writes.append((k, v)) - if tasks := [t for t in next_tasks.values() if t.writes]: - apply_writes( - checkpoint, - channels, - tasks, - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - valid_updates: list[tuple[str, dict[str, Any] | None, str | None]] = [] - if len(updates) == 1: - values, as_node, task_id = updates[0] - # find last node that updated the state, if not provided - if as_node is None and len(self.nodes) == 1: - as_node = tuple(self.nodes)[0] - elif as_node is None and not saved: - if ( - isinstance(self.input_channels, str) - and self.input_channels in self.nodes - ): - as_node = self.input_channels - elif as_node is None: - last_seen_by_node = sorted( - (v, n) - for n, seen in checkpoint["versions_seen"].items() - if n in self.nodes - for v in seen.values() - ) - # if two nodes updated the state at the same time, it's ambiguous - if last_seen_by_node: - if len(last_seen_by_node) == 1: - as_node = last_seen_by_node[0][1] - elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]: - as_node = last_seen_by_node[-1][1] - if as_node is None: - raise InvalidUpdateError("Ambiguous update, specify as_node") - if as_node not in self.nodes: - raise InvalidUpdateError(f"Node {as_node} does not exist") - valid_updates.append((as_node, values, task_id)) - else: - for values, as_node, task_id in updates: - if as_node is None: - raise InvalidUpdateError( - "as_node is required when applying multiple updates" - ) - if as_node not in self.nodes: - raise InvalidUpdateError(f"Node {as_node} does not exist") - - valid_updates.append((as_node, values, task_id)) - - run_tasks: list[PregelTaskWrites] = [] - run_task_ids: list[str] = [] - - for as_node, values, provided_task_id in valid_updates: - # create task to run all writers of the chosen node - writers = self.nodes[as_node].flat_writers - if not writers: - raise InvalidUpdateError(f"Node {as_node} has no writers") - writes: deque[tuple[str, Any]] = deque() - task = PregelTaskWrites((), as_node, writes, [INTERRUPT]) - task_id = provided_task_id or str( - uuid5(UUID(checkpoint["id"]), INTERRUPT) - ) - run_tasks.append(task) - run_task_ids.append(task_id) - run = RunnableSequence(*writers) if len(writers) > 1 else writers[0] - # execute task - await run.ainvoke( - values, - patch_config( - config, - run_name=self.name + "UpdateState", - configurable={ - # deque.extend is thread-safe - CONFIG_KEY_SEND: writes.extend, - CONFIG_KEY_TASK_ID: task_id, - CONFIG_KEY_READ: partial( - local_read, - _scratchpad( - None, - [], - task_id, - "", - None, - step, - step + 2, - ), - channels, - managed, - task, - ), - }, - ), - ) - # save task writes - for task_id, task in zip(run_task_ids, run_tasks): - # channel writes are saved to current checkpoint - channel_writes = [w for w in task.writes if w[0] != PUSH] - if saved and channel_writes: - await checkpointer.aput_writes( - checkpoint_config, channel_writes, task_id - ) - # apply to checkpoint and save - apply_writes( - checkpoint, - channels, - run_tasks, - checkpointer.get_next_version, - self.trigger_to_nodes, - ) - checkpoint = create_checkpoint(checkpoint, channels, step + 1) - # save checkpoint, after applying writes - next_config = await checkpointer.aput( - checkpoint_config, - checkpoint, - { - "source": "update", - "step": step + 1, - "parents": saved.metadata.get("parents", {}) if saved else {}, - }, - get_new_channel_versions( - checkpoint_previous_versions, checkpoint["channel_versions"] - ), - ) - for task_id, task in zip(run_task_ids, run_tasks): - # save push writes - if push_writes := [w for w in task.writes if w[0] == PUSH]: - await checkpointer.aput_writes(next_config, push_writes, task_id) - return patch_checkpoint_map(next_config, saved.metadata if saved else None) - - current_config = patch_configurable( - config, {CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID])} - ) - for superstep in supersteps: - current_config = await aperform_superstep(current_config, superstep) - return current_config - - def update_state( - self, - config: RunnableConfig, - values: dict[str, Any] | Any | None, - as_node: str | None = None, - task_id: str | None = None, - ) -> RunnableConfig: - """Update the state of the graph with the given values, as if they came from - node `as_node`. If `as_node` is not provided, it will be set to the last node - that updated the state, if not ambiguous. - """ - return self.bulk_update_state(config, [[StateUpdate(values, as_node, task_id)]]) - - async def aupdate_state( - self, - config: RunnableConfig, - values: dict[str, Any] | Any, - as_node: str | None = None, - task_id: str | None = None, - ) -> RunnableConfig: - """Asynchronously update the state of the graph with the given values, as if they came from - node `as_node`. If `as_node` is not provided, it will be set to the last node - that updated the state, if not ambiguous. - """ - return await self.abulk_update_state( - config, [[StateUpdate(values, as_node, task_id)]] - ) - - def _defaults( - self, - config: RunnableConfig, - *, - stream_mode: StreamMode | Sequence[StreamMode], - print_mode: StreamMode | Sequence[StreamMode], - output_keys: str | Sequence[str] | None, - interrupt_before: All | Sequence[str] | None, - interrupt_after: All | Sequence[str] | None, - ) -> tuple[ - set[StreamMode], - str | Sequence[str], - All | Sequence[str], - All | Sequence[str], - BaseCheckpointSaver | None, - BaseStore | None, - BaseCache | None, - ]: - if config["recursion_limit"] < 1: - raise ValueError("recursion_limit must be at least 1") - if output_keys is None: - output_keys = self.stream_channels_asis - else: - validate_keys(output_keys, self.channels) - interrupt_before = interrupt_before or self.interrupt_before_nodes - interrupt_after = interrupt_after or self.interrupt_after_nodes - if not isinstance(stream_mode, list): - stream_modes = {stream_mode} - else: - stream_modes = set(stream_mode) - if isinstance(print_mode, str): - stream_modes.add(print_mode) - else: - stream_modes.update(print_mode) - if self.checkpointer is False: - checkpointer: BaseCheckpointSaver | None = None - elif CONFIG_KEY_CHECKPOINTER in config.get(CONF, {}): - checkpointer = config[CONF][CONFIG_KEY_CHECKPOINTER] - elif self.checkpointer is True: - raise RuntimeError("checkpointer=True cannot be used for root graphs.") - else: - checkpointer = self.checkpointer - if checkpointer and not config.get(CONF): - raise ValueError( - "Checkpointer requires one or more of the following 'configurable' " - "keys: thread_id, checkpoint_ns, checkpoint_id" - ) - if CONFIG_KEY_STORE in config.get(CONF, {}): - store: BaseStore | None = config[CONF][CONFIG_KEY_STORE] - else: - store = self.store - if CONFIG_KEY_CACHE in config.get(CONF, {}): - cache: BaseCache | None = config[CONF][CONFIG_KEY_CACHE] - else: - cache = self.cache - return ( - stream_modes, - output_keys, - interrupt_before, - interrupt_after, - checkpointer, - store, - cache, - ) - - def stream( - self, - input: InputT, - config: RunnableConfig | None = None, - *, - stream_mode: StreamMode | Sequence[StreamMode] | None = None, - print_mode: StreamMode | Sequence[StreamMode] = (), - output_keys: str | Sequence[str] | None = None, - interrupt_before: All | Sequence[str] | None = None, - interrupt_after: All | Sequence[str] | None = None, - checkpoint_during: bool | None = None, - debug: bool | None = None, - subgraphs: bool = False, - ) -> Iterator[dict[str, Any] | Any]: - """Stream graph steps for a single input. - - Args: - input: The input to the graph. - config: The configuration to use for the run. - stream_mode: The mode to stream output, defaults to `self.stream_mode`. - Options are: - - - `"values"`: Emit all values in the state after each step, including interrupts. - When used with functional API, values are emitted once at the end of the workflow. - - `"updates"`: Emit only the node or task names and updates returned by the nodes or tasks after each step. - If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately. - - `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`. - - `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks. - Will be emitted as 2-tuples `(LLM token, metadata)`. - - `"checkpoints"`: Emit an event when a checkpoint is created, in the same format as returned by get_state(). - - `"tasks"`: Emit events when tasks start and finish, including their results and errors. - - You can pass a list as the `stream_mode` parameter to stream multiple modes at once. - The streamed outputs will be tuples of `(mode, data)`. - - See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. - print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. - output_keys: The keys to stream, defaults to all non-context channels. - interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph. - interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph. - checkpoint_during: Whether to checkpoint intermediate steps, defaults to False. If False, only the final checkpoint is saved. - subgraphs: Whether to stream events from inside subgraphs, defaults to False. - If True, the events will be emitted as tuples `(namespace, data)`, - or `(namespace, mode, data)` if `stream_mode` is a list, - where `namespace` is a tuple with the path to the node where a subgraph is invoked, - e.g. `("parent_node:", "child_node:")`. - - See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. - - Yields: - The output of each step in the graph. The output shape depends on the stream_mode. - """ - - if stream_mode is None: - # if being called as a node in another graph, default to values mode - # but don't overwrite stream_mode arg if provided - stream_mode = ( - "values" - if config is not None and CONFIG_KEY_TASK_ID in config.get(CONF, {}) - else self.stream_mode - ) - if debug or self.debug: - print_mode = ["updates", "values"] - - stream = SyncQueue() - - config = ensure_config(self.config, config) - callback_manager = get_callback_manager_for_config(config) - run_manager = callback_manager.on_chain_start( - None, - input, - name=config.get("run_name", self.get_name()), - run_id=config.get("run_id"), - ) - try: - # assign defaults - ( - stream_modes, - output_keys, - interrupt_before_, - interrupt_after_, - checkpointer, - store, - cache, - ) = self._defaults( - config, - stream_mode=stream_mode, - print_mode=print_mode, - output_keys=output_keys, - interrupt_before=interrupt_before, - interrupt_after=interrupt_after, - ) - # set up subgraph checkpointing - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config[CONF][CONFIG_KEY_CHECKPOINT_NS] = recast_checkpoint_ns(ns) - # set up messages stream mode - if "messages" in stream_modes: - run_manager.inheritable_handlers.append( - StreamMessagesHandler(stream.put, subgraphs) - ) - # set up custom stream mode - if "custom" in stream_modes: - config[CONF][CONFIG_KEY_STREAM_WRITER] = lambda c: stream.put( - ( - tuple( - get_config()[CONF][CONFIG_KEY_CHECKPOINT_NS].split(NS_SEP)[ - :-1 - ] - ), - "custom", - c, - ) - ) - elif ( - CONFIG_KEY_STREAM not in config[CONF] - and CONFIG_KEY_STREAM_WRITER in config[CONF] - ): - # remove parent graph stream writer if subgraph streaming not requested - del config[CONF][CONFIG_KEY_STREAM_WRITER] - # set checkpointing mode for subgraphs - if checkpoint_during is not None: - config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during - with SyncPregelLoop( - input, - stream=StreamProtocol(stream.put, stream_modes), - config=config, - store=store, - cache=cache, - checkpointer=checkpointer, - nodes=self.nodes, - specs=self.channels, - output_keys=output_keys, - input_keys=self.input_channels, - stream_keys=self.stream_channels_asis, - interrupt_before=interrupt_before_, - interrupt_after=interrupt_after_, - manager=run_manager, - checkpoint_during=checkpoint_during - if checkpoint_during is not None - else config[CONF].get(CONFIG_KEY_CHECKPOINT_DURING, True), - trigger_to_nodes=self.trigger_to_nodes, - migrate_checkpoint=self._migrate_checkpoint, - retry_policy=self.retry_policy, - cache_policy=self.cache_policy, - ) as loop: - # create runner - runner = PregelRunner( - submit=config[CONF].get( - CONFIG_KEY_RUNNER_SUBMIT, weakref.WeakMethod(loop.submit) - ), - put_writes=weakref.WeakMethod(loop.put_writes), - node_finished=config[CONF].get(CONFIG_KEY_NODE_FINISHED), - ) - # enable subgraph streaming - if subgraphs: - loop.config[CONF][CONFIG_KEY_STREAM] = loop.stream - # enable concurrent streaming - if ( - self.stream_eager - or subgraphs - or "messages" in stream_modes - or "custom" in stream_modes - ): - # we are careful to have a single waiter live at any one time - # because on exit we increment semaphore count by exactly 1 - waiter: concurrent.futures.Future | None = None - # because sync futures cannot be cancelled, we instead - # release the stream semaphore on exit, which will cause - # a pending waiter to return immediately - loop.stack.callback(stream._count.release) - - def get_waiter() -> concurrent.futures.Future[None]: - nonlocal waiter - if waiter is None or waiter.done(): - waiter = loop.submit(stream.wait) - return waiter - else: - return waiter - - else: - get_waiter = None # type: ignore[assignment] - # Similarly to Bulk Synchronous Parallel / Pregel model - # computation proceeds in steps, while there are channel updates. - # Channel updates from step N are only visible in step N+1 - # channels are guaranteed to be immutable for the duration of the step, - # with channel updates applied only at the transition between steps. - while loop.tick(): - for task in loop.match_cached_writes(): - loop.output_writes(task.id, task.writes, cached=True) - for _ in runner.tick( - [t for t in loop.tasks.values() if not t.writes], - timeout=self.step_timeout, - get_waiter=get_waiter, - schedule_task=loop.accept_push, - ): - # emit output - yield from _output( - stream_mode, print_mode, subgraphs, stream.get, queue.Empty - ) - loop.after_tick() - # emit output - yield from _output( - stream_mode, print_mode, subgraphs, stream.get, queue.Empty - ) - # handle exit - if loop.status == "out_of_steps": - msg = create_error_message( - message=( - f"Recursion limit of {config['recursion_limit']} reached " - "without hitting a stop condition. You can increase the " - "limit by setting the `recursion_limit` config key." - ), - error_code=ErrorCode.GRAPH_RECURSION_LIMIT, - ) - raise GraphRecursionError(msg) - # set final channel values as run output - run_manager.on_chain_end(loop.output) - except BaseException as e: - run_manager.on_chain_error(e) - raise - - async def astream( - self, - input: InputT, - config: RunnableConfig | None = None, - *, - stream_mode: StreamMode | Sequence[StreamMode] | None = None, - print_mode: StreamMode | Sequence[StreamMode] = (), - output_keys: str | Sequence[str] | None = None, - interrupt_before: All | Sequence[str] | None = None, - interrupt_after: All | Sequence[str] | None = None, - checkpoint_during: bool | None = None, - debug: bool | None = None, - subgraphs: bool = False, - ) -> AsyncIterator[dict[str, Any] | Any]: - """Asynchronously stream graph steps for a single input. - - Args: - input: The input to the graph. - config: The configuration to use for the run. - stream_mode: The mode to stream output, defaults to `self.stream_mode`. - Options are: - - - `"values"`: Emit all values in the state after each step, including interrupts. - When used with functional API, values are emitted once at the end of the workflow. - - `"updates"`: Emit only the node or task names and updates returned by the nodes or tasks after each step. - If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately. - - `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`. - - `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks. - Will be emitted as 2-tuples `(LLM token, metadata)`. - - `"debug"`: Emit debug events with as much information as possible for each step. - - You can pass a list as the `stream_mode` parameter to stream multiple modes at once. - The streamed outputs will be tuples of `(mode, data)`. - - See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. - print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. - output_keys: The keys to stream, defaults to all non-context channels. - interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph. - interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph. - checkpoint_during: Whether to checkpoint intermediate steps, defaults to False. If False, only the final checkpoint is saved. - subgraphs: Whether to stream events from inside subgraphs, defaults to False. - If True, the events will be emitted as tuples `(namespace, data)`, - or `(namespace, mode, data)` if `stream_mode` is a list, - where `namespace` is a tuple with the path to the node where a subgraph is invoked, - e.g. `("parent_node:", "child_node:")`. - - See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. - - Yields: - The output of each step in the graph. The output shape depends on the stream_mode. - """ - - if stream_mode is None: - # if being called as a node in another graph, default to values mode - # but don't overwrite stream_mode arg if provided - stream_mode = ( - "values" - if config is not None and CONFIG_KEY_TASK_ID in config.get(CONF, {}) - else self.stream_mode - ) - if debug or self.debug: - print_mode = ["updates", "values"] - - stream = AsyncQueue() - aioloop = asyncio.get_running_loop() - stream_put = cast( - Callable[[StreamChunk], None], - partial(aioloop.call_soon_threadsafe, stream.put_nowait), - ) - - config = ensure_config(self.config, config) - callback_manager = get_async_callback_manager_for_config(config) - run_manager = await callback_manager.on_chain_start( - None, - input, - name=config.get("run_name", self.get_name()), - run_id=config.get("run_id"), - ) - # if running from astream_log() run each proc with streaming - do_stream = ( - next( - ( - True - for h in run_manager.handlers - if isinstance(h, _StreamingCallbackHandler) - and not isinstance(h, StreamMessagesHandler) - ), - False, - ) - if _StreamingCallbackHandler is not None - else False - ) - try: - # assign defaults - ( - stream_modes, - output_keys, - interrupt_before_, - interrupt_after_, - checkpointer, - store, - cache, - ) = self._defaults( - config, - stream_mode=stream_mode, - print_mode=print_mode, - output_keys=output_keys, - interrupt_before=interrupt_before, - interrupt_after=interrupt_after, - ) - # set up subgraph checkpointing - if self.checkpointer is True: - ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) - config[CONF][CONFIG_KEY_CHECKPOINT_NS] = recast_checkpoint_ns(ns) - # set up messages stream mode - if "messages" in stream_modes: - run_manager.inheritable_handlers.append( - StreamMessagesHandler(stream_put, subgraphs) - ) - # set up custom stream mode - if "custom" in stream_modes: - config[CONF][CONFIG_KEY_STREAM_WRITER] = ( - lambda c: aioloop.call_soon_threadsafe( - stream.put_nowait, - ( - tuple( - get_config()[CONF][CONFIG_KEY_CHECKPOINT_NS].split( - NS_SEP - )[:-1] - ), - "custom", - c, - ), - ) - ) - elif ( - CONFIG_KEY_STREAM not in config[CONF] - and CONFIG_KEY_STREAM_WRITER in config[CONF] - ): - # remove parent graph stream writer if subgraph streaming not requested - del config[CONF][CONFIG_KEY_STREAM_WRITER] - # set checkpointing mode for subgraphs - if checkpoint_during is not None: - config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during - async with AsyncPregelLoop( - input, - stream=StreamProtocol(stream.put_nowait, stream_modes), - config=config, - store=store, - cache=cache, - checkpointer=checkpointer, - nodes=self.nodes, - specs=self.channels, - output_keys=output_keys, - input_keys=self.input_channels, - stream_keys=self.stream_channels_asis, - interrupt_before=interrupt_before_, - interrupt_after=interrupt_after_, - manager=run_manager, - checkpoint_during=checkpoint_during - if checkpoint_during is not None - else config[CONF].get(CONFIG_KEY_CHECKPOINT_DURING, True), - trigger_to_nodes=self.trigger_to_nodes, - migrate_checkpoint=self._migrate_checkpoint, - retry_policy=self.retry_policy, - cache_policy=self.cache_policy, - ) as loop: - # create runner - runner = PregelRunner( - submit=config[CONF].get( - CONFIG_KEY_RUNNER_SUBMIT, weakref.WeakMethod(loop.submit) - ), - put_writes=weakref.WeakMethod(loop.put_writes), - use_astream=do_stream, - node_finished=config[CONF].get(CONFIG_KEY_NODE_FINISHED), - ) - # enable subgraph streaming - if subgraphs: - loop.config[CONF][CONFIG_KEY_STREAM] = StreamProtocol( - stream_put, stream_modes - ) - # enable concurrent streaming - if ( - self.stream_eager - or subgraphs - or "messages" in stream_modes - or "custom" in stream_modes - ): - - def get_waiter() -> asyncio.Task[None]: - return aioloop.create_task(stream.wait()) - - else: - get_waiter = None # type: ignore[assignment] - # Similarly to Bulk Synchronous Parallel / Pregel model - # computation proceeds in steps, while there are channel updates - # channel updates from step N are only visible in step N+1 - # channels are guaranteed to be immutable for the duration of the step, - # with channel updates applied only at the transition between steps - while loop.tick(): - for task in await loop.amatch_cached_writes(): - loop.output_writes(task.id, task.writes, cached=True) - async for _ in runner.atick( - [t for t in loop.tasks.values() if not t.writes], - timeout=self.step_timeout, - get_waiter=get_waiter, - schedule_task=loop.aaccept_push, - ): - # emit output - for o in _output( - stream_mode, - print_mode, - subgraphs, - stream.get_nowait, - asyncio.QueueEmpty, - ): - yield o - loop.after_tick() - # emit output - for o in _output( - stream_mode, - print_mode, - subgraphs, - stream.get_nowait, - asyncio.QueueEmpty, - ): - yield o - # handle exit - if loop.status == "out_of_steps": - msg = create_error_message( - message=( - f"Recursion limit of {config['recursion_limit']} reached " - "without hitting a stop condition. You can increase the " - "limit by setting the `recursion_limit` config key." - ), - error_code=ErrorCode.GRAPH_RECURSION_LIMIT, - ) - raise GraphRecursionError(msg) - # set final channel values as run output - await run_manager.on_chain_end(loop.output) - except BaseException as e: - await asyncio.shield(run_manager.on_chain_error(e)) - raise - - def invoke( - self, - input: InputT, - config: RunnableConfig | None = None, - *, - stream_mode: StreamMode = "values", - print_mode: StreamMode | Sequence[StreamMode] = (), - output_keys: str | Sequence[str] | None = None, - interrupt_before: All | Sequence[str] | None = None, - interrupt_after: All | Sequence[str] | None = None, - **kwargs: Any, - ) -> dict[str, Any] | Any: - """Run the graph with a single input and config. - - Args: - input: The input data for the graph. It can be a dictionary or any other type. - config: Optional. The configuration for the graph run. - stream_mode: Optional[str]. The stream mode for the graph run. Default is "values". - print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. - output_keys: Optional. The output keys to retrieve from the graph run. - interrupt_before: Optional. The nodes to interrupt the graph run before. - interrupt_after: Optional. The nodes to interrupt the graph run after. - **kwargs: Additional keyword arguments to pass to the graph run. - - Returns: - The output of the graph run. If stream_mode is "values", it returns the latest output. - If stream_mode is not "values", it returns a list of output chunks. - """ - output_keys = output_keys if output_keys is not None else self.output_channels - - latest: dict[str, Any] | Any = None - chunks: list[dict[str, Any] | Any] = [] - interrupts: list[Interrupt] = [] - - for chunk in self.stream( - input, - config, - stream_mode=["updates", "values"] - if stream_mode == "values" - else stream_mode, - print_mode=print_mode, - output_keys=output_keys, - interrupt_before=interrupt_before, - interrupt_after=interrupt_after, - **kwargs, - ): - if stream_mode == "values": - if len(chunk) == 2: - mode, payload = cast(tuple[StreamMode, Any], chunk) - else: - _, mode, payload = cast( - tuple[tuple[str, ...], StreamMode, Any], chunk - ) - if ( - mode == "updates" - and isinstance(payload, dict) - and (ints := payload.get(INTERRUPT)) is not None - ): - interrupts.extend(ints) - elif mode == "values": - latest = payload - else: - chunks.append(chunk) - - if stream_mode == "values": - if interrupts: - return ( - {**latest, INTERRUPT: interrupts} - if isinstance(latest, dict) - else {INTERRUPT: interrupts} - ) - return latest - else: - return chunks - - async def ainvoke( - self, - input: InputT, - config: RunnableConfig | None = None, - *, - stream_mode: StreamMode = "values", - print_mode: StreamMode | Sequence[StreamMode] = (), - output_keys: str | Sequence[str] | None = None, - interrupt_before: All | Sequence[str] | None = None, - interrupt_after: All | Sequence[str] | None = None, - **kwargs: Any, - ) -> dict[str, Any] | Any: - """Asynchronously invoke the graph on a single input. - - Args: - input: The input data for the computation. It can be a dictionary or any other type. - config: Optional. The configuration for the computation. - stream_mode: Optional. The stream mode for the computation. Default is "values". - print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. - output_keys: Optional. The output keys to include in the result. Default is None. - interrupt_before: Optional. The nodes to interrupt before. Default is None. - interrupt_after: Optional. The nodes to interrupt after. Default is None. - **kwargs: Additional keyword arguments. - - Returns: - The result of the computation. If stream_mode is "values", it returns the latest value. - If stream_mode is "chunks", it returns a list of chunks. - """ - - output_keys = output_keys if output_keys is not None else self.output_channels - - latest: dict[str, Any] | Any = None - chunks: list[dict[str, Any] | Any] = [] - interrupts: list[Interrupt] = [] - - async for chunk in self.astream( - input, - config, - stream_mode=["updates", "values"] - if stream_mode == "values" - else stream_mode, - print_mode=print_mode, - output_keys=output_keys, - interrupt_before=interrupt_before, - interrupt_after=interrupt_after, - **kwargs, - ): - if stream_mode == "values": - if len(chunk) == 2: - mode, payload = cast(tuple[StreamMode, Any], chunk) - else: - _, mode, payload = cast( - tuple[tuple[str, ...], StreamMode, Any], chunk - ) - if ( - mode == "updates" - and isinstance(payload, dict) - and (ints := payload.get(INTERRUPT)) is not None - ): - interrupts.extend(ints) - elif mode == "values": - latest = payload - else: - chunks.append(chunk) - - if stream_mode == "values": - if interrupts: - return ( - {**latest, INTERRUPT: interrupts} - if isinstance(latest, dict) - else {INTERRUPT: interrupts} - ) - return latest - else: - return chunks - - def clear_cache(self, nodes: Sequence[str] | None = None) -> None: - """Clear the cache for the given nodes.""" - if not self.cache: - raise ValueError("No cache is set for this graph. Cannot clear cache.") - nodes = nodes or self.nodes.keys() - # collect namespaces to clear - namespaces: list[tuple[str, ...]] = [] - for node in nodes: - if node in self.nodes: - namespaces.append( - ( - CACHE_NS_WRITES, - (identifier(self.nodes[node]) or "__dynamic__"), - node, - ), - ) - # clear cache - self.cache.clear(namespaces) - - async def aclear_cache(self, nodes: Sequence[str] | None = None) -> None: - """Asynchronously clear the cache for the given nodes.""" - if not self.cache: - raise ValueError("No cache is set for this graph. Cannot clear cache.") - nodes = nodes or self.nodes.keys() - # collect namespaces to clear - namespaces: list[tuple[str, ...]] = [] - for node in nodes: - if node in self.nodes: - namespaces.append( - ( - CACHE_NS_WRITES, - (identifier(self.nodes[node]) or "__dynamic__"), - node, - ), - ) - # clear cache - await self.cache.aclear(namespaces) - - -def _trigger_to_nodes(nodes: dict[str, PregelNode]) -> Mapping[str, Sequence[str]]: - """Index from a trigger to nodes that depend on it.""" - trigger_to_nodes: defaultdict[str, list[str]] = defaultdict(list) - for name, node in nodes.items(): - for trigger in node.triggers: - trigger_to_nodes[trigger].append(name) - return dict(trigger_to_nodes) - - -def _output( - stream_mode: StreamMode | Sequence[StreamMode], - print_mode: StreamMode | Sequence[StreamMode], - stream_subgraphs: bool, - getter: Callable[[], tuple[tuple[str, ...], str, Any]], - empty_exc: type[Exception], -) -> Iterator: - while True: - try: - ns, mode, payload = getter() - except empty_exc: - break - if mode in print_mode: - if stream_subgraphs and ns: - print( - " ".join( - ( - get_bolded_text(f"[{mode}]"), - get_colored_text(f"[graph={ns}]", color="yellow"), - repr(payload), - ) - ) - ) - else: - print( - " ".join( - ( - get_bolded_text(f"[{mode}]"), - repr(payload), - ) - ) - ) - if mode in stream_mode: - if stream_subgraphs and isinstance(stream_mode, list): - yield (ns, mode, payload) - elif isinstance(stream_mode, list): - yield (mode, payload) - elif stream_subgraphs: - yield (ns, payload) - else: - yield payload +__all__ = ("Pregel", "NodeBuilder") diff --git a/libs/langgraph/langgraph/pregel/algo.py b/libs/langgraph/langgraph/pregel/_algo.py similarity index 99% rename from libs/langgraph/langgraph/pregel/algo.py rename to libs/langgraph/langgraph/pregel/_algo.py index 9d15aff3c..270dfebdb 100644 --- a/libs/langgraph/langgraph/pregel/algo.py +++ b/libs/langgraph/langgraph/pregel/_algo.py @@ -25,6 +25,7 @@ from langchain_core.callbacks.manager import AsyncParentRunManager, ParentRunMan from langchain_core.runnables.config import RunnableConfig from xxhash import xxh3_128_hexdigest +from langgraph._internal._config import merge_configs, patch_config from langgraph.channels.base import BaseChannel from langgraph.channels.topic import Topic from langgraph.checkpoint.base import ( @@ -64,24 +65,23 @@ from langgraph.constants import ( RETURN, TAG_HIDDEN, TASKS, - Send, ) from langgraph.managed.base import ManagedValueMapping -from langgraph.pregel.call import get_runnable_for_task, identifier -from langgraph.pregel.io import read_channels -from langgraph.pregel.log import logger -from langgraph.pregel.read import INPUT_CACHE_KEY_TYPE, PregelNode +from langgraph.pregel._call import get_runnable_for_task, identifier +from langgraph.pregel._io import read_channels +from langgraph.pregel._log import logger +from langgraph.pregel._read import INPUT_CACHE_KEY_TYPE, PregelNode +from langgraph.pregel._scratchpad import PregelScratchpad from langgraph.store.base import BaseStore from langgraph.types import ( All, CacheKey, CachePolicy, PregelExecutableTask, - PregelScratchpad, PregelTask, RetryPolicy, + Send, ) -from langgraph.utils.config import merge_configs, patch_config GetNextVersion = Callable[[Optional[V], None], V] SUPPORTS_EXC_NOTES = sys.version_info >= (3, 11) diff --git a/libs/langgraph/langgraph/pregel/call.py b/libs/langgraph/langgraph/pregel/_call.py similarity index 96% rename from libs/langgraph/langgraph/pregel/call.py rename to libs/langgraph/langgraph/pregel/_call.py index df1e158cb..6bcd93f05 100644 --- a/libs/langgraph/langgraph/pregel/call.py +++ b/libs/langgraph/langgraph/pregel/_call.py @@ -13,16 +13,16 @@ from typing import Any, Callable, Generic, TypeVar, cast from langchain_core.runnables import Runnable from typing_extensions import ParamSpec -from langgraph.constants import CONF, CONFIG_KEY_CALL, RETURN -from langgraph.pregel.write import ChannelWrite, ChannelWriteEntry -from langgraph.types import CachePolicy, RetryPolicy -from langgraph.utils.config import get_config -from langgraph.utils.runnable import ( +from langgraph._internal._runnable import ( RunnableCallable, RunnableSeq, is_async_callable, run_in_executor, ) +from langgraph.config import get_config +from langgraph.constants import CONF, CONFIG_KEY_CALL, RETURN +from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry +from langgraph.types import CachePolicy, RetryPolicy ## # Utilities borrowed from cloudpickle. @@ -78,8 +78,8 @@ def _whichmodule(obj: Any, name: str) -> str | None: def identifier(obj: Any, name: str | None = None) -> str | None: """Return the module and name of an object.""" - from langgraph.pregel.read import PregelNode - from langgraph.utils.runnable import RunnableCallable, RunnableSeq + from langgraph._internal._runnable import RunnableCallable, RunnableSeq + from langgraph.pregel._read import PregelNode if isinstance(obj, PregelNode): obj = obj.bound diff --git a/libs/langgraph/langgraph/pregel/checkpoint.py b/libs/langgraph/langgraph/pregel/_checkpoint.py similarity index 100% rename from libs/langgraph/langgraph/pregel/checkpoint.py rename to libs/langgraph/langgraph/pregel/_checkpoint.py diff --git a/libs/langgraph/langgraph/pregel/draw.py b/libs/langgraph/langgraph/pregel/_draw.py similarity index 97% rename from libs/langgraph/langgraph/pregel/draw.py rename to libs/langgraph/langgraph/pregel/_draw.py index 091e92be3..9720f10e7 100644 --- a/libs/langgraph/langgraph/pregel/draw.py +++ b/libs/langgraph/langgraph/pregel/_draw.py @@ -11,16 +11,16 @@ from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.constants import CONF, CONFIG_KEY_SEND, END, INPUT, START from langgraph.managed.base import ManagedValueSpec -from langgraph.pregel.algo import ( +from langgraph.pregel._algo import ( PregelTaskWrites, apply_writes, increment, prepare_next_tasks, ) -from langgraph.pregel.checkpoint import channels_from_checkpoint, empty_checkpoint -from langgraph.pregel.io import map_input -from langgraph.pregel.read import PregelNode -from langgraph.pregel.write import ChannelWrite +from langgraph.pregel._checkpoint import channels_from_checkpoint, empty_checkpoint +from langgraph.pregel._io import map_input +from langgraph.pregel._read import PregelNode +from langgraph.pregel._write import ChannelWrite from langgraph.types import All, Checkpointer diff --git a/libs/langgraph/langgraph/pregel/executor.py b/libs/langgraph/langgraph/pregel/_executor.py similarity index 98% rename from libs/langgraph/langgraph/pregel/executor.py rename to libs/langgraph/langgraph/pregel/_executor.py index 62df4b19f..db37135c0 100644 --- a/libs/langgraph/langgraph/pregel/executor.py +++ b/libs/langgraph/langgraph/pregel/_executor.py @@ -18,8 +18,8 @@ from langchain_core.runnables import RunnableConfig from langchain_core.runnables.config import get_executor_for_config from typing_extensions import ParamSpec +from langgraph._internal._future import CONTEXT_NOT_SUPPORTED, run_coroutine_threadsafe from langgraph.errors import GraphBubbleUp -from langgraph.utils.future import CONTEXT_NOT_SUPPORTED, run_coroutine_threadsafe P = ParamSpec("P") T = TypeVar("T") diff --git a/libs/langgraph/langgraph/pregel/io.py b/libs/langgraph/langgraph/pregel/_io.py similarity index 99% rename from libs/langgraph/langgraph/pregel/io.py rename to libs/langgraph/langgraph/pregel/_io.py index 48268af76..3eff58c8a 100644 --- a/libs/langgraph/langgraph/pregel/io.py +++ b/libs/langgraph/langgraph/pregel/_io.py @@ -18,7 +18,7 @@ from langgraph.constants import ( TASKS, ) from langgraph.errors import InvalidUpdateError -from langgraph.pregel.log import logger +from langgraph.pregel._log import logger from langgraph.types import Command, PregelExecutableTask, Send diff --git a/libs/langgraph/langgraph/pregel/log.py b/libs/langgraph/langgraph/pregel/_log.py similarity index 100% rename from libs/langgraph/langgraph/pregel/log.py rename to libs/langgraph/langgraph/pregel/_log.py diff --git a/libs/langgraph/langgraph/pregel/loop.py b/libs/langgraph/langgraph/pregel/_loop.py similarity index 98% rename from libs/langgraph/langgraph/pregel/loop.py rename to libs/langgraph/langgraph/pregel/_loop.py index e5d4e1534..7dc2ec5dc 100644 --- a/libs/langgraph/langgraph/pregel/loop.py +++ b/libs/langgraph/langgraph/pregel/_loop.py @@ -27,6 +27,7 @@ from langchain_core.callbacks import AsyncParentRunManager, ParentRunManager from langchain_core.runnables import RunnableConfig from typing_extensions import ParamSpec, Self +from langgraph._internal._config import patch_configurable from langgraph.cache.base import BaseCache from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import ( @@ -69,7 +70,7 @@ from langgraph.managed.base import ( ManagedValueMapping, ManagedValueSpec, ) -from langgraph.pregel.algo import ( +from langgraph.pregel._algo import ( Call, GetNextVersion, PregelTaskWrites, @@ -81,44 +82,42 @@ from langgraph.pregel.algo import ( should_interrupt, task_path_str, ) -from langgraph.pregel.checkpoint import ( +from langgraph.pregel._checkpoint import ( channels_from_checkpoint, copy_checkpoint, create_checkpoint, empty_checkpoint, ) -from langgraph.pregel.debug import ( - map_debug_checkpoint, - map_debug_task_results, - map_debug_tasks, -) -from langgraph.pregel.executor import ( +from langgraph.pregel._executor import ( AsyncBackgroundExecutor, BackgroundExecutor, Submit, ) -from langgraph.pregel.io import ( +from langgraph.pregel._io import ( map_command, map_input, map_output_updates, map_output_values, read_channels, ) -from langgraph.pregel.read import PregelNode -from langgraph.pregel.utils import get_new_channel_versions, is_xxh3_128_hexdigest +from langgraph.pregel._read import PregelNode +from langgraph.pregel._scratchpad import PregelScratchpad +from langgraph.pregel._utils import get_new_channel_versions, is_xxh3_128_hexdigest +from langgraph.pregel.debug import ( + map_debug_checkpoint, + map_debug_task_results, + map_debug_tasks, +) +from langgraph.pregel.protocol import StreamChunk, StreamProtocol from langgraph.store.base import BaseStore from langgraph.types import ( All, CachePolicy, Command, PregelExecutableTask, - PregelScratchpad, RetryPolicy, - StreamChunk, StreamMode, - StreamProtocol, ) -from langgraph.utils.config import patch_configurable V = TypeVar("V") P = ParamSpec("P") diff --git a/libs/langgraph/langgraph/pregel/messages.py b/libs/langgraph/langgraph/pregel/_messages.py similarity index 97% rename from libs/langgraph/langgraph/pregel/messages.py rename to libs/langgraph/langgraph/pregel/_messages.py index 9a9210aa6..b06991ba3 100644 --- a/libs/langgraph/langgraph/pregel/messages.py +++ b/libs/langgraph/langgraph/pregel/_messages.py @@ -13,8 +13,9 @@ from langchain_core.callbacks import BaseCallbackHandler from langchain_core.messages import BaseMessage from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, LLMResult -from langgraph.constants import NS_SEP, TAG_HIDDEN, TAG_NOSTREAM, TAG_NOSTREAM_ALT -from langgraph.types import Command, StreamChunk +from langgraph.constants import NS_SEP, TAG_HIDDEN, TAG_NOSTREAM +from langgraph.pregel.protocol import StreamChunk +from langgraph.types import Command try: from langchain_core.tracers._streaming import _StreamingCallbackHandler @@ -94,9 +95,7 @@ class StreamMessagesHandler(BaseCallbackHandler, _StreamingCallbackHandler): metadata: dict[str, Any] | None = None, **kwargs: Any, ) -> Any: - if metadata and ( - not tags or (TAG_NOSTREAM not in tags and TAG_NOSTREAM_ALT not in tags) - ): + if metadata and (not tags or (TAG_NOSTREAM not in tags)): ns = tuple(cast(str, metadata["langgraph_checkpoint_ns"]).split(NS_SEP))[ :-1 ] diff --git a/libs/langgraph/langgraph/pregel/read.py b/libs/langgraph/langgraph/pregel/_read.py similarity index 97% rename from libs/langgraph/langgraph/pregel/read.py rename to libs/langgraph/langgraph/pregel/_read.py index 30a9af652..ea73d4d63 100644 --- a/libs/langgraph/langgraph/pregel/read.py +++ b/libs/langgraph/langgraph/pregel/_read.py @@ -10,13 +10,13 @@ from typing import ( from langchain_core.runnables import Runnable, RunnableConfig +from langgraph._internal._config import merge_configs +from langgraph._internal._runnable import RunnableCallable, RunnableSeq from langgraph.constants import CONF, CONFIG_KEY_READ +from langgraph.pregel._utils import find_subgraph_pregel +from langgraph.pregel._write import ChannelWrite from langgraph.pregel.protocol import PregelProtocol -from langgraph.pregel.utils import find_subgraph_pregel -from langgraph.pregel.write import ChannelWrite from langgraph.types import CachePolicy, RetryPolicy -from langgraph.utils.config import merge_configs -from langgraph.utils.runnable import RunnableCallable, RunnableSeq READ_TYPE = Callable[[Union[str, Sequence[str]], bool], Union[Any, dict[str, Any]]] INPUT_CACHE_KEY_TYPE = tuple[Callable[..., Any], tuple[str, ...]] diff --git a/libs/langgraph/langgraph/pregel/retry.py b/libs/langgraph/langgraph/pregel/_retry.py similarity index 99% rename from libs/langgraph/langgraph/pregel/retry.py rename to libs/langgraph/langgraph/pregel/_retry.py index a91edb9c4..4873e6824 100644 --- a/libs/langgraph/langgraph/pregel/retry.py +++ b/libs/langgraph/langgraph/pregel/_retry.py @@ -9,6 +9,7 @@ from collections.abc import Awaitable, Sequence from dataclasses import replace from typing import Any, Callable +from langgraph._internal._config import patch_configurable from langgraph.constants import ( CONF, CONFIG_KEY_CHECKPOINT_NS, @@ -17,7 +18,6 @@ from langgraph.constants import ( ) from langgraph.errors import GraphBubbleUp, ParentCommand from langgraph.types import Command, PregelExecutableTask, RetryPolicy -from langgraph.utils.config import patch_configurable logger = logging.getLogger(__name__) SUPPORTS_EXC_NOTES = sys.version_info >= (3, 11) diff --git a/libs/langgraph/langgraph/pregel/runner.py b/libs/langgraph/langgraph/pregel/_runner.py similarity index 98% rename from libs/langgraph/langgraph/pregel/runner.py rename to libs/langgraph/langgraph/pregel/_runner.py index ab6c7a17d..9c29eabca 100644 --- a/libs/langgraph/langgraph/pregel/runner.py +++ b/libs/langgraph/langgraph/pregel/_runner.py @@ -19,6 +19,7 @@ from typing import ( from langchain_core.callbacks import Callbacks +from langgraph._internal._future import chain_future, run_coroutine_threadsafe from langgraph.constants import ( CONF, CONFIG_KEY_CALL, @@ -32,16 +33,15 @@ from langgraph.constants import ( TAG_HIDDEN, ) from langgraph.errors import GraphBubbleUp, GraphInterrupt -from langgraph.pregel.algo import Call -from langgraph.pregel.executor import Submit -from langgraph.pregel.retry import arun_with_retry, run_with_retry +from langgraph.pregel._algo import Call +from langgraph.pregel._executor import Submit +from langgraph.pregel._retry import arun_with_retry, run_with_retry +from langgraph.pregel._scratchpad import PregelScratchpad from langgraph.types import ( CachePolicy, PregelExecutableTask, - PregelScratchpad, RetryPolicy, ) -from langgraph.utils.future import chain_future, run_coroutine_threadsafe F = TypeVar("F", concurrent.futures.Future, asyncio.Future) E = TypeVar("E", threading.Event, asyncio.Event) diff --git a/libs/langgraph/langgraph/pregel/_scratchpad.py b/libs/langgraph/langgraph/pregel/_scratchpad.py new file mode 100644 index 000000000..1e8eb8a8b --- /dev/null +++ b/libs/langgraph/langgraph/pregel/_scratchpad.py @@ -0,0 +1,18 @@ +import dataclasses +from typing import Any, Callable + +from langgraph.types import _DC_KWARGS + + +@dataclasses.dataclass(**_DC_KWARGS) +class PregelScratchpad: + step: int + stop: int + # call + call_counter: Callable[[], int] + # interrupt + interrupt_counter: Callable[[], int] + get_null_resume: Callable[[bool], Any] + resume: list[Any] + # subgraph + subgraph_counter: Callable[[], int] diff --git a/libs/langgraph/langgraph/pregel/utils.py b/libs/langgraph/langgraph/pregel/_utils.py similarity index 97% rename from libs/langgraph/langgraph/pregel/utils.py rename to libs/langgraph/langgraph/pregel/_utils.py index a37228c05..87c026ae4 100644 --- a/libs/langgraph/langgraph/pregel/utils.py +++ b/libs/langgraph/langgraph/pregel/_utils.py @@ -6,12 +6,12 @@ import re import textwrap from typing import Any, Callable -from langchain_core.runnables import RunnableLambda, RunnableSequence +from langchain_core.runnables import Runnable, RunnableLambda, RunnableSequence from typing_extensions import override +from langgraph._internal._runnable import RunnableCallable, RunnableSeq from langgraph.checkpoint.base import ChannelVersions from langgraph.pregel.protocol import PregelProtocol -from langgraph.utils.runnable import Runnable, RunnableCallable, RunnableSeq def get_new_channel_versions( diff --git a/libs/langgraph/langgraph/pregel/validate.py b/libs/langgraph/langgraph/pregel/_validate.py similarity index 98% rename from libs/langgraph/langgraph/pregel/validate.py rename to libs/langgraph/langgraph/pregel/_validate.py index 00da8b4b1..9a8910703 100644 --- a/libs/langgraph/langgraph/pregel/validate.py +++ b/libs/langgraph/langgraph/pregel/_validate.py @@ -6,7 +6,7 @@ from typing import Any from langgraph.channels.base import BaseChannel from langgraph.constants import RESERVED from langgraph.managed.base import ManagedValueMapping -from langgraph.pregel.read import PregelNode +from langgraph.pregel._read import PregelNode from langgraph.types import All diff --git a/libs/langgraph/langgraph/pregel/write.py b/libs/langgraph/langgraph/pregel/_write.py similarity index 98% rename from libs/langgraph/langgraph/pregel/write.py rename to libs/langgraph/langgraph/pregel/_write.py index 7f1fdc73f..56dceb9d4 100644 --- a/libs/langgraph/langgraph/pregel/write.py +++ b/libs/langgraph/langgraph/pregel/_write.py @@ -13,9 +13,10 @@ from typing import ( from langchain_core.runnables import Runnable, RunnableConfig -from langgraph.constants import CONF, CONFIG_KEY_SEND, MISSING, TASKS, Send +from langgraph._internal._runnable import RunnableCallable +from langgraph.constants import CONF, CONFIG_KEY_SEND, MISSING, TASKS from langgraph.errors import InvalidUpdateError -from langgraph.utils.runnable import RunnableCallable +from langgraph.types import Send TYPE_SEND = Callable[[Sequence[tuple[str, Any]]], None] R = TypeVar("R", bound=Runnable) diff --git a/libs/langgraph/langgraph/pregel/debug.py b/libs/langgraph/langgraph/pregel/debug.py index d8d3bbdae..0ccc168d0 100644 --- a/libs/langgraph/langgraph/pregel/debug.py +++ b/libs/langgraph/langgraph/pregel/debug.py @@ -5,8 +5,10 @@ from dataclasses import asdict from typing import Any from uuid import UUID +from langchain_core.runnables import RunnableConfig from typing_extensions import TypedDict +from langgraph._internal._config import patch_checkpoint_map from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import CheckpointMetadata, PendingWrite from langgraph.constants import ( @@ -20,9 +22,10 @@ from langgraph.constants import ( RETURN, TAG_HIDDEN, ) -from langgraph.pregel.io import read_channels +from langgraph.pregel._io import read_channels from langgraph.types import PregelExecutableTask, PregelTask, StateSnapshot -from langgraph.utils.config import RunnableConfig, patch_checkpoint_map + +__all__ = ("TaskPayload", "TaskResultPayload", "CheckpointTask", "CheckpointPayload") class TaskPayload(TypedDict): diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py new file mode 100644 index 000000000..eded49e24 --- /dev/null +++ b/libs/langgraph/langgraph/pregel/main.py @@ -0,0 +1,3061 @@ +from __future__ import annotations + +import asyncio +import concurrent +import concurrent.futures +import queue +import weakref +from collections import defaultdict, deque +from collections.abc import AsyncIterator, Iterator, Mapping, Sequence +from functools import partial +from typing import Any, Callable, Generic, Union, cast, get_type_hints +from uuid import UUID, uuid5 + +from langchain_core.globals import get_debug +from langchain_core.runnables import ( + RunnableSequence, +) +from langchain_core.runnables.base import Input, Output +from langchain_core.runnables.config import ( + RunnableConfig, + get_async_callback_manager_for_config, + get_callback_manager_for_config, +) +from langchain_core.runnables.graph import Graph +from pydantic import BaseModel +from typing_extensions import Self + +from langgraph._internal._config import ( + ensure_config, + merge_configs, + patch_checkpoint_map, + patch_config, + patch_configurable, + recast_checkpoint_ns, +) +from langgraph._internal._pydantic import create_model +from langgraph._internal._queue import ( # type: ignore[attr-defined] + AsyncQueue, + SyncQueue, +) +from langgraph._internal._runnable import ( + Runnable, + RunnableLike, + RunnableSeq, + coerce_to_runnable, +) +from langgraph.cache.base import BaseCache +from langgraph.channels.base import BaseChannel +from langgraph.channels.topic import Topic +from langgraph.checkpoint.base import ( + BaseCheckpointSaver, + Checkpoint, + CheckpointTuple, +) +from langgraph.config import get_config +from langgraph.constants import ( + CACHE_NS_WRITES, + CONF, + CONFIG_KEY_CACHE, + CONFIG_KEY_CHECKPOINT_DURING, + CONFIG_KEY_CHECKPOINT_ID, + CONFIG_KEY_CHECKPOINT_NS, + CONFIG_KEY_CHECKPOINTER, + CONFIG_KEY_NODE_FINISHED, + CONFIG_KEY_READ, + CONFIG_KEY_RUNNER_SUBMIT, + CONFIG_KEY_SEND, + CONFIG_KEY_STORE, + CONFIG_KEY_STREAM, + CONFIG_KEY_STREAM_WRITER, + CONFIG_KEY_TASK_ID, + CONFIG_KEY_THREAD_ID, + END, + ERROR, + INPUT, + INTERRUPT, + NS_END, + NS_SEP, + NULL_TASK_ID, + PUSH, + TASKS, +) +from langgraph.errors import ( + ErrorCode, + GraphRecursionError, + InvalidUpdateError, + create_error_message, +) +from langgraph.managed.base import ManagedValueSpec +from langgraph.pregel._algo import ( + PregelTaskWrites, + _scratchpad, + apply_writes, + local_read, + prepare_next_tasks, +) +from langgraph.pregel._call import identifier +from langgraph.pregel._checkpoint import ( + channels_from_checkpoint, + copy_checkpoint, + create_checkpoint, + empty_checkpoint, +) +from langgraph.pregel._draw import draw_graph +from langgraph.pregel._io import map_input, read_channels +from langgraph.pregel._loop import AsyncPregelLoop, SyncPregelLoop +from langgraph.pregel._messages import StreamMessagesHandler +from langgraph.pregel._read import DEFAULT_BOUND, PregelNode +from langgraph.pregel._retry import RetryPolicy +from langgraph.pregel._runner import PregelRunner +from langgraph.pregel._utils import get_new_channel_versions +from langgraph.pregel._validate import validate_graph, validate_keys +from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry +from langgraph.pregel.debug import get_bolded_text, get_colored_text, tasks_w_writes +from langgraph.pregel.protocol import PregelProtocol, StreamChunk, StreamProtocol +from langgraph.store.base import BaseStore +from langgraph.types import ( + All, + CachePolicy, + Checkpointer, + Interrupt, + Send, + StateSnapshot, + StateUpdate, + StreamMode, +) +from langgraph.typing import InputT, OutputT, StateT + +try: + from langchain_core.tracers._streaming import _StreamingCallbackHandler +except ImportError: + _StreamingCallbackHandler = None # type: ignore + +__all__ = ("NodeBuilder", "Pregel") + +_WriteValue = Union[Callable[[Input], Output], Any] + + +class NodeBuilder: + __slots__ = ( + "_channels", + "_triggers", + "_tags", + "_metadata", + "_writes", + "_bound", + "_retry_policy", + "_cache_policy", + ) + + _channels: str | list[str] + _triggers: list[str] + _tags: list[str] + _metadata: dict[str, Any] + _writes: list[ChannelWriteEntry] + _bound: Runnable + _retry_policy: list[RetryPolicy] + _cache_policy: CachePolicy | None + + def __init__( + self, + ) -> None: + self._channels = [] + self._triggers = [] + self._tags = [] + self._metadata = {} + self._writes = [] + self._bound = DEFAULT_BOUND + self._retry_policy = [] + self._cache_policy = None + + def subscribe_only( + self, + channel: str, + ) -> Self: + """Subscribe to a single channel.""" + if not self._channels: + self._channels = channel + else: + raise ValueError( + "Cannot subscribe to single channels when other channels are already subscribed to" + ) + + self._triggers.append(channel) + + return self + + def subscribe_to( + self, + *channels: str, + read: bool = True, + ) -> Self: + """Add channels to subscribe to. Node will be invoked when any of these + channels are updated, with a dict of the channel values as input. + + Args: + channels: Channel name(s) to subscribe to + read: If True, the channels will be included in the input to the node. + Otherwise, they will trigger the node without being sent in input. + + Returns: + Self for chaining + """ + if isinstance(self._channels, str): + raise ValueError( + "Cannot subscribe to channels when subscribed to a single channel" + ) + if read: + if not self._channels: + self._channels = list(channels) + else: + self._channels.extend(channels) + + if isinstance(channels, str): + self._triggers.append(channels) + else: + self._triggers.extend(channels) + + return self + + def read_from( + self, + *channels: str, + ) -> Self: + """Adds the specified channels to read from, without subscribing to them.""" + assert isinstance(self._channels, list), ( + "Cannot read additional channels when subscribed to single channels" + ) + self._channels.extend(channels) + return self + + def do( + self, + node: RunnableLike, + ) -> Self: + """Adds the specified node.""" + if self._bound is not DEFAULT_BOUND: + self._bound = RunnableSeq( + self._bound, coerce_to_runnable(node, name=None, trace=True) + ) + else: + self._bound = coerce_to_runnable(node, name=None, trace=True) + return self + + def write_to( + self, + *channels: str | ChannelWriteEntry, + **kwargs: _WriteValue, + ) -> Self: + """Add channel writes. + + Args: + *channels: Channel names to write to + **kwargs: Channel name and value mappings + + Returns: + Self for chaining + """ + self._writes.extend( + ChannelWriteEntry(c) if isinstance(c, str) else c for c in channels + ) + self._writes.extend( + ChannelWriteEntry(k, mapper=v) + if callable(v) + else ChannelWriteEntry(k, value=v) + for k, v in kwargs.items() + ) + + return self + + def meta(self, *tags: str, **metadata: Any) -> Self: + """Add tags or metadata to the node.""" + self._tags.extend(tags) + self._metadata.update(metadata) + return self + + def add_retry_policies(self, *policies: RetryPolicy) -> Self: + """Adds retry policies to the node.""" + self._retry_policy.extend(policies) + return self + + def add_cache_policy(self, policy: CachePolicy) -> Self: + """Adds cache policies to the node.""" + self._cache_policy = policy + return self + + def build(self) -> PregelNode: + """Builds the node.""" + return PregelNode( + channels=self._channels, + triggers=self._triggers, + tags=self._tags, + metadata=self._metadata, + writers=[ChannelWrite(self._writes)], + bound=self._bound, + retry_policy=self._retry_policy, + cache_policy=self._cache_policy, + ) + + +class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, OutputT]): + """Pregel manages the runtime behavior for LangGraph applications. + + ## Overview + + Pregel combines [**actors**](https://en.wikipedia.org/wiki/Actor_model) + and **channels** into a single application. + **Actors** read data from channels and write data to channels. + Pregel organizes the execution of the application into multiple steps, + following the **Pregel Algorithm**/**Bulk Synchronous Parallel** model. + + Each step consists of three phases: + + - **Plan**: Determine which **actors** to execute in this step. For example, + in the first step, select the **actors** that subscribe to the special + **input** channels; in subsequent steps, + select the **actors** that subscribe to channels updated in the previous step. + - **Execution**: Execute all selected **actors** in parallel, + until all complete, or one fails, or a timeout is reached. During this + phase, channel updates are invisible to actors until the next step. + - **Update**: Update the channels with the values written by the **actors** + in this step. + + Repeat until no **actors** are selected for execution, or a maximum number of + steps is reached. + + ## Actors + + An **actor** is a `PregelNode`. + It subscribes to channels, reads data from them, and writes data to them. + It can be thought of as an **actor** in the Pregel algorithm. + `PregelNodes` implement LangChain's + Runnable interface. + + ## Channels + + Channels are used to communicate between actors (`PregelNodes`). + Each channel has a value type, an update type, and an update function – which + takes a sequence of updates and + modifies the stored value. Channels can be used to send data from one chain to + another, or to send data from a chain to itself in a future step. LangGraph + provides a number of built-in channels: + + ### Basic channels: LastValue and Topic + + - `LastValue`: The default channel, stores the last value sent to the channel, + useful for input and output values, or for sending data from one step to the next + - `Topic`: A configurable PubSub Topic, useful for sending multiple values + between *actors*, or for accumulating output. Can be configured to deduplicate + values, and/or to accumulate values over the course of multiple steps. + + ### Advanced channels: Context and BinaryOperatorAggregate + + - `Context`: exposes the value of a context manager, managing its lifecycle. + Useful for accessing external resources that require setup and/or teardown. eg. + `client = Context(httpx.Client)` + - `BinaryOperatorAggregate`: stores a persistent value, updated by applying + a binary operator to the current value and each update + sent to the channel, useful for computing aggregates over multiple steps. eg. + `total = BinaryOperatorAggregate(int, operator.add)` + + ## Examples + + Most users will interact with Pregel via a + [StateGraph (Graph API)][langgraph.graph.StateGraph] or via an + [entrypoint (Functional API)][langgraph.func.entrypoint]. + + However, for **advanced** use cases, Pregel can be used directly. If you're + not sure whether you need to use Pregel directly, then the answer is probably no + – you should use the Graph API or Functional API instead. These are higher-level + interfaces that will compile down to Pregel under the hood. + + Here are some examples to give you a sense of how it works: + + Example: Single node application + ```python + from langgraph.channels import EphemeralValue + from langgraph.pregel import Pregel, NodeBuilder + + node1 = ( + NodeBuilder().subscribe_only("a") + .do(lambda x: x + x) + .write_to("b") + ) + + app = Pregel( + nodes={"node1": node1}, + channels={ + "a": EphemeralValue(str), + "b": EphemeralValue(str), + }, + input_channels=["a"], + output_channels=["b"], + ) + + app.invoke({"a": "foo"}) + ``` + + ```con + {'b': 'foofoo'} + ``` + + Example: Using multiple nodes and multiple output channels + ```python + from langgraph.channels import LastValue, EphemeralValue + from langgraph.pregel import Pregel, NodeBuilder + + node1 = ( + NodeBuilder().subscribe_only("a") + .do(lambda x: x + x) + .write_to("b") + ) + + node2 = ( + NodeBuilder().subscribe_to("b") + .do(lambda x: x["b"] + x["b"]) + .write_to("c") + ) + + + app = Pregel( + nodes={"node1": node1, "node2": node2}, + channels={ + "a": EphemeralValue(str), + "b": LastValue(str), + "c": EphemeralValue(str), + }, + input_channels=["a"], + output_channels=["b", "c"], + ) + + app.invoke({"a": "foo"}) + ``` + + ```con + {'b': 'foofoo', 'c': 'foofoofoofoo'} + ``` + + Example: Using a Topic channel + ```python + from langgraph.channels import LastValue, EphemeralValue, Topic + from langgraph.pregel import Pregel, NodeBuilder + + node1 = ( + NodeBuilder().subscribe_only("a") + .do(lambda x: x + x) + .write_to("b", "c") + ) + + node2 = ( + NodeBuilder().subscribe_only("b") + .do(lambda x: x + x) + .write_to("c") + ) + + + app = Pregel( + nodes={"node1": node1, "node2": node2}, + channels={ + "a": EphemeralValue(str), + "b": EphemeralValue(str), + "c": Topic(str, accumulate=True), + }, + input_channels=["a"], + output_channels=["c"], + ) + + app.invoke({"a": "foo"}) + ``` + + ```pycon + {'c': ['foofoo', 'foofoofoofoo']} + ``` + + Example: Using a BinaryOperatorAggregate channel + ```python + from langgraph.channels import EphemeralValue, BinaryOperatorAggregate + from langgraph.pregel import Pregel, NodeBuilder + + + node1 = ( + NodeBuilder().subscribe_only("a") + .do(lambda x: x + x) + .write_to("b", "c") + ) + + node2 = ( + NodeBuilder().subscribe_only("b") + .do(lambda x: x + x) + .write_to("c") + ) + + + def reducer(current, update): + if current: + return current + " | " + update + else: + return update + + app = Pregel( + nodes={"node1": node1, "node2": node2}, + channels={ + "a": EphemeralValue(str), + "b": EphemeralValue(str), + "c": BinaryOperatorAggregate(str, operator=reducer), + }, + input_channels=["a"], + output_channels=["c"] + ) + + app.invoke({"a": "foo"}) + ``` + + ```con + {'c': 'foofoo | foofoofoofoo'} + ``` + + Example: Introducing a cycle + This example demonstrates how to introduce a cycle in the graph, by having + a chain write to a channel it subscribes to. Execution will continue + until a None value is written to the channel. + + ```python + from langgraph.channels import EphemeralValue + from langgraph.pregel import Pregel, NodeBuilder, ChannelWriteEntry + + example_node = ( + NodeBuilder().subscribe_only("value") + .do(lambda x: x + x if len(x) < 10 else None) + .write_to(ChannelWriteEntry(channel="value", skip_none=True)) + ) + + app = Pregel( + nodes={"example_node": example_node}, + channels={ + "value": EphemeralValue(str), + }, + input_channels=["value"], + output_channels=["value"] + ) + + app.invoke({"value": "a"}) + ``` + + ```con + {'value': 'aaaaaaaaaaaaaaaa'} + ``` + """ + + nodes: dict[str, PregelNode] + + channels: dict[str, BaseChannel | ManagedValueSpec] + + stream_mode: StreamMode = "values" + """Mode to stream output, defaults to 'values'.""" + + stream_eager: bool = False + """Whether to force emitting stream events eagerly, automatically turned on + for stream_mode "messages" and "custom".""" + + output_channels: str | Sequence[str] + + stream_channels: str | Sequence[str] | None = None + """Channels to stream, defaults to all channels not in reserved channels""" + + interrupt_after_nodes: All | Sequence[str] + + interrupt_before_nodes: All | Sequence[str] + + input_channels: str | Sequence[str] + + step_timeout: float | None = None + """Maximum time to wait for a step to complete, in seconds. Defaults to None.""" + + debug: bool + """Whether to print debug information during execution. Defaults to False.""" + + checkpointer: Checkpointer = None + """Checkpointer used to save and load graph state. Defaults to None.""" + + store: BaseStore | None = None + """Memory store to use for SharedValues. Defaults to None.""" + + cache: BaseCache | None = None + """Cache to use for storing node results. Defaults to None.""" + + retry_policy: Sequence[RetryPolicy] = () + """Retry policies to use when running tasks. Empty set disables retries.""" + + cache_policy: CachePolicy | None = None + """Cache policy to use for all nodes. Can be overridden by individual nodes. + Defaults to None.""" + + config_type: type[Any] | None = None + + config: RunnableConfig | None = None + + name: str = "LangGraph" + + trigger_to_nodes: Mapping[str, Sequence[str]] + + def __init__( + self, + *, + nodes: dict[str, PregelNode | NodeBuilder], + channels: dict[str, BaseChannel | ManagedValueSpec] | None, + auto_validate: bool = True, + stream_mode: StreamMode = "values", + stream_eager: bool = False, + output_channels: str | Sequence[str], + stream_channels: str | Sequence[str] | None = None, + interrupt_after_nodes: All | Sequence[str] = (), + interrupt_before_nodes: All | Sequence[str] = (), + input_channels: str | Sequence[str], + step_timeout: float | None = None, + debug: bool | None = None, + checkpointer: BaseCheckpointSaver | None = None, + store: BaseStore | None = None, + cache: BaseCache | None = None, + retry_policy: RetryPolicy | Sequence[RetryPolicy] = (), + cache_policy: CachePolicy | None = None, + config_type: type[Any] | None = None, + config: RunnableConfig | None = None, + trigger_to_nodes: Mapping[str, Sequence[str]] | None = None, + name: str = "LangGraph", + ) -> None: + self.nodes = { + k: v.build() if isinstance(v, NodeBuilder) else v for k, v in nodes.items() + } + self.channels = channels or {} + if TASKS in self.channels and not isinstance(self.channels[TASKS], Topic): + raise ValueError( + f"Channel '{TASKS}' is reserved and cannot be used in the graph." + ) + else: + self.channels[TASKS] = Topic(Send, accumulate=False) + self.stream_mode = stream_mode + self.stream_eager = stream_eager + self.output_channels = output_channels + self.stream_channels = stream_channels + self.interrupt_after_nodes = interrupt_after_nodes + self.interrupt_before_nodes = interrupt_before_nodes + self.input_channels = input_channels + self.step_timeout = step_timeout + self.debug = debug if debug is not None else get_debug() + self.checkpointer = checkpointer + self.store = store + self.cache = cache + self.retry_policy = ( + (retry_policy,) if isinstance(retry_policy, RetryPolicy) else retry_policy + ) + self.cache_policy = cache_policy + self.config_type = config_type + self.config = config + self.trigger_to_nodes = trigger_to_nodes or {} + self.name = name + if auto_validate: + self.validate() + + def get_graph( + self, config: RunnableConfig | None = None, *, xray: int | bool = False + ) -> Graph: + """Return a drawable representation of the computation graph.""" + # gather subgraphs + if xray: + subgraphs = { + k: v.get_graph( + config, + xray=xray if isinstance(xray, bool) or xray <= 0 else xray - 1, + ) + for k, v in self.get_subgraphs() + } + else: + subgraphs = {} + + return draw_graph( + merge_configs(self.config, config), + nodes=self.nodes, + specs=self.channels, + input_channels=self.input_channels, + interrupt_after_nodes=self.interrupt_after_nodes, + interrupt_before_nodes=self.interrupt_before_nodes, + trigger_to_nodes=self.trigger_to_nodes, + checkpointer=self.checkpointer, + subgraphs=subgraphs, + ) + + async def aget_graph( + self, config: RunnableConfig | None = None, *, xray: int | bool = False + ) -> Graph: + """Return a drawable representation of the computation graph.""" + + # gather subgraphs + if xray: + subpregels: dict[str, PregelProtocol] = { + k: v async for k, v in self.aget_subgraphs() + } + subgraphs = { + k: v + for k, v in zip( + subpregels, + await asyncio.gather( + *( + p.aget_graph( + config, + xray=xray + if isinstance(xray, bool) or xray <= 0 + else xray - 1, + ) + for p in subpregels.values() + ) + ), + ) + } + else: + subgraphs = {} + + return draw_graph( + merge_configs(self.config, config), + nodes=self.nodes, + specs=self.channels, + input_channels=self.input_channels, + interrupt_after_nodes=self.interrupt_after_nodes, + interrupt_before_nodes=self.interrupt_before_nodes, + trigger_to_nodes=self.trigger_to_nodes, + checkpointer=self.checkpointer, + subgraphs=subgraphs, + ) + + def _repr_mimebundle_(self, **kwargs: Any) -> dict[str, Any]: + """Mime bundle used by Jupyter to display the graph""" + return { + "text/plain": repr(self), + "image/png": self.get_graph().draw_mermaid_png(), + } + + def copy(self, update: dict[str, Any] | None = None) -> Self: + attrs = {k: v for k, v in self.__dict__.items() if k != "__orig_class__"} + attrs.update(update or {}) + return self.__class__(**attrs) + + def with_config(self, config: RunnableConfig | None = None, **kwargs: Any) -> Self: + """Create a copy of the Pregel object with an updated config.""" + return self.copy( + {"config": merge_configs(self.config, config, cast(RunnableConfig, kwargs))} + ) + + def validate(self) -> Self: + validate_graph( + self.nodes, + {k: v for k, v in self.channels.items() if isinstance(v, BaseChannel)}, + {k: v for k, v in self.channels.items() if not isinstance(v, BaseChannel)}, + self.input_channels, + self.output_channels, + self.stream_channels, + self.interrupt_after_nodes, + self.interrupt_before_nodes, + ) + self.trigger_to_nodes = _trigger_to_nodes(self.nodes) + return self + + def config_schema(self, *, include: Sequence[str] | None = None) -> type[BaseModel]: + include = include or [] + fields = { + **({"configurable": (self.config_type, None)} if self.config_type else {}), + **{ + field_name: (field_type, None) + for field_name, field_type in get_type_hints(RunnableConfig).items() + if field_name in [i for i in include if i != "configurable"] + }, + } + return create_model(self.get_name("Config"), field_definitions=fields) + + def get_config_jsonschema( + self, *, include: Sequence[str] | None = None + ) -> dict[str, Any]: + schema = self.config_schema(include=include) + if hasattr(schema, "model_json_schema"): + return schema.model_json_schema() + else: + return schema.schema() + + @property + def InputType(self) -> Any: + if isinstance(self.input_channels, str): + channel = self.channels[self.input_channels] + if isinstance(channel, BaseChannel): + return channel.UpdateType + + def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]: + config = merge_configs(self.config, config) + if isinstance(self.input_channels, str): + return super().get_input_schema(config) + else: + return create_model( + self.get_name("Input"), + field_definitions={ + k: (c.UpdateType, None) + for k in self.input_channels or self.channels.keys() + if (c := self.channels[k]) and isinstance(c, BaseChannel) + }, + ) + + def get_input_jsonschema( + self, config: RunnableConfig | None = None + ) -> dict[str, Any]: + schema = self.get_input_schema(config) + if hasattr(schema, "model_json_schema"): + return schema.model_json_schema() + else: + return schema.schema() + + @property + def OutputType(self) -> Any: + if isinstance(self.output_channels, str): + channel = self.channels[self.output_channels] + if isinstance(channel, BaseChannel): + return channel.ValueType + + def get_output_schema( + self, config: RunnableConfig | None = None + ) -> type[BaseModel]: + config = merge_configs(self.config, config) + if isinstance(self.output_channels, str): + return super().get_output_schema(config) + else: + return create_model( + self.get_name("Output"), + field_definitions={ + k: (c.ValueType, None) + for k in self.output_channels + if (c := self.channels[k]) and isinstance(c, BaseChannel) + }, + ) + + def get_output_jsonschema( + self, config: RunnableConfig | None = None + ) -> dict[str, Any]: + schema = self.get_output_schema(config) + if hasattr(schema, "model_json_schema"): + return schema.model_json_schema() + else: + return schema.schema() + + @property + def stream_channels_list(self) -> Sequence[str]: + stream_channels = self.stream_channels_asis + return ( + [stream_channels] if isinstance(stream_channels, str) else stream_channels + ) + + @property + def stream_channels_asis(self) -> str | Sequence[str]: + return self.stream_channels or [ + k for k in self.channels if isinstance(self.channels[k], BaseChannel) + ] + + def get_subgraphs( + self, *, namespace: str | None = None, recurse: bool = False + ) -> Iterator[tuple[str, PregelProtocol]]: + """Get the subgraphs of the graph. + + Args: + namespace: The namespace to filter the subgraphs by. + recurse: Whether to recurse into the subgraphs. + If False, only the immediate subgraphs will be returned. + + Returns: + Iterator[tuple[str, PregelProtocol]]: An iterator of the (namespace, subgraph) pairs. + """ + for name, node in self.nodes.items(): + # filter by prefix + if namespace is not None: + if not namespace.startswith(name): + continue + + # find the subgraph, if any + graph = node.subgraphs[0] if node.subgraphs else None + + # if found, yield recursively + if graph: + if name == namespace: + yield name, graph + return # we found it, stop searching + if namespace is None: + yield name, graph + if recurse and isinstance(graph, Pregel): + if namespace is not None: + namespace = namespace[len(name) + 1 :] + yield from ( + (f"{name}{NS_SEP}{n}", s) + for n, s in graph.get_subgraphs( + namespace=namespace, recurse=recurse + ) + ) + + async def aget_subgraphs( + self, *, namespace: str | None = None, recurse: bool = False + ) -> AsyncIterator[tuple[str, PregelProtocol]]: + """Get the subgraphs of the graph. + + Args: + namespace: The namespace to filter the subgraphs by. + recurse: Whether to recurse into the subgraphs. + If False, only the immediate subgraphs will be returned. + + Returns: + AsyncIterator[tuple[str, PregelProtocol]]: An iterator of the (namespace, subgraph) pairs. + """ + for name, node in self.get_subgraphs(namespace=namespace, recurse=recurse): + yield name, node + + def _migrate_checkpoint(self, checkpoint: Checkpoint) -> None: + """Migrate a saved checkpoint to new channel layout.""" + if checkpoint["v"] < 4 and checkpoint.get("pending_sends"): + pending_sends: list[Send] = checkpoint.pop("pending_sends") + checkpoint["channel_values"][TASKS] = pending_sends + checkpoint["channel_versions"][TASKS] = max( + checkpoint["channel_versions"].values() + ) + + def _prepare_state_snapshot( + self, + config: RunnableConfig, + saved: CheckpointTuple | None, + recurse: BaseCheckpointSaver | None = None, + apply_pending_writes: bool = False, + ) -> StateSnapshot: + if not saved: + return StateSnapshot( + values={}, + next=(), + config=config, + metadata=None, + created_at=None, + parent_config=None, + tasks=(), + interrupts=(), + ) + + # migrate checkpoint if needed + self._migrate_checkpoint(saved.checkpoint) + + step = saved.metadata.get("step", -1) + 1 + stop = step + 2 + channels, managed = channels_from_checkpoint( + self.channels, + saved.checkpoint, + ) + # tasks for this checkpoint + next_tasks = prepare_next_tasks( + saved.checkpoint, + saved.pending_writes or [], + self.nodes, + channels, + managed, + saved.config, + step, + stop, + for_execution=True, + store=self.store, + checkpointer=( + self.checkpointer + if isinstance(self.checkpointer, BaseCheckpointSaver) + else None + ), + manager=None, + ) + # get the subgraphs + subgraphs = dict(self.get_subgraphs()) + parent_ns = saved.config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + task_states: dict[str, RunnableConfig | StateSnapshot] = {} + for task in next_tasks.values(): + if task.name not in subgraphs: + continue + # assemble checkpoint_ns for this task + task_ns = f"{task.name}{NS_END}{task.id}" + if parent_ns: + task_ns = f"{parent_ns}{NS_SEP}{task_ns}" + if not recurse: + # set config as signal that subgraph checkpoints exist + config = { + CONF: { + "thread_id": saved.config[CONF]["thread_id"], + CONFIG_KEY_CHECKPOINT_NS: task_ns, + } + } + task_states[task.id] = config + else: + # get the state of the subgraph + config = { + CONF: { + CONFIG_KEY_CHECKPOINTER: recurse, + "thread_id": saved.config[CONF]["thread_id"], + CONFIG_KEY_CHECKPOINT_NS: task_ns, + } + } + task_states[task.id] = subgraphs[task.name].get_state( + config, subgraphs=True + ) + # apply pending writes + if null_writes := [ + w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID + ]: + apply_writes( + saved.checkpoint, + channels, + [PregelTaskWrites((), INPUT, null_writes, [])], + None, + self.trigger_to_nodes, + ) + if apply_pending_writes and saved.pending_writes: + for tid, k, v in saved.pending_writes: + if k in (ERROR, INTERRUPT): + continue + if tid not in next_tasks: + continue + next_tasks[tid].writes.append((k, v)) + if tasks := [t for t in next_tasks.values() if t.writes]: + apply_writes( + saved.checkpoint, channels, tasks, None, self.trigger_to_nodes + ) + tasks_with_writes = tasks_w_writes( + next_tasks.values(), + saved.pending_writes, + task_states, + self.stream_channels_asis, + ) + # assemble the state snapshot + return StateSnapshot( + read_channels(channels, self.stream_channels_asis), + tuple(t.name for t in next_tasks.values() if not t.writes), + patch_checkpoint_map(saved.config, saved.metadata), + saved.metadata, + saved.checkpoint["ts"], + patch_checkpoint_map(saved.parent_config, saved.metadata), + tasks_with_writes, + tuple([i for task in tasks_with_writes for i in task.interrupts]), + ) + + async def _aprepare_state_snapshot( + self, + config: RunnableConfig, + saved: CheckpointTuple | None, + recurse: BaseCheckpointSaver | None = None, + apply_pending_writes: bool = False, + ) -> StateSnapshot: + if not saved: + return StateSnapshot( + values={}, + next=(), + config=config, + metadata=None, + created_at=None, + parent_config=None, + tasks=(), + interrupts=(), + ) + + # migrate checkpoint if needed + self._migrate_checkpoint(saved.checkpoint) + + step = saved.metadata.get("step", -1) + 1 + stop = step + 2 + channels, managed = channels_from_checkpoint( + self.channels, + saved.checkpoint, + ) + # tasks for this checkpoint + next_tasks = prepare_next_tasks( + saved.checkpoint, + saved.pending_writes or [], + self.nodes, + channels, + managed, + saved.config, + step, + stop, + for_execution=True, + store=self.store, + checkpointer=( + self.checkpointer + if isinstance(self.checkpointer, BaseCheckpointSaver) + else None + ), + manager=None, + ) + # get the subgraphs + subgraphs = {n: g async for n, g in self.aget_subgraphs()} + parent_ns = saved.config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + task_states: dict[str, RunnableConfig | StateSnapshot] = {} + for task in next_tasks.values(): + if task.name not in subgraphs: + continue + # assemble checkpoint_ns for this task + task_ns = f"{task.name}{NS_END}{task.id}" + if parent_ns: + task_ns = f"{parent_ns}{NS_SEP}{task_ns}" + if not recurse: + # set config as signal that subgraph checkpoints exist + config = { + CONF: { + "thread_id": saved.config[CONF]["thread_id"], + CONFIG_KEY_CHECKPOINT_NS: task_ns, + } + } + task_states[task.id] = config + else: + # get the state of the subgraph + config = { + CONF: { + CONFIG_KEY_CHECKPOINTER: recurse, + "thread_id": saved.config[CONF]["thread_id"], + CONFIG_KEY_CHECKPOINT_NS: task_ns, + } + } + task_states[task.id] = await subgraphs[task.name].aget_state( + config, subgraphs=True + ) + # apply pending writes + if null_writes := [ + w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID + ]: + apply_writes( + saved.checkpoint, + channels, + [PregelTaskWrites((), INPUT, null_writes, [])], + None, + self.trigger_to_nodes, + ) + if apply_pending_writes and saved.pending_writes: + for tid, k, v in saved.pending_writes: + if k in (ERROR, INTERRUPT): + continue + if tid not in next_tasks: + continue + next_tasks[tid].writes.append((k, v)) + if tasks := [t for t in next_tasks.values() if t.writes]: + apply_writes( + saved.checkpoint, channels, tasks, None, self.trigger_to_nodes + ) + + tasks_with_writes = tasks_w_writes( + next_tasks.values(), + saved.pending_writes, + task_states, + self.stream_channels_asis, + ) + # assemble the state snapshot + return StateSnapshot( + read_channels(channels, self.stream_channels_asis), + tuple(t.name for t in next_tasks.values() if not t.writes), + patch_checkpoint_map(saved.config, saved.metadata), + saved.metadata, + saved.checkpoint["ts"], + patch_checkpoint_map(saved.parent_config, saved.metadata), + tasks_with_writes, + tuple([i for task in tasks_with_writes for i in task.interrupts]), + ) + + def get_state( + self, config: RunnableConfig, *, subgraphs: bool = False + ) -> StateSnapshot: + """Get the current state of the graph.""" + checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( + CONFIG_KEY_CHECKPOINTER, self.checkpointer + ) + if not checkpointer: + raise ValueError("No checkpointer set") + + if ( + checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: + # remove task_ids from checkpoint_ns + recast = recast_checkpoint_ns(checkpoint_ns) + # find the subgraph with the matching name + for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): + return pregel.get_state( + patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), + subgraphs=subgraphs, + ) + else: + raise ValueError(f"Subgraph {recast} not found") + + config = merge_configs(self.config, config) if self.config else config + if self.checkpointer is True: + ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) + config = merge_configs( + config, {CONF: {CONFIG_KEY_CHECKPOINT_NS: recast_checkpoint_ns(ns)}} + ) + thread_id = config[CONF][CONFIG_KEY_THREAD_ID] + if not isinstance(thread_id, str): + config[CONF][CONFIG_KEY_THREAD_ID] = str(thread_id) + + saved = checkpointer.get_tuple(config) + return self._prepare_state_snapshot( + config, + saved, + recurse=checkpointer if subgraphs else None, + apply_pending_writes=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], + ) + + async def aget_state( + self, config: RunnableConfig, *, subgraphs: bool = False + ) -> StateSnapshot: + """Get the current state of the graph.""" + checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( + CONFIG_KEY_CHECKPOINTER, self.checkpointer + ) + if not checkpointer: + raise ValueError("No checkpointer set") + + if ( + checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: + # remove task_ids from checkpoint_ns + recast = recast_checkpoint_ns(checkpoint_ns) + # find the subgraph with the matching name + async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): + return await pregel.aget_state( + patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), + subgraphs=subgraphs, + ) + else: + raise ValueError(f"Subgraph {recast} not found") + + config = merge_configs(self.config, config) if self.config else config + if self.checkpointer is True: + ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) + config = merge_configs( + config, {CONF: {CONFIG_KEY_CHECKPOINT_NS: recast_checkpoint_ns(ns)}} + ) + thread_id = config[CONF][CONFIG_KEY_THREAD_ID] + if not isinstance(thread_id, str): + config[CONF][CONFIG_KEY_THREAD_ID] = str(thread_id) + + saved = await checkpointer.aget_tuple(config) + return await self._aprepare_state_snapshot( + config, + saved, + recurse=checkpointer if subgraphs else None, + apply_pending_writes=CONFIG_KEY_CHECKPOINT_ID not in config[CONF], + ) + + def get_state_history( + self, + config: RunnableConfig, + *, + filter: dict[str, Any] | None = None, + before: RunnableConfig | None = None, + limit: int | None = None, + ) -> Iterator[StateSnapshot]: + """Get the history of the state of the graph.""" + config = ensure_config(config) + checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( + CONFIG_KEY_CHECKPOINTER, self.checkpointer + ) + if not checkpointer: + raise ValueError("No checkpointer set") + + if ( + checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: + # remove task_ids from checkpoint_ns + recast = recast_checkpoint_ns(checkpoint_ns) + # find the subgraph with the matching name + for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): + yield from pregel.get_state_history( + patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), + filter=filter, + before=before, + limit=limit, + ) + return + else: + raise ValueError(f"Subgraph {recast} not found") + + config = merge_configs( + self.config, + config, + { + CONF: { + CONFIG_KEY_CHECKPOINT_NS: checkpoint_ns, + CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID]), + } + }, + ) + # eagerly consume list() to avoid holding up the db cursor + for checkpoint_tuple in list( + checkpointer.list(config, before=before, limit=limit, filter=filter) + ): + yield self._prepare_state_snapshot( + checkpoint_tuple.config, checkpoint_tuple + ) + + async def aget_state_history( + self, + config: RunnableConfig, + *, + filter: dict[str, Any] | None = None, + before: RunnableConfig | None = None, + limit: int | None = None, + ) -> AsyncIterator[StateSnapshot]: + """Asynchronously get the history of the state of the graph.""" + config = ensure_config(config) + checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( + CONFIG_KEY_CHECKPOINTER, self.checkpointer + ) + if not checkpointer: + raise ValueError("No checkpointer set") + + if ( + checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: + # remove task_ids from checkpoint_ns + recast = recast_checkpoint_ns(checkpoint_ns) + # find the subgraph with the matching name + async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): + async for state in pregel.aget_state_history( + patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), + filter=filter, + before=before, + limit=limit, + ): + yield state + return + else: + raise ValueError(f"Subgraph {recast} not found") + + config = merge_configs( + self.config, + config, + { + CONF: { + CONFIG_KEY_CHECKPOINT_NS: checkpoint_ns, + CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID]), + } + }, + ) + # eagerly consume list() to avoid holding up the db cursor + for checkpoint_tuple in [ + c + async for c in checkpointer.alist( + config, before=before, limit=limit, filter=filter + ) + ]: + yield await self._aprepare_state_snapshot( + checkpoint_tuple.config, checkpoint_tuple + ) + + def bulk_update_state( + self, + config: RunnableConfig, + supersteps: Sequence[Sequence[StateUpdate]], + ) -> RunnableConfig: + """Apply updates to the graph state in bulk. Requires a checkpointer to be set. + + Args: + config: The config to apply the updates to. + supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state. + Each update is a tuple of the form `(values, as_node, task_id)` where task_id is optional. + + Raises: + ValueError: If no checkpointer is set or no updates are provided. + InvalidUpdateError: If an invalid update is provided. + + Returns: + RunnableConfig: The updated config. + """ + + checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( + CONFIG_KEY_CHECKPOINTER, self.checkpointer + ) + if not checkpointer: + raise ValueError("No checkpointer set") + + if len(supersteps) == 0: + raise ValueError("No supersteps provided") + + if any(len(u) == 0 for u in supersteps): + raise ValueError("No updates provided") + + # delegate to subgraph + if ( + checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: + # remove task_ids from checkpoint_ns + recast = recast_checkpoint_ns(checkpoint_ns) + # find the subgraph with the matching name + for _, pregel in self.get_subgraphs(namespace=recast, recurse=True): + return pregel.bulk_update_state( + patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), + supersteps, + ) + else: + raise ValueError(f"Subgraph {recast} not found") + + def perform_superstep( + input_config: RunnableConfig, updates: Sequence[StateUpdate] + ) -> RunnableConfig: + # get last checkpoint + config = ensure_config(self.config, input_config) + saved = checkpointer.get_tuple(config) + if saved is not None: + self._migrate_checkpoint(saved.checkpoint) + checkpoint = ( + copy_checkpoint(saved.checkpoint) if saved else empty_checkpoint() + ) + checkpoint_previous_versions = ( + saved.checkpoint["channel_versions"].copy() if saved else {} + ) + step = saved.metadata.get("step", -1) if saved else -1 + # merge configurable fields with previous checkpoint config + checkpoint_config = patch_configurable( + config, + { + CONFIG_KEY_CHECKPOINT_NS: config[CONF].get( + CONFIG_KEY_CHECKPOINT_NS, "" + ) + }, + ) + if saved: + checkpoint_config = patch_configurable(config, saved.config[CONF]) + channels, managed = channels_from_checkpoint( + self.channels, + checkpoint, + ) + values, as_node = updates[0][:2] + + # no values as END, just clear all tasks + if values is None and as_node == END: + if len(updates) > 1: + raise InvalidUpdateError( + "Cannot apply multiple updates when clearing state" + ) + + if saved is not None: + # tasks for this checkpoint + next_tasks = prepare_next_tasks( + checkpoint, + saved.pending_writes or [], + self.nodes, + channels, + managed, + saved.config, + step + 1, + step + 3, + for_execution=True, + store=self.store, + checkpointer=checkpointer, + manager=None, + ) + # apply null writes + if null_writes := [ + w[1:] + for w in saved.pending_writes or [] + if w[0] == NULL_TASK_ID + ]: + apply_writes( + checkpoint, + channels, + [PregelTaskWrites((), INPUT, null_writes, [])], + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + # apply writes from tasks that already ran + for tid, k, v in saved.pending_writes or []: + if k in (ERROR, INTERRUPT): + continue + if tid not in next_tasks: + continue + next_tasks[tid].writes.append((k, v)) + # clear all current tasks + apply_writes( + checkpoint, + channels, + next_tasks.values(), + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + # save checkpoint + next_config = checkpointer.put( + checkpoint_config, + create_checkpoint(checkpoint, channels, step), + { + "source": "update", + "step": step + 1, + "parents": saved.metadata.get("parents", {}) if saved else {}, + }, + get_new_channel_versions( + checkpoint_previous_versions, + checkpoint["channel_versions"], + ), + ) + return patch_checkpoint_map( + next_config, saved.metadata if saved else None + ) + + # act as an input + if as_node == INPUT: + if len(updates) > 1: + raise InvalidUpdateError( + "Cannot apply multiple updates when updating as input" + ) + + if input_writes := deque(map_input(self.input_channels, values)): + apply_writes( + checkpoint, + channels, + [PregelTaskWrites((), INPUT, input_writes, [])], + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + + # apply input write to channels + next_step = ( + step + 1 + if saved and saved.metadata.get("step") is not None + else -1 + ) + next_config = checkpointer.put( + checkpoint_config, + create_checkpoint(checkpoint, channels, next_step), + { + "source": "input", + "step": next_step, + "parents": saved.metadata.get("parents", {}) + if saved + else {}, + }, + get_new_channel_versions( + checkpoint_previous_versions, + checkpoint["channel_versions"], + ), + ) + + # store the writes + checkpointer.put_writes( + next_config, + input_writes, + str(uuid5(UUID(checkpoint["id"]), INPUT)), + ) + + return patch_checkpoint_map( + next_config, saved.metadata if saved else None + ) + else: + raise InvalidUpdateError( + f"Received no input writes for {self.input_channels}" + ) + + # copy checkpoint + if as_node == "__copy__": + if len(updates) > 1: + raise InvalidUpdateError( + "Cannot copy checkpoint with multiple updates" + ) + + if saved is None: + raise InvalidUpdateError("Cannot copy a non-existent checkpoint") + + next_checkpoint = create_checkpoint(checkpoint, None, step) + + # copy checkpoint + next_config = checkpointer.put( + saved.parent_config + or patch_configurable( + saved.config, {CONFIG_KEY_CHECKPOINT_ID: None} + ), + next_checkpoint, + { + "source": "fork", + "step": step + 1, + "parents": saved.metadata.get("parents", {}), + }, + {}, + ) + + # we want to both clone a checkpoint and update state in one go. + # reuse the same task ID if possible. + if isinstance(values, list) and len(values) > 0: + # figure out the task IDs for the next update checkpoint + next_tasks = prepare_next_tasks( + next_checkpoint, + saved.pending_writes or [], + self.nodes, + channels, + managed, + next_config, + step + 2, + step + 4, + for_execution=True, + store=self.store, + checkpointer=checkpointer, + manager=None, + ) + + tasks_group_by = defaultdict(list) + user_group_by: dict[str, list[StateUpdate]] = defaultdict(list) + + for task in next_tasks.values(): + tasks_group_by[task.name].append(task.id) + + for item in values: + if not isinstance(item, Sequence): + raise InvalidUpdateError( + f"Invalid update item: {item} when copying checkpoint" + ) + + values, as_node = item[:2] + + user_group = user_group_by[as_node] + tasks_group = tasks_group_by[as_node] + + target_idx = len(user_group) + task_id = ( + tasks_group[target_idx] + if target_idx < len(tasks_group) + else None + ) + + user_group_by[as_node].append( + StateUpdate(values=values, as_node=as_node, task_id=task_id) + ) + + return perform_superstep( + patch_checkpoint_map(next_config, saved.metadata), + [item for lst in user_group_by.values() for item in lst], + ) + + return patch_checkpoint_map(next_config, saved.metadata) + + # apply pending writes, if not on specific checkpoint + if ( + CONFIG_KEY_CHECKPOINT_ID not in config[CONF] + and saved is not None + and saved.pending_writes + ): + # tasks for this checkpoint + next_tasks = prepare_next_tasks( + checkpoint, + saved.pending_writes, + self.nodes, + channels, + managed, + saved.config, + step + 1, + step + 3, + for_execution=True, + store=self.store, + checkpointer=checkpointer, + manager=None, + ) + # apply null writes + if null_writes := [ + w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID + ]: + apply_writes( + checkpoint, + channels, + [PregelTaskWrites((), INPUT, null_writes, [])], + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + # apply writes + for tid, k, v in saved.pending_writes: + if k in (ERROR, INTERRUPT): + continue + if tid not in next_tasks: + continue + next_tasks[tid].writes.append((k, v)) + if tasks := [t for t in next_tasks.values() if t.writes]: + apply_writes( + checkpoint, + channels, + tasks, + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + valid_updates: list[tuple[str, dict[str, Any] | None, str | None]] = [] + if len(updates) == 1: + values, as_node, task_id = updates[0] + # find last node that updated the state, if not provided + if as_node is None and len(self.nodes) == 1: + as_node = tuple(self.nodes)[0] + elif as_node is None and not any( + v + for vv in checkpoint["versions_seen"].values() + for v in vv.values() + ): + if ( + isinstance(self.input_channels, str) + and self.input_channels in self.nodes + ): + as_node = self.input_channels + elif as_node is None: + last_seen_by_node = sorted( + (v, n) + for n, seen in checkpoint["versions_seen"].items() + if n in self.nodes + for v in seen.values() + ) + # if two nodes updated the state at the same time, it's ambiguous + if last_seen_by_node: + if len(last_seen_by_node) == 1: + as_node = last_seen_by_node[0][1] + elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]: + as_node = last_seen_by_node[-1][1] + if as_node is None: + raise InvalidUpdateError("Ambiguous update, specify as_node") + if as_node not in self.nodes: + raise InvalidUpdateError(f"Node {as_node} does not exist") + valid_updates.append((as_node, values, task_id)) + else: + for values, as_node, task_id in updates: + if as_node is None: + raise InvalidUpdateError( + "as_node is required when applying multiple updates" + ) + if as_node not in self.nodes: + raise InvalidUpdateError(f"Node {as_node} does not exist") + + valid_updates.append((as_node, values, task_id)) + + run_tasks: list[PregelTaskWrites] = [] + run_task_ids: list[str] = [] + + for as_node, values, provided_task_id in valid_updates: + # create task to run all writers of the chosen node + writers = self.nodes[as_node].flat_writers + if not writers: + raise InvalidUpdateError(f"Node {as_node} has no writers") + writes: deque[tuple[str, Any]] = deque() + task = PregelTaskWrites((), as_node, writes, [INTERRUPT]) + task_id = provided_task_id or str( + uuid5(UUID(checkpoint["id"]), INTERRUPT) + ) + run_tasks.append(task) + run_task_ids.append(task_id) + run = RunnableSequence(*writers) if len(writers) > 1 else writers[0] + # execute task + run.invoke( + values, + patch_config( + config, + run_name=self.name + "UpdateState", + configurable={ + # deque.extend is thread-safe + CONFIG_KEY_SEND: writes.extend, + CONFIG_KEY_TASK_ID: task_id, + CONFIG_KEY_READ: partial( + local_read, + _scratchpad( + None, + [], + task_id, + "", + None, + step, + step + 2, + ), + channels, + managed, + task, + ), + }, + ), + ) + # save task writes + for task_id, task in zip(run_task_ids, run_tasks): + # channel writes are saved to current checkpoint + channel_writes = [w for w in task.writes if w[0] != PUSH] + if saved and channel_writes: + checkpointer.put_writes(checkpoint_config, channel_writes, task_id) + # apply to checkpoint and save + apply_writes( + checkpoint, + channels, + run_tasks, + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + checkpoint = create_checkpoint(checkpoint, channels, step + 1) + next_config = checkpointer.put( + checkpoint_config, + checkpoint, + { + "source": "update", + "step": step + 1, + "parents": saved.metadata.get("parents", {}) if saved else {}, + }, + get_new_channel_versions( + checkpoint_previous_versions, checkpoint["channel_versions"] + ), + ) + for task_id, task in zip(run_task_ids, run_tasks): + # save push writes + if push_writes := [w for w in task.writes if w[0] == PUSH]: + checkpointer.put_writes(next_config, push_writes, task_id) + + return patch_checkpoint_map(next_config, saved.metadata if saved else None) + + current_config = patch_configurable( + config, {CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID])} + ) + for superstep in supersteps: + current_config = perform_superstep(current_config, superstep) + return current_config + + async def abulk_update_state( + self, + config: RunnableConfig, + supersteps: Sequence[Sequence[StateUpdate]], + ) -> RunnableConfig: + """Asynchronously apply updates to the graph state in bulk. Requires a checkpointer to be set. + + Args: + config: The config to apply the updates to. + supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state. + Each update is a tuple of the form `(values, as_node, task_id)` where task_id is optional. + + Raises: + ValueError: If no checkpointer is set or no updates are provided. + InvalidUpdateError: If an invalid update is provided. + + Returns: + RunnableConfig: The updated config. + """ + + checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get( + CONFIG_KEY_CHECKPOINTER, self.checkpointer + ) + if not checkpointer: + raise ValueError("No checkpointer set") + + if len(supersteps) == 0: + raise ValueError("No supersteps provided") + + if any(len(u) == 0 for u in supersteps): + raise ValueError("No updates provided") + + # delegate to subgraph + if ( + checkpoint_ns := config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "") + ) and CONFIG_KEY_CHECKPOINTER not in config[CONF]: + # remove task_ids from checkpoint_ns + recast = recast_checkpoint_ns(checkpoint_ns) + # find the subgraph with the matching name + async for _, pregel in self.aget_subgraphs(namespace=recast, recurse=True): + return await pregel.abulk_update_state( + patch_configurable(config, {CONFIG_KEY_CHECKPOINTER: checkpointer}), + supersteps, + ) + else: + raise ValueError(f"Subgraph {recast} not found") + + async def aperform_superstep( + input_config: RunnableConfig, updates: Sequence[StateUpdate] + ) -> RunnableConfig: + # get last checkpoint + config = ensure_config(self.config, input_config) + saved = await checkpointer.aget_tuple(config) + if saved is not None: + self._migrate_checkpoint(saved.checkpoint) + checkpoint = ( + copy_checkpoint(saved.checkpoint) if saved else empty_checkpoint() + ) + checkpoint_previous_versions = ( + saved.checkpoint["channel_versions"].copy() if saved else {} + ) + step = saved.metadata.get("step", -1) if saved else -1 + # merge configurable fields with previous checkpoint config + checkpoint_config = patch_configurable( + config, + { + CONFIG_KEY_CHECKPOINT_NS: config[CONF].get( + CONFIG_KEY_CHECKPOINT_NS, "" + ) + }, + ) + if saved: + checkpoint_config = patch_configurable(config, saved.config[CONF]) + channels, managed = channels_from_checkpoint( + self.channels, + checkpoint, + ) + values, as_node = updates[0][:2] + # no values, just clear all tasks + if values is None and as_node == END: + if len(updates) > 1: + raise InvalidUpdateError( + "Cannot apply multiple updates when clearing state" + ) + if saved is not None: + # tasks for this checkpoint + next_tasks = prepare_next_tasks( + checkpoint, + saved.pending_writes or [], + self.nodes, + channels, + managed, + saved.config, + step + 1, + step + 3, + for_execution=True, + store=self.store, + checkpointer=checkpointer, + manager=None, + ) + # apply null writes + if null_writes := [ + w[1:] + for w in saved.pending_writes or [] + if w[0] == NULL_TASK_ID + ]: + apply_writes( + checkpoint, + channels, + [PregelTaskWrites((), INPUT, null_writes, [])], + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + # apply writes from tasks that already ran + for tid, k, v in saved.pending_writes or []: + if k in (ERROR, INTERRUPT): + continue + if tid not in next_tasks: + continue + next_tasks[tid].writes.append((k, v)) + # clear all current tasks + apply_writes( + checkpoint, + channels, + next_tasks.values(), + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + # save checkpoint + next_config = await checkpointer.aput( + checkpoint_config, + create_checkpoint(checkpoint, channels, step), + { + "source": "update", + "step": step + 1, + "parents": saved.metadata.get("parents", {}) if saved else {}, + }, + get_new_channel_versions( + checkpoint_previous_versions, checkpoint["channel_versions"] + ), + ) + return patch_checkpoint_map( + next_config, saved.metadata if saved else None + ) + + # act as an input + if as_node == INPUT: + if len(updates) > 1: + raise InvalidUpdateError( + "Cannot apply multiple updates when updating as input" + ) + + if input_writes := deque(map_input(self.input_channels, values)): + apply_writes( + checkpoint, + channels, + [PregelTaskWrites((), INPUT, input_writes, [])], + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + + # apply input write to channels + next_step = ( + step + 1 + if saved and saved.metadata.get("step") is not None + else -1 + ) + next_config = await checkpointer.aput( + checkpoint_config, + create_checkpoint(checkpoint, channels, next_step), + { + "source": "input", + "step": next_step, + "parents": saved.metadata.get("parents", {}) + if saved + else {}, + }, + get_new_channel_versions( + checkpoint_previous_versions, + checkpoint["channel_versions"], + ), + ) + + # store the writes + await checkpointer.aput_writes( + next_config, + input_writes, + str(uuid5(UUID(checkpoint["id"]), INPUT)), + ) + + return patch_checkpoint_map( + next_config, saved.metadata if saved else None + ) + else: + raise InvalidUpdateError( + f"Received no input writes for {self.input_channels}" + ) + + # no values, copy checkpoint + if as_node == "__copy__": + if len(updates) > 1: + raise InvalidUpdateError( + "Cannot copy checkpoint with multiple updates" + ) + + if saved is None: + raise InvalidUpdateError("Cannot copy a non-existent checkpoint") + + next_checkpoint = create_checkpoint(checkpoint, None, step) + + # copy checkpoint + next_config = await checkpointer.aput( + saved.parent_config + or patch_configurable( + saved.config, {CONFIG_KEY_CHECKPOINT_ID: None} + ), + next_checkpoint, + { + "source": "fork", + "step": step + 1, + "parents": saved.metadata.get("parents", {}), + }, + {}, + ) + + # we want to both clone a checkpoint and update state in one go. + # reuse the same task ID if possible. + if isinstance(values, list) and len(values) > 0: + # figure out the task IDs for the next update checkpoint + next_tasks = prepare_next_tasks( + next_checkpoint, + saved.pending_writes or [], + self.nodes, + channels, + managed, + next_config, + step + 2, + step + 4, + for_execution=True, + store=self.store, + checkpointer=checkpointer, + manager=None, + ) + + tasks_group_by = defaultdict(list) + user_group_by: dict[str, list[StateUpdate]] = defaultdict(list) + + for task in next_tasks.values(): + tasks_group_by[task.name].append(task.id) + + for item in values: + if not isinstance(item, Sequence): + raise InvalidUpdateError( + f"Invalid update item: {item} when copying checkpoint" + ) + + values, as_node = item[:2] + user_group = user_group_by[as_node] + tasks_group = tasks_group_by[as_node] + + target_idx = len(user_group) + task_id = ( + tasks_group[target_idx] + if target_idx < len(tasks_group) + else None + ) + + user_group_by[as_node].append( + StateUpdate(values=values, as_node=as_node, task_id=task_id) + ) + + return await aperform_superstep( + patch_checkpoint_map(next_config, saved.metadata), + [item for lst in user_group_by.values() for item in lst], + ) + + return patch_checkpoint_map( + next_config, saved.metadata if saved else None + ) + # apply pending writes, if not on specific checkpoint + if ( + CONFIG_KEY_CHECKPOINT_ID not in config[CONF] + and saved is not None + and saved.pending_writes + ): + # tasks for this checkpoint + next_tasks = prepare_next_tasks( + checkpoint, + saved.pending_writes, + self.nodes, + channels, + managed, + saved.config, + step + 1, + step + 3, + for_execution=True, + store=self.store, + checkpointer=checkpointer, + manager=None, + ) + # apply null writes + if null_writes := [ + w[1:] for w in saved.pending_writes or [] if w[0] == NULL_TASK_ID + ]: + apply_writes( + checkpoint, + channels, + [PregelTaskWrites((), INPUT, null_writes, [])], + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + for tid, k, v in saved.pending_writes: + if k in (ERROR, INTERRUPT): + continue + if tid not in next_tasks: + continue + next_tasks[tid].writes.append((k, v)) + if tasks := [t for t in next_tasks.values() if t.writes]: + apply_writes( + checkpoint, + channels, + tasks, + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + valid_updates: list[tuple[str, dict[str, Any] | None, str | None]] = [] + if len(updates) == 1: + values, as_node, task_id = updates[0] + # find last node that updated the state, if not provided + if as_node is None and len(self.nodes) == 1: + as_node = tuple(self.nodes)[0] + elif as_node is None and not saved: + if ( + isinstance(self.input_channels, str) + and self.input_channels in self.nodes + ): + as_node = self.input_channels + elif as_node is None: + last_seen_by_node = sorted( + (v, n) + for n, seen in checkpoint["versions_seen"].items() + if n in self.nodes + for v in seen.values() + ) + # if two nodes updated the state at the same time, it's ambiguous + if last_seen_by_node: + if len(last_seen_by_node) == 1: + as_node = last_seen_by_node[0][1] + elif last_seen_by_node[-1][0] != last_seen_by_node[-2][0]: + as_node = last_seen_by_node[-1][1] + if as_node is None: + raise InvalidUpdateError("Ambiguous update, specify as_node") + if as_node not in self.nodes: + raise InvalidUpdateError(f"Node {as_node} does not exist") + valid_updates.append((as_node, values, task_id)) + else: + for values, as_node, task_id in updates: + if as_node is None: + raise InvalidUpdateError( + "as_node is required when applying multiple updates" + ) + if as_node not in self.nodes: + raise InvalidUpdateError(f"Node {as_node} does not exist") + + valid_updates.append((as_node, values, task_id)) + + run_tasks: list[PregelTaskWrites] = [] + run_task_ids: list[str] = [] + + for as_node, values, provided_task_id in valid_updates: + # create task to run all writers of the chosen node + writers = self.nodes[as_node].flat_writers + if not writers: + raise InvalidUpdateError(f"Node {as_node} has no writers") + writes: deque[tuple[str, Any]] = deque() + task = PregelTaskWrites((), as_node, writes, [INTERRUPT]) + task_id = provided_task_id or str( + uuid5(UUID(checkpoint["id"]), INTERRUPT) + ) + run_tasks.append(task) + run_task_ids.append(task_id) + run = RunnableSequence(*writers) if len(writers) > 1 else writers[0] + # execute task + await run.ainvoke( + values, + patch_config( + config, + run_name=self.name + "UpdateState", + configurable={ + # deque.extend is thread-safe + CONFIG_KEY_SEND: writes.extend, + CONFIG_KEY_TASK_ID: task_id, + CONFIG_KEY_READ: partial( + local_read, + _scratchpad( + None, + [], + task_id, + "", + None, + step, + step + 2, + ), + channels, + managed, + task, + ), + }, + ), + ) + # save task writes + for task_id, task in zip(run_task_ids, run_tasks): + # channel writes are saved to current checkpoint + channel_writes = [w for w in task.writes if w[0] != PUSH] + if saved and channel_writes: + await checkpointer.aput_writes( + checkpoint_config, channel_writes, task_id + ) + # apply to checkpoint and save + apply_writes( + checkpoint, + channels, + run_tasks, + checkpointer.get_next_version, + self.trigger_to_nodes, + ) + checkpoint = create_checkpoint(checkpoint, channels, step + 1) + # save checkpoint, after applying writes + next_config = await checkpointer.aput( + checkpoint_config, + checkpoint, + { + "source": "update", + "step": step + 1, + "parents": saved.metadata.get("parents", {}) if saved else {}, + }, + get_new_channel_versions( + checkpoint_previous_versions, checkpoint["channel_versions"] + ), + ) + for task_id, task in zip(run_task_ids, run_tasks): + # save push writes + if push_writes := [w for w in task.writes if w[0] == PUSH]: + await checkpointer.aput_writes(next_config, push_writes, task_id) + return patch_checkpoint_map(next_config, saved.metadata if saved else None) + + current_config = patch_configurable( + config, {CONFIG_KEY_THREAD_ID: str(config[CONF][CONFIG_KEY_THREAD_ID])} + ) + for superstep in supersteps: + current_config = await aperform_superstep(current_config, superstep) + return current_config + + def update_state( + self, + config: RunnableConfig, + values: dict[str, Any] | Any | None, + as_node: str | None = None, + task_id: str | None = None, + ) -> RunnableConfig: + """Update the state of the graph with the given values, as if they came from + node `as_node`. If `as_node` is not provided, it will be set to the last node + that updated the state, if not ambiguous. + """ + return self.bulk_update_state(config, [[StateUpdate(values, as_node, task_id)]]) + + async def aupdate_state( + self, + config: RunnableConfig, + values: dict[str, Any] | Any, + as_node: str | None = None, + task_id: str | None = None, + ) -> RunnableConfig: + """Asynchronously update the state of the graph with the given values, as if they came from + node `as_node`. If `as_node` is not provided, it will be set to the last node + that updated the state, if not ambiguous. + """ + return await self.abulk_update_state( + config, [[StateUpdate(values, as_node, task_id)]] + ) + + def _defaults( + self, + config: RunnableConfig, + *, + stream_mode: StreamMode | Sequence[StreamMode], + print_mode: StreamMode | Sequence[StreamMode], + output_keys: str | Sequence[str] | None, + interrupt_before: All | Sequence[str] | None, + interrupt_after: All | Sequence[str] | None, + ) -> tuple[ + set[StreamMode], + str | Sequence[str], + All | Sequence[str], + All | Sequence[str], + BaseCheckpointSaver | None, + BaseStore | None, + BaseCache | None, + ]: + if config["recursion_limit"] < 1: + raise ValueError("recursion_limit must be at least 1") + if output_keys is None: + output_keys = self.stream_channels_asis + else: + validate_keys(output_keys, self.channels) + interrupt_before = interrupt_before or self.interrupt_before_nodes + interrupt_after = interrupt_after or self.interrupt_after_nodes + if not isinstance(stream_mode, list): + stream_modes = {stream_mode} + else: + stream_modes = set(stream_mode) + if isinstance(print_mode, str): + stream_modes.add(print_mode) + else: + stream_modes.update(print_mode) + if self.checkpointer is False: + checkpointer: BaseCheckpointSaver | None = None + elif CONFIG_KEY_CHECKPOINTER in config.get(CONF, {}): + checkpointer = config[CONF][CONFIG_KEY_CHECKPOINTER] + elif self.checkpointer is True: + raise RuntimeError("checkpointer=True cannot be used for root graphs.") + else: + checkpointer = self.checkpointer + if checkpointer and not config.get(CONF): + raise ValueError( + "Checkpointer requires one or more of the following 'configurable' " + "keys: thread_id, checkpoint_ns, checkpoint_id" + ) + if CONFIG_KEY_STORE in config.get(CONF, {}): + store: BaseStore | None = config[CONF][CONFIG_KEY_STORE] + else: + store = self.store + if CONFIG_KEY_CACHE in config.get(CONF, {}): + cache: BaseCache | None = config[CONF][CONFIG_KEY_CACHE] + else: + cache = self.cache + return ( + stream_modes, + output_keys, + interrupt_before, + interrupt_after, + checkpointer, + store, + cache, + ) + + def stream( + self, + input: InputT, + config: RunnableConfig | None = None, + *, + stream_mode: StreamMode | Sequence[StreamMode] | None = None, + print_mode: StreamMode | Sequence[StreamMode] = (), + output_keys: str | Sequence[str] | None = None, + interrupt_before: All | Sequence[str] | None = None, + interrupt_after: All | Sequence[str] | None = None, + checkpoint_during: bool | None = None, + debug: bool | None = None, + subgraphs: bool = False, + ) -> Iterator[dict[str, Any] | Any]: + """Stream graph steps for a single input. + + Args: + input: The input to the graph. + config: The configuration to use for the run. + stream_mode: The mode to stream output, defaults to `self.stream_mode`. + Options are: + + - `"values"`: Emit all values in the state after each step, including interrupts. + When used with functional API, values are emitted once at the end of the workflow. + - `"updates"`: Emit only the node or task names and updates returned by the nodes or tasks after each step. + If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately. + - `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`. + - `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks. + Will be emitted as 2-tuples `(LLM token, metadata)`. + - `"checkpoints"`: Emit an event when a checkpoint is created, in the same format as returned by get_state(). + - `"tasks"`: Emit events when tasks start and finish, including their results and errors. + + You can pass a list as the `stream_mode` parameter to stream multiple modes at once. + The streamed outputs will be tuples of `(mode, data)`. + + See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. + print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. + output_keys: The keys to stream, defaults to all non-context channels. + interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph. + interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph. + checkpoint_during: Whether to checkpoint intermediate steps, defaults to False. If False, only the final checkpoint is saved. + subgraphs: Whether to stream events from inside subgraphs, defaults to False. + If True, the events will be emitted as tuples `(namespace, data)`, + or `(namespace, mode, data)` if `stream_mode` is a list, + where `namespace` is a tuple with the path to the node where a subgraph is invoked, + e.g. `("parent_node:", "child_node:")`. + + See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. + + Yields: + The output of each step in the graph. The output shape depends on the stream_mode. + """ + + if stream_mode is None: + # if being called as a node in another graph, default to values mode + # but don't overwrite stream_mode arg if provided + stream_mode = ( + "values" + if config is not None and CONFIG_KEY_TASK_ID in config.get(CONF, {}) + else self.stream_mode + ) + if debug or self.debug: + print_mode = ["updates", "values"] + + stream = SyncQueue() + + config = ensure_config(self.config, config) + callback_manager = get_callback_manager_for_config(config) + run_manager = callback_manager.on_chain_start( + None, + input, + name=config.get("run_name", self.get_name()), + run_id=config.get("run_id"), + ) + try: + # assign defaults + ( + stream_modes, + output_keys, + interrupt_before_, + interrupt_after_, + checkpointer, + store, + cache, + ) = self._defaults( + config, + stream_mode=stream_mode, + print_mode=print_mode, + output_keys=output_keys, + interrupt_before=interrupt_before, + interrupt_after=interrupt_after, + ) + # set up subgraph checkpointing + if self.checkpointer is True: + ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) + config[CONF][CONFIG_KEY_CHECKPOINT_NS] = recast_checkpoint_ns(ns) + # set up messages stream mode + if "messages" in stream_modes: + run_manager.inheritable_handlers.append( + StreamMessagesHandler(stream.put, subgraphs) + ) + # set up custom stream mode + if "custom" in stream_modes: + config[CONF][CONFIG_KEY_STREAM_WRITER] = lambda c: stream.put( + ( + tuple( + get_config()[CONF][CONFIG_KEY_CHECKPOINT_NS].split(NS_SEP)[ + :-1 + ] + ), + "custom", + c, + ) + ) + elif ( + CONFIG_KEY_STREAM not in config[CONF] + and CONFIG_KEY_STREAM_WRITER in config[CONF] + ): + # remove parent graph stream writer if subgraph streaming not requested + del config[CONF][CONFIG_KEY_STREAM_WRITER] + # set checkpointing mode for subgraphs + if checkpoint_during is not None: + config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during + with SyncPregelLoop( + input, + stream=StreamProtocol(stream.put, stream_modes), + config=config, + store=store, + cache=cache, + checkpointer=checkpointer, + nodes=self.nodes, + specs=self.channels, + output_keys=output_keys, + input_keys=self.input_channels, + stream_keys=self.stream_channels_asis, + interrupt_before=interrupt_before_, + interrupt_after=interrupt_after_, + manager=run_manager, + checkpoint_during=checkpoint_during + if checkpoint_during is not None + else config[CONF].get(CONFIG_KEY_CHECKPOINT_DURING, True), + trigger_to_nodes=self.trigger_to_nodes, + migrate_checkpoint=self._migrate_checkpoint, + retry_policy=self.retry_policy, + cache_policy=self.cache_policy, + ) as loop: + # create runner + runner = PregelRunner( + submit=config[CONF].get( + CONFIG_KEY_RUNNER_SUBMIT, weakref.WeakMethod(loop.submit) + ), + put_writes=weakref.WeakMethod(loop.put_writes), + node_finished=config[CONF].get(CONFIG_KEY_NODE_FINISHED), + ) + # enable subgraph streaming + if subgraphs: + loop.config[CONF][CONFIG_KEY_STREAM] = loop.stream + # enable concurrent streaming + if ( + self.stream_eager + or subgraphs + or "messages" in stream_modes + or "custom" in stream_modes + ): + # we are careful to have a single waiter live at any one time + # because on exit we increment semaphore count by exactly 1 + waiter: concurrent.futures.Future | None = None + # because sync futures cannot be cancelled, we instead + # release the stream semaphore on exit, which will cause + # a pending waiter to return immediately + loop.stack.callback(stream._count.release) + + def get_waiter() -> concurrent.futures.Future[None]: + nonlocal waiter + if waiter is None or waiter.done(): + waiter = loop.submit(stream.wait) + return waiter + else: + return waiter + + else: + get_waiter = None # type: ignore[assignment] + # Similarly to Bulk Synchronous Parallel / Pregel model + # computation proceeds in steps, while there are channel updates. + # Channel updates from step N are only visible in step N+1 + # channels are guaranteed to be immutable for the duration of the step, + # with channel updates applied only at the transition between steps. + while loop.tick(): + for task in loop.match_cached_writes(): + loop.output_writes(task.id, task.writes, cached=True) + for _ in runner.tick( + [t for t in loop.tasks.values() if not t.writes], + timeout=self.step_timeout, + get_waiter=get_waiter, + schedule_task=loop.accept_push, + ): + # emit output + yield from _output( + stream_mode, print_mode, subgraphs, stream.get, queue.Empty + ) + loop.after_tick() + # emit output + yield from _output( + stream_mode, print_mode, subgraphs, stream.get, queue.Empty + ) + # handle exit + if loop.status == "out_of_steps": + msg = create_error_message( + message=( + f"Recursion limit of {config['recursion_limit']} reached " + "without hitting a stop condition. You can increase the " + "limit by setting the `recursion_limit` config key." + ), + error_code=ErrorCode.GRAPH_RECURSION_LIMIT, + ) + raise GraphRecursionError(msg) + # set final channel values as run output + run_manager.on_chain_end(loop.output) + except BaseException as e: + run_manager.on_chain_error(e) + raise + + async def astream( + self, + input: InputT, + config: RunnableConfig | None = None, + *, + stream_mode: StreamMode | Sequence[StreamMode] | None = None, + print_mode: StreamMode | Sequence[StreamMode] = (), + output_keys: str | Sequence[str] | None = None, + interrupt_before: All | Sequence[str] | None = None, + interrupt_after: All | Sequence[str] | None = None, + checkpoint_during: bool | None = None, + debug: bool | None = None, + subgraphs: bool = False, + ) -> AsyncIterator[dict[str, Any] | Any]: + """Asynchronously stream graph steps for a single input. + + Args: + input: The input to the graph. + config: The configuration to use for the run. + stream_mode: The mode to stream output, defaults to `self.stream_mode`. + Options are: + + - `"values"`: Emit all values in the state after each step, including interrupts. + When used with functional API, values are emitted once at the end of the workflow. + - `"updates"`: Emit only the node or task names and updates returned by the nodes or tasks after each step. + If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately. + - `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`. + - `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks. + Will be emitted as 2-tuples `(LLM token, metadata)`. + - `"debug"`: Emit debug events with as much information as possible for each step. + + You can pass a list as the `stream_mode` parameter to stream multiple modes at once. + The streamed outputs will be tuples of `(mode, data)`. + + See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. + print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. + output_keys: The keys to stream, defaults to all non-context channels. + interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph. + interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph. + checkpoint_during: Whether to checkpoint intermediate steps, defaults to False. If False, only the final checkpoint is saved. + subgraphs: Whether to stream events from inside subgraphs, defaults to False. + If True, the events will be emitted as tuples `(namespace, data)`, + or `(namespace, mode, data)` if `stream_mode` is a list, + where `namespace` is a tuple with the path to the node where a subgraph is invoked, + e.g. `("parent_node:", "child_node:")`. + + See [LangGraph streaming guide](https://langchain-ai.github.io/langgraph/how-tos/streaming/) for more details. + + Yields: + The output of each step in the graph. The output shape depends on the stream_mode. + """ + + if stream_mode is None: + # if being called as a node in another graph, default to values mode + # but don't overwrite stream_mode arg if provided + stream_mode = ( + "values" + if config is not None and CONFIG_KEY_TASK_ID in config.get(CONF, {}) + else self.stream_mode + ) + if debug or self.debug: + print_mode = ["updates", "values"] + + stream = AsyncQueue() + aioloop = asyncio.get_running_loop() + stream_put = cast( + Callable[[StreamChunk], None], + partial(aioloop.call_soon_threadsafe, stream.put_nowait), + ) + + config = ensure_config(self.config, config) + callback_manager = get_async_callback_manager_for_config(config) + run_manager = await callback_manager.on_chain_start( + None, + input, + name=config.get("run_name", self.get_name()), + run_id=config.get("run_id"), + ) + # if running from astream_log() run each proc with streaming + do_stream = ( + next( + ( + True + for h in run_manager.handlers + if isinstance(h, _StreamingCallbackHandler) + and not isinstance(h, StreamMessagesHandler) + ), + False, + ) + if _StreamingCallbackHandler is not None + else False + ) + try: + # assign defaults + ( + stream_modes, + output_keys, + interrupt_before_, + interrupt_after_, + checkpointer, + store, + cache, + ) = self._defaults( + config, + stream_mode=stream_mode, + print_mode=print_mode, + output_keys=output_keys, + interrupt_before=interrupt_before, + interrupt_after=interrupt_after, + ) + # set up subgraph checkpointing + if self.checkpointer is True: + ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) + config[CONF][CONFIG_KEY_CHECKPOINT_NS] = recast_checkpoint_ns(ns) + # set up messages stream mode + if "messages" in stream_modes: + run_manager.inheritable_handlers.append( + StreamMessagesHandler(stream_put, subgraphs) + ) + # set up custom stream mode + if "custom" in stream_modes: + config[CONF][CONFIG_KEY_STREAM_WRITER] = ( + lambda c: aioloop.call_soon_threadsafe( + stream.put_nowait, + ( + tuple( + get_config()[CONF][CONFIG_KEY_CHECKPOINT_NS].split( + NS_SEP + )[:-1] + ), + "custom", + c, + ), + ) + ) + elif ( + CONFIG_KEY_STREAM not in config[CONF] + and CONFIG_KEY_STREAM_WRITER in config[CONF] + ): + # remove parent graph stream writer if subgraph streaming not requested + del config[CONF][CONFIG_KEY_STREAM_WRITER] + # set checkpointing mode for subgraphs + if checkpoint_during is not None: + config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during + async with AsyncPregelLoop( + input, + stream=StreamProtocol(stream.put_nowait, stream_modes), + config=config, + store=store, + cache=cache, + checkpointer=checkpointer, + nodes=self.nodes, + specs=self.channels, + output_keys=output_keys, + input_keys=self.input_channels, + stream_keys=self.stream_channels_asis, + interrupt_before=interrupt_before_, + interrupt_after=interrupt_after_, + manager=run_manager, + checkpoint_during=checkpoint_during + if checkpoint_during is not None + else config[CONF].get(CONFIG_KEY_CHECKPOINT_DURING, True), + trigger_to_nodes=self.trigger_to_nodes, + migrate_checkpoint=self._migrate_checkpoint, + retry_policy=self.retry_policy, + cache_policy=self.cache_policy, + ) as loop: + # create runner + runner = PregelRunner( + submit=config[CONF].get( + CONFIG_KEY_RUNNER_SUBMIT, weakref.WeakMethod(loop.submit) + ), + put_writes=weakref.WeakMethod(loop.put_writes), + use_astream=do_stream, + node_finished=config[CONF].get(CONFIG_KEY_NODE_FINISHED), + ) + # enable subgraph streaming + if subgraphs: + loop.config[CONF][CONFIG_KEY_STREAM] = StreamProtocol( + stream_put, stream_modes + ) + # enable concurrent streaming + if ( + self.stream_eager + or subgraphs + or "messages" in stream_modes + or "custom" in stream_modes + ): + + def get_waiter() -> asyncio.Task[None]: + return aioloop.create_task(stream.wait()) + + else: + get_waiter = None # type: ignore[assignment] + # Similarly to Bulk Synchronous Parallel / Pregel model + # computation proceeds in steps, while there are channel updates + # channel updates from step N are only visible in step N+1 + # channels are guaranteed to be immutable for the duration of the step, + # with channel updates applied only at the transition between steps + while loop.tick(): + for task in await loop.amatch_cached_writes(): + loop.output_writes(task.id, task.writes, cached=True) + async for _ in runner.atick( + [t for t in loop.tasks.values() if not t.writes], + timeout=self.step_timeout, + get_waiter=get_waiter, + schedule_task=loop.aaccept_push, + ): + # emit output + for o in _output( + stream_mode, + print_mode, + subgraphs, + stream.get_nowait, + asyncio.QueueEmpty, + ): + yield o + loop.after_tick() + # emit output + for o in _output( + stream_mode, + print_mode, + subgraphs, + stream.get_nowait, + asyncio.QueueEmpty, + ): + yield o + # handle exit + if loop.status == "out_of_steps": + msg = create_error_message( + message=( + f"Recursion limit of {config['recursion_limit']} reached " + "without hitting a stop condition. You can increase the " + "limit by setting the `recursion_limit` config key." + ), + error_code=ErrorCode.GRAPH_RECURSION_LIMIT, + ) + raise GraphRecursionError(msg) + # set final channel values as run output + await run_manager.on_chain_end(loop.output) + except BaseException as e: + await asyncio.shield(run_manager.on_chain_error(e)) + raise + + def invoke( + self, + input: InputT, + config: RunnableConfig | None = None, + *, + stream_mode: StreamMode = "values", + print_mode: StreamMode | Sequence[StreamMode] = (), + output_keys: str | Sequence[str] | None = None, + interrupt_before: All | Sequence[str] | None = None, + interrupt_after: All | Sequence[str] | None = None, + **kwargs: Any, + ) -> dict[str, Any] | Any: + """Run the graph with a single input and config. + + Args: + input: The input data for the graph. It can be a dictionary or any other type. + config: Optional. The configuration for the graph run. + stream_mode: Optional[str]. The stream mode for the graph run. Default is "values". + print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. + output_keys: Optional. The output keys to retrieve from the graph run. + interrupt_before: Optional. The nodes to interrupt the graph run before. + interrupt_after: Optional. The nodes to interrupt the graph run after. + **kwargs: Additional keyword arguments to pass to the graph run. + + Returns: + The output of the graph run. If stream_mode is "values", it returns the latest output. + If stream_mode is not "values", it returns a list of output chunks. + """ + output_keys = output_keys if output_keys is not None else self.output_channels + + latest: dict[str, Any] | Any = None + chunks: list[dict[str, Any] | Any] = [] + interrupts: list[Interrupt] = [] + + for chunk in self.stream( + input, + config, + stream_mode=["updates", "values"] + if stream_mode == "values" + else stream_mode, + print_mode=print_mode, + output_keys=output_keys, + interrupt_before=interrupt_before, + interrupt_after=interrupt_after, + **kwargs, + ): + if stream_mode == "values": + if len(chunk) == 2: + mode, payload = cast(tuple[StreamMode, Any], chunk) + else: + _, mode, payload = cast( + tuple[tuple[str, ...], StreamMode, Any], chunk + ) + if ( + mode == "updates" + and isinstance(payload, dict) + and (ints := payload.get(INTERRUPT)) is not None + ): + interrupts.extend(ints) + elif mode == "values": + latest = payload + else: + chunks.append(chunk) + + if stream_mode == "values": + if interrupts: + return ( + {**latest, INTERRUPT: interrupts} + if isinstance(latest, dict) + else {INTERRUPT: interrupts} + ) + return latest + else: + return chunks + + async def ainvoke( + self, + input: InputT, + config: RunnableConfig | None = None, + *, + stream_mode: StreamMode = "values", + print_mode: StreamMode | Sequence[StreamMode] = (), + output_keys: str | Sequence[str] | None = None, + interrupt_before: All | Sequence[str] | None = None, + interrupt_after: All | Sequence[str] | None = None, + **kwargs: Any, + ) -> dict[str, Any] | Any: + """Asynchronously invoke the graph on a single input. + + Args: + input: The input data for the computation. It can be a dictionary or any other type. + config: Optional. The configuration for the computation. + stream_mode: Optional. The stream mode for the computation. Default is "values". + print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way. + output_keys: Optional. The output keys to include in the result. Default is None. + interrupt_before: Optional. The nodes to interrupt before. Default is None. + interrupt_after: Optional. The nodes to interrupt after. Default is None. + **kwargs: Additional keyword arguments. + + Returns: + The result of the computation. If stream_mode is "values", it returns the latest value. + If stream_mode is "chunks", it returns a list of chunks. + """ + + output_keys = output_keys if output_keys is not None else self.output_channels + + latest: dict[str, Any] | Any = None + chunks: list[dict[str, Any] | Any] = [] + interrupts: list[Interrupt] = [] + + async for chunk in self.astream( + input, + config, + stream_mode=["updates", "values"] + if stream_mode == "values" + else stream_mode, + print_mode=print_mode, + output_keys=output_keys, + interrupt_before=interrupt_before, + interrupt_after=interrupt_after, + **kwargs, + ): + if stream_mode == "values": + if len(chunk) == 2: + mode, payload = cast(tuple[StreamMode, Any], chunk) + else: + _, mode, payload = cast( + tuple[tuple[str, ...], StreamMode, Any], chunk + ) + if ( + mode == "updates" + and isinstance(payload, dict) + and (ints := payload.get(INTERRUPT)) is not None + ): + interrupts.extend(ints) + elif mode == "values": + latest = payload + else: + chunks.append(chunk) + + if stream_mode == "values": + if interrupts: + return ( + {**latest, INTERRUPT: interrupts} + if isinstance(latest, dict) + else {INTERRUPT: interrupts} + ) + return latest + else: + return chunks + + def clear_cache(self, nodes: Sequence[str] | None = None) -> None: + """Clear the cache for the given nodes.""" + if not self.cache: + raise ValueError("No cache is set for this graph. Cannot clear cache.") + nodes = nodes or self.nodes.keys() + # collect namespaces to clear + namespaces: list[tuple[str, ...]] = [] + for node in nodes: + if node in self.nodes: + namespaces.append( + ( + CACHE_NS_WRITES, + (identifier(self.nodes[node]) or "__dynamic__"), + node, + ), + ) + # clear cache + self.cache.clear(namespaces) + + async def aclear_cache(self, nodes: Sequence[str] | None = None) -> None: + """Asynchronously clear the cache for the given nodes.""" + if not self.cache: + raise ValueError("No cache is set for this graph. Cannot clear cache.") + nodes = nodes or self.nodes.keys() + # collect namespaces to clear + namespaces: list[tuple[str, ...]] = [] + for node in nodes: + if node in self.nodes: + namespaces.append( + ( + CACHE_NS_WRITES, + (identifier(self.nodes[node]) or "__dynamic__"), + node, + ), + ) + # clear cache + await self.cache.aclear(namespaces) + + +def _trigger_to_nodes(nodes: dict[str, PregelNode]) -> Mapping[str, Sequence[str]]: + """Index from a trigger to nodes that depend on it.""" + trigger_to_nodes: defaultdict[str, list[str]] = defaultdict(list) + for name, node in nodes.items(): + for trigger in node.triggers: + trigger_to_nodes[trigger].append(name) + return dict(trigger_to_nodes) + + +def _output( + stream_mode: StreamMode | Sequence[StreamMode], + print_mode: StreamMode | Sequence[StreamMode], + stream_subgraphs: bool, + getter: Callable[[], tuple[tuple[str, ...], str, Any]], + empty_exc: type[Exception], +) -> Iterator: + while True: + try: + ns, mode, payload = getter() + except empty_exc: + break + if mode in print_mode: + if stream_subgraphs and ns: + print( + " ".join( + ( + get_bolded_text(f"[{mode}]"), + get_colored_text(f"[graph={ns}]", color="yellow"), + repr(payload), + ) + ) + ) + else: + print( + " ".join( + ( + get_bolded_text(f"[{mode}]"), + repr(payload), + ) + ) + ) + if mode in stream_mode: + if stream_subgraphs and isinstance(stream_mode, list): + yield (ns, mode, payload) + elif isinstance(stream_mode, list): + yield (mode, payload) + elif stream_subgraphs: + yield (ns, payload) + else: + yield payload diff --git a/libs/langgraph/langgraph/pregel/protocol.py b/libs/langgraph/langgraph/pregel/protocol.py index e55be3cbc..504cb1760 100644 --- a/libs/langgraph/langgraph/pregel/protocol.py +++ b/libs/langgraph/langgraph/pregel/protocol.py @@ -2,17 +2,18 @@ from __future__ import annotations from abc import ABC, abstractmethod from collections.abc import AsyncIterator, Iterator, Sequence -from typing import Any, Generic +from typing import Any, Callable, Generic, cast from langchain_core.runnables import Runnable, RunnableConfig from langchain_core.runnables.graph import Graph as DrawableGraph from typing_extensions import Self -from langgraph.pregel.types import All, StateSnapshot, StateUpdate, StreamMode +from langgraph.types import All, StateSnapshot, StateUpdate, StreamMode from langgraph.typing import InputT, OutputT, StateT +__all__ = ("PregelProtocol", "StreamProtocol") + -# TODO: remove Runnable inheritance here! class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT], ABC): @abstractmethod def with_config( @@ -138,3 +139,22 @@ class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT], AB interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, ) -> dict[str, Any] | Any: ... + + +StreamChunk = tuple[tuple[str, ...], str, Any] + + +class StreamProtocol: + __slots__ = ("modes", "__call__") + + modes: set[StreamMode] + + __call__: Callable[[Self, StreamChunk], None] + + def __init__( + self, + __call__: Callable[[StreamChunk], None], + modes: set[StreamMode], + ) -> None: + self.__call__ = cast(Callable[[Self, StreamChunk], None], __call__) + self.modes = modes diff --git a/libs/langgraph/langgraph/pregel/py.typed b/libs/langgraph/langgraph/pregel/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/langgraph/langgraph/pregel/remote.py b/libs/langgraph/langgraph/pregel/remote.py index 38b334e46..145dd548f 100644 --- a/libs/langgraph/langgraph/pregel/remote.py +++ b/libs/langgraph/langgraph/pregel/remote.py @@ -29,6 +29,7 @@ from langgraph_sdk.schema import Command as CommandSDK from langgraph_sdk.schema import StreamMode as StreamModeSDK from typing_extensions import Self +from langgraph._internal._config import merge_configs from langgraph.checkpoint.base import CheckpointMetadata from langgraph.constants import ( CONF, @@ -41,12 +42,19 @@ from langgraph.constants import ( NS_SEP, ) from langgraph.errors import GraphInterrupt -from langgraph.pregel.protocol import PregelProtocol -from langgraph.pregel.types import All, PregelTask, StateSnapshot, StreamMode -from langgraph.types import Command, Interrupt, StreamProtocol -from langgraph.utils.config import merge_configs +from langgraph.pregel.protocol import PregelProtocol, StreamProtocol +from langgraph.types import ( + All, + Command, + Interrupt, + PregelTask, + StateSnapshot, + StreamMode, +) -CONF_DROPLIST = frozenset( +__all__ = ("RemoteGraph", "RemoteException") + +_CONF_DROPLIST = frozenset( ( CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_CHECKPOINT_ID, @@ -56,7 +64,7 @@ CONF_DROPLIST = frozenset( ) -def sanitize_config_value(v: Any) -> Any: +def _sanitize_config_value(v: Any) -> Any: """Recursively sanitize a config value to ensure it contains only primitives.""" if isinstance(v, (str, int, float, bool)): return v @@ -64,14 +72,14 @@ def sanitize_config_value(v: Any) -> Any: sanitized_dict = {} for k, val in v.items(): if isinstance(k, str): - sanitized_value = sanitize_config_value(val) + sanitized_value = _sanitize_config_value(val) if sanitized_value is not None: sanitized_dict[k] = sanitized_value return sanitized_dict elif isinstance(v, (list, tuple)): sanitized_list = [] for item in v: - sanitized_item = sanitize_config_value(item) + sanitized_item = _sanitize_config_value(item) if sanitized_item is not None: sanitized_list.append(sanitized_item) return sanitized_list @@ -347,7 +355,7 @@ class RemoteGraph(PregelProtocol): for k, v in config["metadata"].items(): if ( isinstance(k, str) - and (sanitized_value := sanitize_config_value(v)) is not None + and (sanitized_value := _sanitize_config_value(v)) is not None ): sanitized["metadata"][k] = sanitized_value @@ -356,8 +364,8 @@ class RemoteGraph(PregelProtocol): for k, v in config["configurable"].items(): if ( isinstance(k, str) - and k not in CONF_DROPLIST - and (sanitized_value := sanitize_config_value(v)) is not None + and k not in _CONF_DROPLIST + and (sanitized_value := _sanitize_config_value(v)) is not None ): sanitized["configurable"][k] = sanitized_value diff --git a/libs/langgraph/langgraph/pregel/types.py b/libs/langgraph/langgraph/pregel/types.py index 212c0c4af..39a36df68 100644 --- a/libs/langgraph/langgraph/pregel/types.py +++ b/libs/langgraph/langgraph/pregel/types.py @@ -25,3 +25,14 @@ __all__ = [ "StreamWriter", "default_retry_on", ] + +from warnings import warn + +from langgraph.warnings import LangGraphDeprecatedSinceV10 + +warn( + "Importing from langgraph.pregel.types is deprecated. " + "Please use 'from langgraph.types import ...' instead.", + LangGraphDeprecatedSinceV10, + stacklevel=2, +) diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index d95c4fd51..0813270cf 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -18,12 +18,12 @@ from typing import ( ) from langchain_core.runnables import Runnable, RunnableConfig -from typing_extensions import Self from xxhash import xxh3_128_hexdigest +from langgraph._internal._cache import default_cache_key +from langgraph._internal._fields import get_cached_annotated_keys, get_update_as_tuples +from langgraph._internal._retry import default_retry_on from langgraph.checkpoint.base import BaseCheckpointSaver, CheckpointMetadata -from langgraph.utils.cache import default_cache_key -from langgraph.utils.fields import get_cached_annotated_keys, get_update_as_tuples if TYPE_CHECKING: from langgraph.pregel.protocol import PregelProtocol @@ -37,6 +37,24 @@ except ImportError: pass +__all__ = ( + "All", + "Checkpointer", + "StreamMode", + "StreamWriter", + "RetryPolicy", + "CachePolicy", + "Interrupt", + "StateUpdate", + "PregelTask", + "PregelExecutableTask", + "StateSnapshot", + "Send", + "Command", + "interrupt", +) + + All = Literal["*"] """Special value to indicate that graph should interrupt on all nodes.""" @@ -73,37 +91,6 @@ else: _DC_KWARGS = {"frozen": True} -def default_retry_on(exc: Exception) -> bool: - import httpx - import requests - - if isinstance(exc, ConnectionError): - return True - if isinstance(exc, httpx.HTTPStatusError): - return 500 <= exc.response.status_code < 600 - if isinstance(exc, requests.HTTPError): - return 500 <= exc.response.status_code < 600 if exc.response else True - if isinstance( - exc, - ( - ValueError, - TypeError, - ArithmeticError, - ImportError, - LookupError, - NameError, - SyntaxError, - RuntimeError, - ReferenceError, - StopIteration, - StopAsyncIteration, - OSError, - ), - ): - return False - return True - - class RetryPolicy(NamedTuple): """Configuration for retrying nodes. @@ -364,39 +351,6 @@ class Command(Generic[N], ToolOutputMixin): PARENT: ClassVar[Literal["__parent__"]] = "__parent__" -StreamChunk = tuple[tuple[str, ...], str, Any] - - -class StreamProtocol: - __slots__ = ("modes", "__call__") - - modes: set[StreamMode] - - __call__: Callable[[Self, StreamChunk], None] - - def __init__( - self, - __call__: Callable[[StreamChunk], None], - modes: set[StreamMode], - ) -> None: - self.__call__ = cast(Callable[[Self, StreamChunk], None], __call__) - self.modes = modes - - -@dataclasses.dataclass(**_DC_KWARGS) -class PregelScratchpad: - step: int - stop: int - # call - call_counter: Callable[[], int] - # interrupt - interrupt_counter: Callable[[], int] - get_null_resume: Callable[[bool], Any] - resume: list[Any] - # subgraph - subgraph_counter: Callable[[], int] - - def interrupt(value: Any) -> Any: """Interrupt the graph with a resumable exception from within a node. @@ -504,7 +458,7 @@ def interrupt(value: Any) -> Any: conf = get_config()["configurable"] # track interrupt index - scratchpad: PregelScratchpad = conf[CONFIG_KEY_SCRATCHPAD] + scratchpad = conf[CONFIG_KEY_SCRATCHPAD] idx = scratchpad.interrupt_counter() # find previous resume values if scratchpad.resume: diff --git a/libs/langgraph/langgraph/typing.py b/libs/langgraph/langgraph/typing.py index 01ea27adf..d7a82e9fd 100644 --- a/libs/langgraph/langgraph/typing.py +++ b/libs/langgraph/langgraph/typing.py @@ -4,7 +4,15 @@ from typing import Union from typing_extensions import TypeVar -from langgraph._typing import StateLike +from langgraph._internal._typing import StateLike + +__all__ = ( + "StateT", + "StateT_co", + "StateT_contra", + "InputT", + "OutputT", +) StateT = TypeVar("StateT", bound=StateLike) """Type variable used to represent the state in a graph.""" @@ -19,12 +27,6 @@ InputT = TypeVar("InputT", bound=StateLike, default=StateT) Defaults to `StateT`. """ -ResolvedInputT = TypeVar("ResolvedInputT", bound=StateLike) -"""Type variable used to represent the resolved input to a state graph. - -No default. -""" - OutputT = TypeVar("OutputT", bound=Union[StateLike, None], default=StateT) """Type variable used to represent the output of a state graph.""" diff --git a/libs/langgraph/langgraph/utils/py.typed b/libs/langgraph/langgraph/utils/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/langgraph/langgraph/version.py b/libs/langgraph/langgraph/version.py index f5cb757f5..a81f647c7 100644 --- a/libs/langgraph/langgraph/version.py +++ b/libs/langgraph/langgraph/version.py @@ -2,6 +2,8 @@ from importlib import metadata +__all__ = ("__version__",) + try: __version__ = metadata.version(__package__) except metadata.PackageNotFoundError: diff --git a/libs/langgraph/langgraph/warnings.py b/libs/langgraph/langgraph/warnings.py index e8fd59d88..638f247e3 100644 --- a/libs/langgraph/langgraph/warnings.py +++ b/libs/langgraph/langgraph/warnings.py @@ -2,6 +2,12 @@ from __future__ import annotations +__all__ = ( + "LangGraphDeprecationWarning", + "LangGraphDeprecatedSinceV05", + "LangGraphDeprecatedSinceV10", +) + class LangGraphDeprecationWarning(DeprecationWarning): """A LangGraph specific deprecation warning. @@ -46,3 +52,10 @@ class LangGraphDeprecatedSinceV05(LangGraphDeprecationWarning): def __init__(self, message: str, *args: object) -> None: super().__init__(message, *args, since=(0, 5), expected_removal=(2, 0)) + + +class LangGraphDeprecatedSinceV10(LangGraphDeprecationWarning): + """A specific `LangGraphDeprecationWarning` subclass defining functionality deprecated since LangGraph v1.0.0""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(1, 0), expected_removal=(2, 0)) diff --git a/libs/langgraph/tests/__snapshots__/test_large_cases.ambr b/libs/langgraph/tests/__snapshots__/test_large_cases.ambr index 9f4d08cf9..2a6c8dd25 100644 --- a/libs/langgraph/tests/__snapshots__/test_large_cases.ambr +++ b/libs/langgraph/tests/__snapshots__/test_large_cases.ambr @@ -1,105 +1,4 @@ # serializer version: 1 -# name: test_conditional_graph[memory] - ''' - { - "nodes": [ - { - "id": "agent", - "type": "runnable", - "data": { - "id": [ - "langchain", - "schema", - "runnable", - "RunnableAssign" - ], - "name": "agent" - } - }, - { - "id": "tools", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "runnable", - "RunnableCallable" - ], - "name": "tools" - }, - "metadata": { - "parents": {}, - "version": 2, - "variant": "b" - } - }, - { - "id": "__start__" - }, - { - "id": "__end__" - } - ], - "edges": [ - { - "source": "__start__", - "target": "agent" - }, - { - "source": "agent", - "target": "__end__", - "data": "exit", - "conditional": true - }, - { - "source": "agent", - "target": "tools", - "data": "continue", - "conditional": true - }, - { - "source": "tools", - "target": "agent" - } - ] - } - ''' -# --- -# name: test_conditional_graph[memory].1 - ''' - graph TD; - __start__ --> agent; - agent -.  exit  .-> __end__; - agent -.  continue  .-> tools; - tools --> agent; - - ''' -# --- -# name: test_conditional_graph[memory].2 - ''' - --- - config: - flowchart: - curve: linear - --- - graph TD; - agent(agent) - tools(tools
parents = {} - version = 2 - variant = b) - __start__([

__start__

]):::first - __end__([

__end__

]):::last - __start__ --> agent; - agent -.  exit  .-> __end__; - agent -.  continue  .-> tools; - tools --> agent; - classDef default fill:#f2f0ff,line-height:1.2 - classDef first fill-opacity:0 - classDef last fill:#bfb6fc - - ''' -# --- # name: test_conditional_state_graph[memory] '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "enum": ["AgentAction"], "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "enum": ["AgentFinish"], "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"default": null, "title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "default": null, "title": "Agent Outcome"}, "intermediate_steps": {"default": null, "items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "title": "LangGraphInput", "type": "object"}' # --- @@ -116,8 +15,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -142,8 +41,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "tools" @@ -204,8 +103,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -291,8 +190,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -304,8 +203,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "agent" diff --git a/libs/langgraph/tests/__snapshots__/test_pregel.ambr b/libs/langgraph/tests/__snapshots__/test_pregel.ambr index a47c52178..b8e226348 100644 --- a/libs/langgraph/tests/__snapshots__/test_pregel.ambr +++ b/libs/langgraph/tests/__snapshots__/test_pregel.ambr @@ -1,93 +1,4 @@ # serializer version: 1 -# name: test_conditional_entrypoint_graph - '{"title": "LangGraphInput"}' -# --- -# name: test_conditional_entrypoint_graph.1 - '{"title": "LangGraphOutput"}' -# --- -# name: test_conditional_entrypoint_graph.2 - ''' - { - "nodes": [ - { - "id": "left", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "runnable", - "RunnableCallable" - ], - "name": "left" - } - }, - { - "id": "right", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "runnable", - "RunnableCallable" - ], - "name": "right" - } - }, - { - "id": "__start__", - "type": "runnable", - "data": { - "id": [ - "langgraph", - "utils", - "runnable", - "RunnableCallable" - ], - "name": "__start__" - } - }, - { - "id": "__end__" - } - ], - "edges": [ - { - "source": "__start__", - "target": "left", - "data": "go-left", - "conditional": true - }, - { - "source": "__start__", - "target": "right", - "data": "go-right", - "conditional": true - }, - { - "source": "left", - "target": "__end__", - "conditional": true - }, - { - "source": "right", - "target": "__end__" - } - ] - } - ''' -# --- -# name: test_conditional_entrypoint_graph.3 - ''' - graph TD; - __start__ -.  go-left  .-> left; - __start__ -.  go-right  .-> right; - left -.-> __end__; - right --> __end__; - - ''' -# --- # name: test_conditional_entrypoint_graph_state '{"properties": {"input": {"default": null, "title": "Input", "type": "string"}, "output": {"default": null, "title": "Output", "type": "string"}, "steps": {"default": null, "items": {"type": "string"}, "title": "Steps", "type": "array"}}, "title": "LangGraphInput", "type": "object"}' # --- @@ -104,8 +15,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -117,8 +28,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "left" @@ -130,8 +41,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "right" @@ -193,8 +104,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -206,8 +117,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "get_weather" @@ -249,8 +160,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -262,8 +173,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "A" @@ -275,8 +186,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "B" @@ -327,8 +238,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -340,8 +251,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "human" @@ -353,8 +264,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "agent" @@ -406,8 +317,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -461,8 +372,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "__start__" @@ -474,8 +385,8 @@ "data": { "id": [ "langgraph", - "utils", - "runnable", + "_internal", + "_runnable", "RunnableCallable" ], "name": "worker_node" @@ -767,8 +678,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': '__start__', @@ -780,8 +691,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'tool_one', @@ -793,8 +704,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'tool_three', @@ -809,8 +720,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'tool_two:__start__', @@ -822,8 +733,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'tool_two:tool_two_slow', @@ -835,8 +746,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'tool_two:tool_two_fast', @@ -1020,8 +931,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': '__start__', @@ -1033,8 +944,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'ask_question', @@ -1046,8 +957,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'answer_question', @@ -1087,8 +998,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': '__start__', @@ -1100,8 +1011,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'generate_analysts', @@ -1126,8 +1037,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'generate_sections', @@ -1185,8 +1096,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': '__start__', @@ -1198,8 +1109,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'generate_analysts', @@ -1211,8 +1122,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'generate_sections', @@ -1227,8 +1138,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'conduct_interview:__start__', @@ -1240,8 +1151,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'conduct_interview:ask_question', @@ -1253,8 +1164,8 @@ 'data': dict({ 'id': list([ 'langgraph', - 'utils', - 'runnable', + '_internal', + '_runnable', 'RunnableCallable', ]), 'name': 'conduct_interview:answer_question', diff --git a/libs/langgraph/tests/test_algo.py b/libs/langgraph/tests/test_algo.py index 4dbdffeee..f32fa4334 100644 --- a/libs/langgraph/tests/test_algo.py +++ b/libs/langgraph/tests/test_algo.py @@ -1,6 +1,6 @@ from langgraph.constants import PULL, PUSH -from langgraph.pregel.algo import prepare_next_tasks, task_path_str -from langgraph.pregel.checkpoint import channels_from_checkpoint, empty_checkpoint +from langgraph.pregel._algo import prepare_next_tasks, task_path_str +from langgraph.pregel._checkpoint import channels_from_checkpoint, empty_checkpoint def test_prepare_next_tasks() -> None: diff --git a/libs/langgraph/tests/test_checkpoint_migration.py b/libs/langgraph/tests/test_checkpoint_migration.py index 1f3b47c13..d1dd65e61 100644 --- a/libs/langgraph/tests/test_checkpoint_migration.py +++ b/libs/langgraph/tests/test_checkpoint_migration.py @@ -7,11 +7,11 @@ from typing import Annotated, Literal, Optional, Union import pytest from typing_extensions import TypedDict +from langgraph._internal._config import patch_configurable from langgraph.checkpoint.base import BaseCheckpointSaver, CheckpointTuple from langgraph.graph.state import StateGraph -from langgraph.pregel.checkpoint import copy_checkpoint +from langgraph.pregel._checkpoint import copy_checkpoint from langgraph.types import Command, Interrupt, PregelTask, StateSnapshot, interrupt -from langgraph.utils.config import patch_configurable from tests.any_int import AnyInt from tests.any_str import AnyDict, AnyObject, AnyStr diff --git a/libs/langgraph/tests/test_config_async.py b/libs/langgraph/tests/test_config_async.py index e57433a28..a65a5f2af 100644 --- a/libs/langgraph/tests/test_config_async.py +++ b/libs/langgraph/tests/test_config_async.py @@ -1,7 +1,7 @@ import pytest from langchain_core.callbacks import AsyncCallbackManager -from langgraph.utils.config import get_async_callback_manager_for_config +from langgraph._internal._config import get_async_callback_manager_for_config pytestmark = pytest.mark.anyio diff --git a/libs/langgraph/tests/test_deprecation.py b/libs/langgraph/tests/test_deprecation.py index 2a217edae..0edb00b6f 100644 --- a/libs/langgraph/tests/test_deprecation.py +++ b/libs/langgraph/tests/test_deprecation.py @@ -4,7 +4,7 @@ from typing_extensions import TypedDict from langgraph.func import entrypoint, task from langgraph.graph import StateGraph from langgraph.types import RetryPolicy -from langgraph.warnings import LangGraphDeprecatedSinceV05 +from langgraph.warnings import LangGraphDeprecatedSinceV05, LangGraphDeprecatedSinceV10 class PlainState(TypedDict): ... @@ -66,3 +66,25 @@ def test_add_node_input_schema() -> None: match="`input` is deprecated and will be removed. Please use `input_schema` instead.", ): builder.add_node("test_node", lambda state: state, input=PlainState) # type: ignore[arg-type] + + +def test_constants_deprecation() -> None: + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="Importing Send from langgraph.constants is deprecated. Please use 'from langgraph.types import Send' instead.", + ): + from langgraph.constants import Send # noqa: F401 + + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="Importing Interrupt from langgraph.constants is deprecated. Please use 'from langgraph.types import Interrupt' instead.", + ): + from langgraph.constants import Interrupt # noqa: F401 + + +def test_pregel_deprecation() -> None: + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="Importing from langgraph.pregel.types is deprecated. Please use 'from langgraph.types import ...' instead.", + ): + from langgraph.pregel.types import StateSnapshot # noqa: F401 diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index dc732da91..2fabec768 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -43,27 +43,26 @@ from langgraph.checkpoint.base import ( from langgraph.checkpoint.memory import InMemorySaver from langgraph.config import get_stream_writer from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START -from langgraph.errors import InvalidUpdateError, ParentCommand +from langgraph.errors import GraphRecursionError, InvalidUpdateError, ParentCommand from langgraph.func import entrypoint, task from langgraph.graph import END, StateGraph from langgraph.graph.message import MessageGraph, MessagesState, add_messages from langgraph.prebuilt.tool_node import ToolNode from langgraph.pregel import ( - GraphRecursionError, NodeBuilder, Pregel, - StateSnapshot, ) -from langgraph.pregel.loop import SyncPregelLoop -from langgraph.pregel.retry import RetryPolicy -from langgraph.pregel.runner import PregelRunner +from langgraph.pregel._loop import SyncPregelLoop +from langgraph.pregel._runner import PregelRunner from langgraph.store.base import BaseStore from langgraph.types import ( CachePolicy, Command, Interrupt, PregelTask, + RetryPolicy, Send, + StateSnapshot, StateUpdate, StreamWriter, interrupt, diff --git a/libs/langgraph/tests/test_pregel_async.py b/libs/langgraph/tests/test_pregel_async.py index 61d1ab5d7..6569a1d8d 100644 --- a/libs/langgraph/tests/test_pregel_async.py +++ b/libs/langgraph/tests/test_pregel_async.py @@ -42,22 +42,28 @@ from langgraph.checkpoint.base import ( from langgraph.checkpoint.memory import InMemorySaver from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START -from langgraph.errors import InvalidUpdateError, NodeInterrupt, ParentCommand +from langgraph.errors import ( + GraphRecursionError, + InvalidUpdateError, + NodeInterrupt, + ParentCommand, +) from langgraph.func import entrypoint, task from langgraph.graph import END, StateGraph from langgraph.graph.message import MessagesState, add_messages from langgraph.prebuilt.tool_node import ToolNode -from langgraph.pregel import GraphRecursionError, NodeBuilder, Pregel, StateSnapshot -from langgraph.pregel.loop import AsyncPregelLoop -from langgraph.pregel.retry import RetryPolicy -from langgraph.pregel.runner import PregelRunner +from langgraph.pregel import NodeBuilder, Pregel +from langgraph.pregel._loop import AsyncPregelLoop +from langgraph.pregel._runner import PregelRunner from langgraph.store.base import BaseStore from langgraph.types import ( CachePolicy, Command, Interrupt, PregelTask, + RetryPolicy, Send, + StateSnapshot, StateUpdate, StreamWriter, interrupt, @@ -8381,7 +8387,7 @@ async def test_draw_invalid(): "id": "__start__", "type": "runnable", "data": { - "id": ["langgraph", "utils", "runnable", "RunnableCallable"], + "id": ["langgraph", "_internal", "_runnable", "RunnableCallable"], "name": "__start__", }, }, @@ -8389,7 +8395,7 @@ async def test_draw_invalid(): "id": "agent", "type": "runnable", "data": { - "id": ["langgraph", "utils", "runnable", "RunnableCallable"], + "id": ["langgraph", "_internal", "_runnable", "RunnableCallable"], "name": "agent", }, }, @@ -8397,7 +8403,7 @@ async def test_draw_invalid(): "id": "tool", "type": "runnable", "data": { - "id": ["langgraph", "utils", "runnable", "RunnableCallable"], + "id": ["langgraph", "_internal", "_runnable", "RunnableCallable"], "name": "tool", }, }, @@ -8405,7 +8411,7 @@ async def test_draw_invalid(): "id": "nothing", "type": "runnable", "data": { - "id": ["langgraph", "utils", "runnable", "RunnableCallable"], + "id": ["langgraph", "_internal", "_runnable", "RunnableCallable"], "name": "nothing", }, }, diff --git a/libs/langgraph/tests/test_pydantic.py b/libs/langgraph/tests/test_pydantic.py index 8298970ba..e3340f3ec 100644 --- a/libs/langgraph/tests/test_pydantic.py +++ b/libs/langgraph/tests/test_pydantic.py @@ -21,9 +21,9 @@ from pydantic import ( model_validator, ) +from langgraph._internal._pydantic import is_supported_by_pydantic from langgraph.constants import END, START from langgraph.graph.state import StateGraph -from langgraph.utils.pydantic import is_supported_by_pydantic def test_is_supported_by_pydantic() -> None: diff --git a/libs/langgraph/tests/test_remote_graph.py b/libs/langgraph/tests/test_remote_graph.py index 5fa61db3f..262109673 100644 --- a/libs/langgraph/tests/test_remote_graph.py +++ b/libs/langgraph/tests/test_remote_graph.py @@ -15,8 +15,7 @@ from langgraph.errors import GraphInterrupt from langgraph.graph import StateGraph, add_messages from langgraph.pregel import Pregel from langgraph.pregel.remote import RemoteGraph -from langgraph.pregel.types import StateSnapshot -from langgraph.types import Interrupt +from langgraph.types import Interrupt, StateSnapshot from tests.conftest import NO_DOCKER from tests.example_app.example_graph import app diff --git a/libs/langgraph/tests/test_retry.py b/libs/langgraph/tests/test_retry.py index 6eae7ee5c..ac37bea91 100644 --- a/libs/langgraph/tests/test_retry.py +++ b/libs/langgraph/tests/test_retry.py @@ -4,7 +4,7 @@ import pytest from typing_extensions import TypedDict from langgraph.graph import START, StateGraph -from langgraph.pregel.retry import _should_retry_on +from langgraph.pregel._retry import _should_retry_on from langgraph.types import RetryPolicy diff --git a/libs/langgraph/tests/test_runnable.py b/libs/langgraph/tests/test_runnable.py index 1dd8e35cc..5fb390919 100644 --- a/libs/langgraph/tests/test_runnable.py +++ b/libs/langgraph/tests/test_runnable.py @@ -4,9 +4,9 @@ from typing import Any, Optional import pytest +from langgraph._internal._runnable import RunnableCallable from langgraph.store.base import BaseStore from langgraph.types import StreamWriter -from langgraph.utils.runnable import RunnableCallable pytestmark = pytest.mark.anyio @@ -85,7 +85,7 @@ def test_runnable_callable_injectable_arguments() -> None: """ # Test Optional[BaseStore] annotation. - def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP007 + def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP045 """Test function that accepts an optional store parameter.""" assert store is None return "success" @@ -159,7 +159,7 @@ async def test_runnable_callable_injectable_arguments_async() -> None: """ # Test Optional[BaseStore] annotation. - def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP007 + def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP045 """Test function that accepts an optional store parameter.""" assert store is None return "success" diff --git a/libs/langgraph/tests/test_utils.py b/libs/langgraph/tests/test_utils.py index 455032afa..fabac0595 100644 --- a/libs/langgraph/tests/test_utils.py +++ b/libs/langgraph/tests/test_utils.py @@ -17,18 +17,18 @@ import langsmith import pytest from typing_extensions import NotRequired, Required, TypedDict -from langgraph.graph import END, StateGraph -from langgraph.graph.state import CompiledStateGraph -from langgraph.utils.config import _is_not_empty -from langgraph.utils.fields import ( +from langgraph._internal._config import _is_not_empty +from langgraph._internal._fields import ( _is_optional_type, get_enhanced_type_hints, get_field_default, ) -from langgraph.utils.runnable import ( +from langgraph._internal._runnable import ( is_async_callable, is_async_generator, ) +from langgraph.graph import END, StateGraph +from langgraph.graph.state import CompiledStateGraph pytestmark = pytest.mark.anyio diff --git a/libs/langgraph/langgraph/utils/__init__.py b/libs/langgraph/utils/__init__.py similarity index 100% rename from libs/langgraph/langgraph/utils/__init__.py rename to libs/langgraph/utils/__init__.py diff --git a/libs/langgraph/utils/runnable.py b/libs/langgraph/utils/runnable.py new file mode 100644 index 000000000..0c1a94ed4 --- /dev/null +++ b/libs/langgraph/utils/runnable.py @@ -0,0 +1,2 @@ +# import for backwards compatibility +from langgraph._internal._runnable import RunnableCallable, RunnableSeq # noqa: F401 diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index db2d48375..a4f5a8600 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -2,10 +2,8 @@ version = 1 revision = 2 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.13' and python_full_version < '4.0'", - "python_full_version >= '3.12.4' and python_full_version < '3.13'", - "python_full_version >= '3.11' and python_full_version < '3.12.4'", - "python_full_version >= '4.0'", + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] @@ -57,14 +55,14 @@ wheels = [ [[package]] name = "argon2-cffi" -version = "23.1.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798, upload-time = "2023-08-15T14:13:12.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124, upload-time = "2023-08-15T14:13:10.752Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, ] [[package]] @@ -175,7 +173,7 @@ name = "blockbuster" version = "1.5.24" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "forbiddenfruit", marker = "python_full_version >= '3.11' and python_full_version < '4.0' and implementation_name == 'cpython'" }, + { name = "forbiddenfruit", marker = "python_full_version >= '3.11' and implementation_name == 'cpython'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/35/c8/1e456a043179f2aef10bcaafea79f6d06c0ac45cc994767a54f680509f3b/blockbuster-1.5.24.tar.gz", hash = "sha256:97645775761a5d425666ec0bc99629b65c7eccdc2f770d2439850682567af4ec", size = 51245, upload-time = "2025-03-18T10:12:06.398Z" } wheels = [ @@ -184,11 +182,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, ] [[package]] @@ -354,10 +352,8 @@ name = "click" version = "8.2.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and python_full_version < '4.0'", - "python_full_version >= '3.12.4' and python_full_version < '3.13'", - "python_full_version >= '3.11' and python_full_version < '3.12.4'", - "python_full_version >= '4.0'", + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", "python_full_version == '3.10.*'", ] dependencies = [ @@ -400,76 +396,76 @@ wheels = [ [[package]] name = "coverage" -version = "7.8.2" +version = "7.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/07/998afa4a0ecdf9b1981ae05415dad2d4e7716e1b1f00abbd91691ac09ac9/coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27", size = 812759, upload-time = "2025-05-23T11:39:57.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/e0/98670a80884f64578f0c22cd70c5e81a6e07b08167721c7487b4d70a7ca0/coverage-7.9.1.tar.gz", hash = "sha256:6cf43c78c4282708a28e466316935ec7489a9c487518a77fa68f716c67909cec", size = 813650, upload-time = "2025-06-13T13:02:28.627Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/6b/7dd06399a5c0b81007e3a6af0395cd60e6a30f959f8d407d3ee04642e896/coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a", size = 211573, upload-time = "2025-05-23T11:37:47.207Z" }, - { url = "https://files.pythonhosted.org/packages/f0/df/2b24090820a0bac1412955fb1a4dade6bc3b8dcef7b899c277ffaf16916d/coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be", size = 212006, upload-time = "2025-05-23T11:37:50.289Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c4/e4e3b998e116625562a872a342419652fa6ca73f464d9faf9f52f1aff427/coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3", size = 241128, upload-time = "2025-05-23T11:37:52.229Z" }, - { url = "https://files.pythonhosted.org/packages/b1/67/b28904afea3e87a895da850ba587439a61699bf4b73d04d0dfd99bbd33b4/coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6", size = 239026, upload-time = "2025-05-23T11:37:53.846Z" }, - { url = "https://files.pythonhosted.org/packages/8c/0f/47bf7c5630d81bc2cd52b9e13043685dbb7c79372a7f5857279cc442b37c/coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622", size = 240172, upload-time = "2025-05-23T11:37:55.711Z" }, - { url = "https://files.pythonhosted.org/packages/ba/38/af3eb9d36d85abc881f5aaecf8209383dbe0fa4cac2d804c55d05c51cb04/coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c", size = 240086, upload-time = "2025-05-23T11:37:57.724Z" }, - { url = "https://files.pythonhosted.org/packages/9e/64/c40c27c2573adeba0fe16faf39a8aa57368a1f2148865d6bb24c67eadb41/coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3", size = 238792, upload-time = "2025-05-23T11:37:59.737Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ab/b7c85146f15457671c1412afca7c25a5696d7625e7158002aa017e2d7e3c/coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404", size = 239096, upload-time = "2025-05-23T11:38:01.693Z" }, - { url = "https://files.pythonhosted.org/packages/d3/50/9446dad1310905fb1dc284d60d4320a5b25d4e3e33f9ea08b8d36e244e23/coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7", size = 214144, upload-time = "2025-05-23T11:38:03.68Z" }, - { url = "https://files.pythonhosted.org/packages/23/ed/792e66ad7b8b0df757db8d47af0c23659cdb5a65ef7ace8b111cacdbee89/coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347", size = 215043, upload-time = "2025-05-23T11:38:05.217Z" }, - { url = "https://files.pythonhosted.org/packages/6a/4d/1ff618ee9f134d0de5cc1661582c21a65e06823f41caf801aadf18811a8e/coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9", size = 211692, upload-time = "2025-05-23T11:38:08.485Z" }, - { url = "https://files.pythonhosted.org/packages/96/fa/c3c1b476de96f2bc7a8ca01a9f1fcb51c01c6b60a9d2c3e66194b2bdb4af/coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879", size = 212115, upload-time = "2025-05-23T11:38:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/f7/c2/5414c5a1b286c0f3881ae5adb49be1854ac5b7e99011501f81c8c1453065/coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a", size = 244740, upload-time = "2025-05-23T11:38:11.947Z" }, - { url = "https://files.pythonhosted.org/packages/cd/46/1ae01912dfb06a642ef3dd9cf38ed4996fda8fe884dab8952da616f81a2b/coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5", size = 242429, upload-time = "2025-05-23T11:38:13.955Z" }, - { url = "https://files.pythonhosted.org/packages/06/58/38c676aec594bfe2a87c7683942e5a30224791d8df99bcc8439fde140377/coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11", size = 244218, upload-time = "2025-05-23T11:38:15.631Z" }, - { url = "https://files.pythonhosted.org/packages/80/0c/95b1023e881ce45006d9abc250f76c6cdab7134a1c182d9713878dfefcb2/coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a", size = 243865, upload-time = "2025-05-23T11:38:17.622Z" }, - { url = "https://files.pythonhosted.org/packages/57/37/0ae95989285a39e0839c959fe854a3ae46c06610439350d1ab860bf020ac/coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb", size = 242038, upload-time = "2025-05-23T11:38:19.966Z" }, - { url = "https://files.pythonhosted.org/packages/4d/82/40e55f7c0eb5e97cc62cbd9d0746fd24e8caf57be5a408b87529416e0c70/coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54", size = 242567, upload-time = "2025-05-23T11:38:21.912Z" }, - { url = "https://files.pythonhosted.org/packages/f9/35/66a51adc273433a253989f0d9cc7aa6bcdb4855382cf0858200afe578861/coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a", size = 214194, upload-time = "2025-05-23T11:38:23.571Z" }, - { url = "https://files.pythonhosted.org/packages/f6/8f/a543121f9f5f150eae092b08428cb4e6b6d2d134152c3357b77659d2a605/coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975", size = 215109, upload-time = "2025-05-23T11:38:25.137Z" }, - { url = "https://files.pythonhosted.org/packages/77/65/6cc84b68d4f35186463cd7ab1da1169e9abb59870c0f6a57ea6aba95f861/coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53", size = 213521, upload-time = "2025-05-23T11:38:27.123Z" }, - { url = "https://files.pythonhosted.org/packages/8d/2a/1da1ada2e3044fcd4a3254fb3576e160b8fe5b36d705c8a31f793423f763/coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c", size = 211876, upload-time = "2025-05-23T11:38:29.01Z" }, - { url = "https://files.pythonhosted.org/packages/70/e9/3d715ffd5b6b17a8be80cd14a8917a002530a99943cc1939ad5bb2aa74b9/coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1", size = 212130, upload-time = "2025-05-23T11:38:30.675Z" }, - { url = "https://files.pythonhosted.org/packages/a0/02/fdce62bb3c21649abfd91fbdcf041fb99be0d728ff00f3f9d54d97ed683e/coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279", size = 246176, upload-time = "2025-05-23T11:38:32.395Z" }, - { url = "https://files.pythonhosted.org/packages/a7/52/decbbed61e03b6ffe85cd0fea360a5e04a5a98a7423f292aae62423b8557/coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99", size = 243068, upload-time = "2025-05-23T11:38:33.989Z" }, - { url = "https://files.pythonhosted.org/packages/38/6c/d0e9c0cce18faef79a52778219a3c6ee8e336437da8eddd4ab3dbd8fadff/coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20", size = 245328, upload-time = "2025-05-23T11:38:35.568Z" }, - { url = "https://files.pythonhosted.org/packages/f0/70/f703b553a2f6b6c70568c7e398ed0789d47f953d67fbba36a327714a7bca/coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2", size = 245099, upload-time = "2025-05-23T11:38:37.627Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fb/4cbb370dedae78460c3aacbdad9d249e853f3bc4ce5ff0e02b1983d03044/coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57", size = 243314, upload-time = "2025-05-23T11:38:39.238Z" }, - { url = "https://files.pythonhosted.org/packages/39/9f/1afbb2cb9c8699b8bc38afdce00a3b4644904e6a38c7bf9005386c9305ec/coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f", size = 244489, upload-time = "2025-05-23T11:38:40.845Z" }, - { url = "https://files.pythonhosted.org/packages/79/fa/f3e7ec7d220bff14aba7a4786ae47043770cbdceeea1803083059c878837/coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8", size = 214366, upload-time = "2025-05-23T11:38:43.551Z" }, - { url = "https://files.pythonhosted.org/packages/54/aa/9cbeade19b7e8e853e7ffc261df885d66bf3a782c71cba06c17df271f9e6/coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223", size = 215165, upload-time = "2025-05-23T11:38:45.148Z" }, - { url = "https://files.pythonhosted.org/packages/c4/73/e2528bf1237d2448f882bbebaec5c3500ef07301816c5c63464b9da4d88a/coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f", size = 213548, upload-time = "2025-05-23T11:38:46.74Z" }, - { url = "https://files.pythonhosted.org/packages/1a/93/eb6400a745ad3b265bac36e8077fdffcf0268bdbbb6c02b7220b624c9b31/coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca", size = 211898, upload-time = "2025-05-23T11:38:49.066Z" }, - { url = "https://files.pythonhosted.org/packages/1b/7c/bdbf113f92683024406a1cd226a199e4200a2001fc85d6a6e7e299e60253/coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d", size = 212171, upload-time = "2025-05-23T11:38:51.207Z" }, - { url = "https://files.pythonhosted.org/packages/91/22/594513f9541a6b88eb0dba4d5da7d71596dadef6b17a12dc2c0e859818a9/coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85", size = 245564, upload-time = "2025-05-23T11:38:52.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f4/2860fd6abeebd9f2efcfe0fd376226938f22afc80c1943f363cd3c28421f/coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257", size = 242719, upload-time = "2025-05-23T11:38:54.529Z" }, - { url = "https://files.pythonhosted.org/packages/89/60/f5f50f61b6332451520e6cdc2401700c48310c64bc2dd34027a47d6ab4ca/coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108", size = 244634, upload-time = "2025-05-23T11:38:57.326Z" }, - { url = "https://files.pythonhosted.org/packages/3b/70/7f4e919039ab7d944276c446b603eea84da29ebcf20984fb1fdf6e602028/coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0", size = 244824, upload-time = "2025-05-23T11:38:59.421Z" }, - { url = "https://files.pythonhosted.org/packages/26/45/36297a4c0cea4de2b2c442fe32f60c3991056c59cdc3cdd5346fbb995c97/coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050", size = 242872, upload-time = "2025-05-23T11:39:01.049Z" }, - { url = "https://files.pythonhosted.org/packages/a4/71/e041f1b9420f7b786b1367fa2a375703889ef376e0d48de9f5723fb35f11/coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48", size = 244179, upload-time = "2025-05-23T11:39:02.709Z" }, - { url = "https://files.pythonhosted.org/packages/bd/db/3c2bf49bdc9de76acf2491fc03130c4ffc51469ce2f6889d2640eb563d77/coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7", size = 214393, upload-time = "2025-05-23T11:39:05.457Z" }, - { url = "https://files.pythonhosted.org/packages/c6/dc/947e75d47ebbb4b02d8babb1fad4ad381410d5bc9da7cfca80b7565ef401/coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3", size = 215194, upload-time = "2025-05-23T11:39:07.171Z" }, - { url = "https://files.pythonhosted.org/packages/90/31/a980f7df8a37eaf0dc60f932507fda9656b3a03f0abf188474a0ea188d6d/coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7", size = 213580, upload-time = "2025-05-23T11:39:08.862Z" }, - { url = "https://files.pythonhosted.org/packages/8a/6a/25a37dd90f6c95f59355629417ebcb74e1c34e38bb1eddf6ca9b38b0fc53/coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008", size = 212734, upload-time = "2025-05-23T11:39:11.109Z" }, - { url = "https://files.pythonhosted.org/packages/36/8b/3a728b3118988725f40950931abb09cd7f43b3c740f4640a59f1db60e372/coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36", size = 212959, upload-time = "2025-05-23T11:39:12.751Z" }, - { url = "https://files.pythonhosted.org/packages/53/3c/212d94e6add3a3c3f412d664aee452045ca17a066def8b9421673e9482c4/coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46", size = 257024, upload-time = "2025-05-23T11:39:15.569Z" }, - { url = "https://files.pythonhosted.org/packages/a4/40/afc03f0883b1e51bbe804707aae62e29c4e8c8bbc365c75e3e4ddeee9ead/coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be", size = 252867, upload-time = "2025-05-23T11:39:17.64Z" }, - { url = "https://files.pythonhosted.org/packages/18/a2/3699190e927b9439c6ded4998941a3c1d6fa99e14cb28d8536729537e307/coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740", size = 255096, upload-time = "2025-05-23T11:39:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/b4/06/16e3598b9466456b718eb3e789457d1a5b8bfb22e23b6e8bbc307df5daf0/coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625", size = 256276, upload-time = "2025-05-23T11:39:21.077Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d5/4b5a120d5d0223050a53d2783c049c311eea1709fa9de12d1c358e18b707/coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b", size = 254478, upload-time = "2025-05-23T11:39:22.838Z" }, - { url = "https://files.pythonhosted.org/packages/ba/85/f9ecdb910ecdb282b121bfcaa32fa8ee8cbd7699f83330ee13ff9bbf1a85/coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199", size = 255255, upload-time = "2025-05-23T11:39:24.644Z" }, - { url = "https://files.pythonhosted.org/packages/50/63/2d624ac7d7ccd4ebbd3c6a9eba9d7fc4491a1226071360d59dd84928ccb2/coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8", size = 215109, upload-time = "2025-05-23T11:39:26.722Z" }, - { url = "https://files.pythonhosted.org/packages/22/5e/7053b71462e970e869111c1853afd642212568a350eba796deefdfbd0770/coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d", size = 216268, upload-time = "2025-05-23T11:39:28.429Z" }, - { url = "https://files.pythonhosted.org/packages/07/69/afa41aa34147655543dbe96994f8a246daf94b361ccf5edfd5df62ce066a/coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b", size = 214071, upload-time = "2025-05-23T11:39:30.55Z" }, - { url = "https://files.pythonhosted.org/packages/71/1e/388267ad9c6aa126438acc1ceafede3bb746afa9872e3ec5f0691b7d5efa/coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a", size = 211566, upload-time = "2025-05-23T11:39:32.333Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a5/acc03e5cf0bba6357f5e7c676343de40fbf431bb1e115fbebf24b2f7f65e/coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d", size = 211996, upload-time = "2025-05-23T11:39:34.512Z" }, - { url = "https://files.pythonhosted.org/packages/5b/a2/0fc0a9f6b7c24fa4f1d7210d782c38cb0d5e692666c36eaeae9a441b6755/coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca", size = 240741, upload-time = "2025-05-23T11:39:36.252Z" }, - { url = "https://files.pythonhosted.org/packages/e6/da/1c6ba2cf259710eed8916d4fd201dccc6be7380ad2b3b9f63ece3285d809/coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d", size = 238672, upload-time = "2025-05-23T11:39:38.03Z" }, - { url = "https://files.pythonhosted.org/packages/ac/51/c8fae0dc3ca421e6e2509503696f910ff333258db672800c3bdef256265a/coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787", size = 239769, upload-time = "2025-05-23T11:39:40.24Z" }, - { url = "https://files.pythonhosted.org/packages/59/8e/b97042ae92c59f40be0c989df090027377ba53f2d6cef73c9ca7685c26a6/coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7", size = 239555, upload-time = "2025-05-23T11:39:42.3Z" }, - { url = "https://files.pythonhosted.org/packages/47/35/b8893e682d6e96b1db2af5997fc13ef62219426fb17259d6844c693c5e00/coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3", size = 237768, upload-time = "2025-05-23T11:39:44.069Z" }, - { url = "https://files.pythonhosted.org/packages/03/6c/023b0b9a764cb52d6243a4591dcb53c4caf4d7340445113a1f452bb80591/coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7", size = 238757, upload-time = "2025-05-23T11:39:46.195Z" }, - { url = "https://files.pythonhosted.org/packages/03/ed/3af7e4d721bd61a8df7de6de9e8a4271e67f3d9e086454558fd9f48eb4f6/coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a", size = 214166, upload-time = "2025-05-23T11:39:47.934Z" }, - { url = "https://files.pythonhosted.org/packages/9d/30/ee774b626773750dc6128354884652507df3c59d6aa8431526107e595227/coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e", size = 215050, upload-time = "2025-05-23T11:39:50.252Z" }, - { url = "https://files.pythonhosted.org/packages/69/2f/572b29496d8234e4a7773200dd835a0d32d9e171f2d974f3fe04a9dbc271/coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837", size = 203636, upload-time = "2025-05-23T11:39:52.002Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1a/0b9c32220ad694d66062f571cc5cedfa9997b64a591e8a500bb63de1bd40/coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32", size = 203623, upload-time = "2025-05-23T11:39:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/c1/78/1c1c5ec58f16817c09cbacb39783c3655d54a221b6552f47ff5ac9297603/coverage-7.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc94d7c5e8423920787c33d811c0be67b7be83c705f001f7180c7b186dcf10ca", size = 212028, upload-time = "2025-06-13T13:00:29.293Z" }, + { url = "https://files.pythonhosted.org/packages/98/db/e91b9076f3a888e3b4ad7972ea3842297a52cc52e73fd1e529856e473510/coverage-7.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16aa0830d0c08a2c40c264cef801db8bc4fc0e1892782e45bcacbd5889270509", size = 212420, upload-time = "2025-06-13T13:00:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d0/2b3733412954576b0aea0a16c3b6b8fbe95eb975d8bfa10b07359ead4252/coverage-7.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf95981b126f23db63e9dbe4cf65bd71f9a6305696fa5e2262693bc4e2183f5b", size = 241529, upload-time = "2025-06-13T13:00:35.786Z" }, + { url = "https://files.pythonhosted.org/packages/b3/00/5e2e5ae2e750a872226a68e984d4d3f3563cb01d1afb449a17aa819bc2c4/coverage-7.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f05031cf21699785cd47cb7485f67df619e7bcdae38e0fde40d23d3d0210d3c3", size = 239403, upload-time = "2025-06-13T13:00:37.399Z" }, + { url = "https://files.pythonhosted.org/packages/37/3b/a2c27736035156b0a7c20683afe7df498480c0dfdf503b8c878a21b6d7fb/coverage-7.9.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4fbcab8764dc072cb651a4bcda4d11fb5658a1d8d68842a862a6610bd8cfa3", size = 240548, upload-time = "2025-06-13T13:00:39.647Z" }, + { url = "https://files.pythonhosted.org/packages/98/f5/13d5fc074c3c0e0dc80422d9535814abf190f1254d7c3451590dc4f8b18c/coverage-7.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16649a7330ec307942ed27d06ee7e7a38417144620bb3d6e9a18ded8a2d3e5", size = 240459, upload-time = "2025-06-13T13:00:40.934Z" }, + { url = "https://files.pythonhosted.org/packages/36/24/24b9676ea06102df824c4a56ffd13dc9da7904478db519efa877d16527d5/coverage-7.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cea0a27a89e6432705fffc178064503508e3c0184b4f061700e771a09de58187", size = 239128, upload-time = "2025-06-13T13:00:42.343Z" }, + { url = "https://files.pythonhosted.org/packages/be/05/242b7a7d491b369ac5fee7908a6e5ba42b3030450f3ad62c645b40c23e0e/coverage-7.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e980b53a959fa53b6f05343afbd1e6f44a23ed6c23c4b4c56c6662bbb40c82ce", size = 239402, upload-time = "2025-06-13T13:00:43.634Z" }, + { url = "https://files.pythonhosted.org/packages/73/e0/4de7f87192fa65c9c8fbaeb75507e124f82396b71de1797da5602898be32/coverage-7.9.1-cp310-cp310-win32.whl", hash = "sha256:70760b4c5560be6ca70d11f8988ee6542b003f982b32f83d5ac0b72476607b70", size = 214518, upload-time = "2025-06-13T13:00:45.622Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ab/5e4e2fe458907d2a65fab62c773671cfc5ac704f1e7a9ddd91996f66e3c2/coverage-7.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a66e8f628b71f78c0e0342003d53b53101ba4e00ea8dabb799d9dba0abbbcebe", size = 215436, upload-time = "2025-06-13T13:00:47.245Z" }, + { url = "https://files.pythonhosted.org/packages/60/34/fa69372a07d0903a78ac103422ad34db72281c9fc625eba94ac1185da66f/coverage-7.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95c765060e65c692da2d2f51a9499c5e9f5cf5453aeaf1420e3fc847cc060582", size = 212146, upload-time = "2025-06-13T13:00:48.496Z" }, + { url = "https://files.pythonhosted.org/packages/27/f0/da1894915d2767f093f081c42afeba18e760f12fdd7a2f4acbe00564d767/coverage-7.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba383dc6afd5ec5b7a0d0c23d38895db0e15bcba7fb0fa8901f245267ac30d86", size = 212536, upload-time = "2025-06-13T13:00:51.535Z" }, + { url = "https://files.pythonhosted.org/packages/10/d5/3fc33b06e41e390f88eef111226a24e4504d216ab8e5d1a7089aa5a3c87a/coverage-7.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37ae0383f13cbdcf1e5e7014489b0d71cc0106458878ccde52e8a12ced4298ed", size = 245092, upload-time = "2025-06-13T13:00:52.883Z" }, + { url = "https://files.pythonhosted.org/packages/0a/39/7aa901c14977aba637b78e95800edf77f29f5a380d29768c5b66f258305b/coverage-7.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69aa417a030bf11ec46149636314c24c8d60fadb12fc0ee8f10fda0d918c879d", size = 242806, upload-time = "2025-06-13T13:00:54.571Z" }, + { url = "https://files.pythonhosted.org/packages/43/fc/30e5cfeaf560b1fc1989227adedc11019ce4bb7cce59d65db34fe0c2d963/coverage-7.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a4be2a28656afe279b34d4f91c3e26eccf2f85500d4a4ff0b1f8b54bf807338", size = 244610, upload-time = "2025-06-13T13:00:56.932Z" }, + { url = "https://files.pythonhosted.org/packages/bf/15/cca62b13f39650bc87b2b92bb03bce7f0e79dd0bf2c7529e9fc7393e4d60/coverage-7.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:382e7ddd5289f140259b610e5f5c58f713d025cb2f66d0eb17e68d0a94278875", size = 244257, upload-time = "2025-06-13T13:00:58.545Z" }, + { url = "https://files.pythonhosted.org/packages/cd/1a/c0f2abe92c29e1464dbd0ff9d56cb6c88ae2b9e21becdb38bea31fcb2f6c/coverage-7.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e5532482344186c543c37bfad0ee6069e8ae4fc38d073b8bc836fc8f03c9e250", size = 242309, upload-time = "2025-06-13T13:00:59.836Z" }, + { url = "https://files.pythonhosted.org/packages/57/8d/c6fd70848bd9bf88fa90df2af5636589a8126d2170f3aade21ed53f2b67a/coverage-7.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a39d18b3f50cc121d0ce3838d32d58bd1d15dab89c910358ebefc3665712256c", size = 242898, upload-time = "2025-06-13T13:01:02.506Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9e/6ca46c7bff4675f09a66fe2797cd1ad6a24f14c9c7c3b3ebe0470a6e30b8/coverage-7.9.1-cp311-cp311-win32.whl", hash = "sha256:dd24bd8d77c98557880def750782df77ab2b6885a18483dc8588792247174b32", size = 214561, upload-time = "2025-06-13T13:01:04.012Z" }, + { url = "https://files.pythonhosted.org/packages/a1/30/166978c6302010742dabcdc425fa0f938fa5a800908e39aff37a7a876a13/coverage-7.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b55ad10a35a21b8015eabddc9ba31eb590f54adc9cd39bcf09ff5349fd52125", size = 215493, upload-time = "2025-06-13T13:01:05.702Z" }, + { url = "https://files.pythonhosted.org/packages/60/07/a6d2342cd80a5be9f0eeab115bc5ebb3917b4a64c2953534273cf9bc7ae6/coverage-7.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ad935f0016be24c0e97fc8c40c465f9c4b85cbbe6eac48934c0dc4d2568321e", size = 213869, upload-time = "2025-06-13T13:01:09.345Z" }, + { url = "https://files.pythonhosted.org/packages/68/d9/7f66eb0a8f2fce222de7bdc2046ec41cb31fe33fb55a330037833fb88afc/coverage-7.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8de12b4b87c20de895f10567639c0797b621b22897b0af3ce4b4e204a743626", size = 212336, upload-time = "2025-06-13T13:01:10.909Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/e07cb920ef3addf20f052ee3d54906e57407b6aeee3227a9c91eea38a665/coverage-7.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5add197315a054e92cee1b5f686a2bcba60c4c3e66ee3de77ace6c867bdee7cb", size = 212571, upload-time = "2025-06-13T13:01:12.518Z" }, + { url = "https://files.pythonhosted.org/packages/78/f8/96f155de7e9e248ca9c8ff1a40a521d944ba48bec65352da9be2463745bf/coverage-7.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600a1d4106fe66f41e5d0136dfbc68fe7200a5cbe85610ddf094f8f22e1b0300", size = 246377, upload-time = "2025-06-13T13:01:14.87Z" }, + { url = "https://files.pythonhosted.org/packages/3e/cf/1d783bd05b7bca5c10ded5f946068909372e94615a4416afadfe3f63492d/coverage-7.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a876e4c3e5a2a1715a6608906aa5a2e0475b9c0f68343c2ada98110512ab1d8", size = 243394, upload-time = "2025-06-13T13:01:16.23Z" }, + { url = "https://files.pythonhosted.org/packages/02/dd/e7b20afd35b0a1abea09fb3998e1abc9f9bd953bee548f235aebd2b11401/coverage-7.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f34346dd63010453922c8e628a52ea2d2ccd73cb2487f7700ac531b247c8a5", size = 245586, upload-time = "2025-06-13T13:01:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/4e/38/b30b0006fea9d617d1cb8e43b1bc9a96af11eff42b87eb8c716cf4d37469/coverage-7.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:888f8eee13f2377ce86d44f338968eedec3291876b0b8a7289247ba52cb984cd", size = 245396, upload-time = "2025-06-13T13:01:19.164Z" }, + { url = "https://files.pythonhosted.org/packages/31/e4/4d8ec1dc826e16791f3daf1b50943e8e7e1eb70e8efa7abb03936ff48418/coverage-7.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9969ef1e69b8c8e1e70d591f91bbc37fc9a3621e447525d1602801a24ceda898", size = 243577, upload-time = "2025-06-13T13:01:22.433Z" }, + { url = "https://files.pythonhosted.org/packages/25/f4/b0e96c5c38e6e40ef465c4bc7f138863e2909c00e54a331da335faf0d81a/coverage-7.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60c458224331ee3f1a5b472773e4a085cc27a86a0b48205409d364272d67140d", size = 244809, upload-time = "2025-06-13T13:01:24.143Z" }, + { url = "https://files.pythonhosted.org/packages/8a/65/27e0a1fa5e2e5079bdca4521be2f5dabf516f94e29a0defed35ac2382eb2/coverage-7.9.1-cp312-cp312-win32.whl", hash = "sha256:5f646a99a8c2b3ff4c6a6e081f78fad0dde275cd59f8f49dc4eab2e394332e74", size = 214724, upload-time = "2025-06-13T13:01:25.435Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a8/d5b128633fd1a5e0401a4160d02fa15986209a9e47717174f99dc2f7166d/coverage-7.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:30f445f85c353090b83e552dcbbdad3ec84c7967e108c3ae54556ca69955563e", size = 215535, upload-time = "2025-06-13T13:01:27.861Z" }, + { url = "https://files.pythonhosted.org/packages/a3/37/84bba9d2afabc3611f3e4325ee2c6a47cd449b580d4a606b240ce5a6f9bf/coverage-7.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:af41da5dca398d3474129c58cb2b106a5d93bbb196be0d307ac82311ca234342", size = 213904, upload-time = "2025-06-13T13:01:29.202Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a7/a027970c991ca90f24e968999f7d509332daf6b8c3533d68633930aaebac/coverage-7.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:31324f18d5969feef7344a932c32428a2d1a3e50b15a6404e97cba1cc9b2c631", size = 212358, upload-time = "2025-06-13T13:01:30.909Z" }, + { url = "https://files.pythonhosted.org/packages/f2/48/6aaed3651ae83b231556750280682528fea8ac7f1232834573472d83e459/coverage-7.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c804506d624e8a20fb3108764c52e0eef664e29d21692afa375e0dd98dc384f", size = 212620, upload-time = "2025-06-13T13:01:32.256Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2a/f4b613f3b44d8b9f144847c89151992b2b6b79cbc506dee89ad0c35f209d/coverage-7.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef64c27bc40189f36fcc50c3fb8f16ccda73b6a0b80d9bd6e6ce4cffcd810bbd", size = 245788, upload-time = "2025-06-13T13:01:33.948Z" }, + { url = "https://files.pythonhosted.org/packages/04/d2/de4fdc03af5e4e035ef420ed26a703c6ad3d7a07aff2e959eb84e3b19ca8/coverage-7.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4fe2348cc6ec372e25adec0219ee2334a68d2f5222e0cba9c0d613394e12d86", size = 243001, upload-time = "2025-06-13T13:01:35.285Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e8/eed18aa5583b0423ab7f04e34659e51101135c41cd1dcb33ac1d7013a6d6/coverage-7.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34ed2186fe52fcc24d4561041979a0dec69adae7bce2ae8d1c49eace13e55c43", size = 244985, upload-time = "2025-06-13T13:01:36.712Z" }, + { url = "https://files.pythonhosted.org/packages/17/f8/ae9e5cce8885728c934eaa58ebfa8281d488ef2afa81c3dbc8ee9e6d80db/coverage-7.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:25308bd3d00d5eedd5ae7d4357161f4df743e3c0240fa773ee1b0f75e6c7c0f1", size = 245152, upload-time = "2025-06-13T13:01:39.303Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c8/272c01ae792bb3af9b30fac14d71d63371db227980682836ec388e2c57c0/coverage-7.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73e9439310f65d55a5a1e0564b48e34f5369bee943d72c88378f2d576f5a5751", size = 243123, upload-time = "2025-06-13T13:01:40.727Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d0/2819a1e3086143c094ab446e3bdf07138527a7b88cb235c488e78150ba7a/coverage-7.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37ab6be0859141b53aa89412a82454b482c81cf750de4f29223d52268a86de67", size = 244506, upload-time = "2025-06-13T13:01:42.184Z" }, + { url = "https://files.pythonhosted.org/packages/8b/4e/9f6117b89152df7b6112f65c7a4ed1f2f5ec8e60c4be8f351d91e7acc848/coverage-7.9.1-cp313-cp313-win32.whl", hash = "sha256:64bdd969456e2d02a8b08aa047a92d269c7ac1f47e0c977675d550c9a0863643", size = 214766, upload-time = "2025-06-13T13:01:44.482Z" }, + { url = "https://files.pythonhosted.org/packages/27/0f/4b59f7c93b52c2c4ce7387c5a4e135e49891bb3b7408dcc98fe44033bbe0/coverage-7.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:be9e3f68ca9edb897c2184ad0eee815c635565dbe7a0e7e814dc1f7cbab92c0a", size = 215568, upload-time = "2025-06-13T13:01:45.772Z" }, + { url = "https://files.pythonhosted.org/packages/09/1e/9679826336f8c67b9c39a359352882b24a8a7aee48d4c9cad08d38d7510f/coverage-7.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:1c503289ffef1d5105d91bbb4d62cbe4b14bec4d13ca225f9c73cde9bb46207d", size = 213939, upload-time = "2025-06-13T13:01:47.087Z" }, + { url = "https://files.pythonhosted.org/packages/bb/5b/5c6b4e7a407359a2e3b27bf9c8a7b658127975def62077d441b93a30dbe8/coverage-7.9.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0b3496922cb5f4215bf5caaef4cf12364a26b0be82e9ed6d050f3352cf2d7ef0", size = 213079, upload-time = "2025-06-13T13:01:48.554Z" }, + { url = "https://files.pythonhosted.org/packages/a2/22/1e2e07279fd2fd97ae26c01cc2186e2258850e9ec125ae87184225662e89/coverage-7.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9565c3ab1c93310569ec0d86b017f128f027cab0b622b7af288696d7ed43a16d", size = 213299, upload-time = "2025-06-13T13:01:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/4c5125a4b69d66b8c85986d3321520f628756cf524af810baab0790c7647/coverage-7.9.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2241ad5dbf79ae1d9c08fe52b36d03ca122fb9ac6bca0f34439e99f8327ac89f", size = 256535, upload-time = "2025-06-13T13:01:51.314Z" }, + { url = "https://files.pythonhosted.org/packages/81/8b/e36a04889dda9960be4263e95e777e7b46f1bb4fc32202612c130a20c4da/coverage-7.9.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb5838701ca68b10ebc0937dbd0eb81974bac54447c55cd58dea5bca8451029", size = 252756, upload-time = "2025-06-13T13:01:54.403Z" }, + { url = "https://files.pythonhosted.org/packages/98/82/be04eff8083a09a4622ecd0e1f31a2c563dbea3ed848069e7b0445043a70/coverage-7.9.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a25f814591a8c0c5372c11ac8967f669b97444c47fd794926e175c4047ece", size = 254912, upload-time = "2025-06-13T13:01:56.769Z" }, + { url = "https://files.pythonhosted.org/packages/0f/25/c26610a2c7f018508a5ab958e5b3202d900422cf7cdca7670b6b8ca4e8df/coverage-7.9.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2d04b16a6062516df97969f1ae7efd0de9c31eb6ebdceaa0d213b21c0ca1a683", size = 256144, upload-time = "2025-06-13T13:01:58.19Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8b/fb9425c4684066c79e863f1e6e7ecebb49e3a64d9f7f7860ef1688c56f4a/coverage-7.9.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7931b9e249edefb07cd6ae10c702788546341d5fe44db5b6108a25da4dca513f", size = 254257, upload-time = "2025-06-13T13:01:59.645Z" }, + { url = "https://files.pythonhosted.org/packages/93/df/27b882f54157fc1131e0e215b0da3b8d608d9b8ef79a045280118a8f98fe/coverage-7.9.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52e92b01041151bf607ee858e5a56c62d4b70f4dac85b8c8cb7fb8a351ab2c10", size = 255094, upload-time = "2025-06-13T13:02:01.37Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/cad1c3dbed8b3ee9e16fa832afe365b4e3eeab1fb6edb65ebbf745eabc92/coverage-7.9.1-cp313-cp313t-win32.whl", hash = "sha256:684e2110ed84fd1ca5f40e89aa44adf1729dc85444004111aa01866507adf363", size = 215437, upload-time = "2025-06-13T13:02:02.905Z" }, + { url = "https://files.pythonhosted.org/packages/99/4d/fad293bf081c0e43331ca745ff63673badc20afea2104b431cdd8c278b4c/coverage-7.9.1-cp313-cp313t-win_amd64.whl", hash = "sha256:437c576979e4db840539674e68c84b3cda82bc824dd138d56bead1435f1cb5d7", size = 216605, upload-time = "2025-06-13T13:02:05.638Z" }, + { url = "https://files.pythonhosted.org/packages/1f/56/4ee027d5965fc7fc126d7ec1187529cc30cc7d740846e1ecb5e92d31b224/coverage-7.9.1-cp313-cp313t-win_arm64.whl", hash = "sha256:18a0912944d70aaf5f399e350445738a1a20b50fbea788f640751c2ed9208b6c", size = 214392, upload-time = "2025-06-13T13:02:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d6/c41dd9b02bf16ec001aaf1cbef665537606899a3db1094e78f5ae17540ca/coverage-7.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f424507f57878e424d9a95dc4ead3fbdd72fd201e404e861e465f28ea469951", size = 212029, upload-time = "2025-06-13T13:02:09.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c0/40420d81d731f84c3916dcdf0506b3e6c6570817bff2576b83f780914ae6/coverage-7.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:535fde4001b2783ac80865d90e7cc7798b6b126f4cd8a8c54acfe76804e54e58", size = 212407, upload-time = "2025-06-13T13:02:11.151Z" }, + { url = "https://files.pythonhosted.org/packages/9b/87/f0db7d62d0e09f14d6d2f6ae8c7274a2f09edf74895a34b412a0601e375a/coverage-7.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02532fd3290bb8fa6bec876520842428e2a6ed6c27014eca81b031c2d30e3f71", size = 241160, upload-time = "2025-06-13T13:02:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b7/3337c064f058a5d7696c4867159651a5b5fb01a5202bcf37362f0c51400e/coverage-7.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56f5eb308b17bca3bbff810f55ee26d51926d9f89ba92707ee41d3c061257e55", size = 239027, upload-time = "2025-06-13T13:02:14.294Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/5898a283f66d1bd413c32c2e0e05408196fd4f37e206e2b06c6e0c626e0e/coverage-7.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfa447506c1a52271f1b0de3f42ea0fa14676052549095e378d5bff1c505ff7b", size = 240145, upload-time = "2025-06-13T13:02:15.745Z" }, + { url = "https://files.pythonhosted.org/packages/e0/33/d96e3350078a3c423c549cb5b2ba970de24c5257954d3e4066e2b2152d30/coverage-7.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9ca8e220006966b4a7b68e8984a6aee645a0384b0769e829ba60281fe61ec4f7", size = 239871, upload-time = "2025-06-13T13:02:17.344Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6e/6fb946072455f71a820cac144d49d11747a0f1a21038060a68d2d0200499/coverage-7.9.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49f1d0788ba5b7ba65933f3a18864117c6506619f5ca80326b478f72acf3f385", size = 238122, upload-time = "2025-06-13T13:02:18.849Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5c/bc43f25c8586840ce25a796a8111acf6a2b5f0909ba89a10d41ccff3920d/coverage-7.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68cd53aec6f45b8e4724c0950ce86eacb775c6be01ce6e3669fe4f3a21e768ed", size = 239058, upload-time = "2025-06-13T13:02:21.423Z" }, + { url = "https://files.pythonhosted.org/packages/11/d8/ce2007418dd7fd00ff8c8b898bb150bb4bac2d6a86df05d7b88a07ff595f/coverage-7.9.1-cp39-cp39-win32.whl", hash = "sha256:95335095b6c7b1cc14c3f3f17d5452ce677e8490d101698562b2ffcacc304c8d", size = 214532, upload-time = "2025-06-13T13:02:22.857Z" }, + { url = "https://files.pythonhosted.org/packages/20/21/334e76fa246e92e6d69cab217f7c8a70ae0cc8f01438bd0544103f29528e/coverage-7.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:e1b5191d1648acc439b24721caab2fd0c86679d8549ed2c84d5a7ec1bedcc244", size = 215439, upload-time = "2025-06-13T13:02:24.268Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e5/c723545c3fd3204ebde3b4cc4b927dce709d3b6dc577754bb57f63ca4a4a/coverage-7.9.1-pp39.pp310.pp311-none-any.whl", hash = "sha256:db0f04118d1db74db6c9e1cb1898532c7dcc220f1d2718f058601f7c3f499514", size = 204009, upload-time = "2025-06-13T13:02:25.787Z" }, + { url = "https://files.pythonhosted.org/packages/08/b8/7ddd1e8ba9701dea08ce22029917140e6f66a859427406579fd8d0ca7274/coverage-7.9.1-py3-none-any.whl", hash = "sha256:66b974b145aa189516b6bf2d8423e888b742517d37872f6ee4c5be0073bd9a3c", size = 204000, upload-time = "2025-06-13T13:02:27.173Z" }, ] [package.optional-dependencies] @@ -482,7 +478,7 @@ name = "cryptography" version = "44.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "python_full_version >= '3.11' and python_full_version < '4.0' and platform_python_implementation != 'PyPy'" }, + { name = "cffi", marker = "python_full_version >= '3.11' and platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/53/d6/1411ab4d6108ab167d06254c5be517681f1e331f90edf1379895bcb87020/cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053", size = 711096, upload-time = "2025-05-02T19:36:04.667Z" } wheels = [ @@ -701,8 +697,8 @@ dependencies = [ { name = "comm" }, { name = "debugpy" }, { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "ipython", version = "9.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -745,7 +741,7 @@ wheels = [ [[package]] name = "ipython" -version = "8.36.0" +version = "8.37.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.10.*'", @@ -763,20 +759,18 @@ dependencies = [ { name = "traitlets", marker = "python_full_version == '3.10.*'" }, { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/9f/d9a73710df947b7804bd9d93509463fb3a89e0ddc99c9fcc67279cddbeb6/ipython-8.36.0.tar.gz", hash = "sha256:24658e9fe5c5c819455043235ba59cfffded4a35936eefceceab6b192f7092ff", size = 5604997, upload-time = "2025-04-25T18:03:38.031Z" } +sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/d7/c1c9f371790b3a181e343c4815a361e5a0cc7d90ef6642d64ba5d05de289/ipython-8.36.0-py3-none-any.whl", hash = "sha256:12b913914d010dcffa2711505ec8be4bf0180742d97f1e5175e51f22086428c1", size = 831074, upload-time = "2025-04-25T18:03:34.951Z" }, + { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, ] [[package]] name = "ipython" -version = "9.2.0" +version = "9.4.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and python_full_version < '4.0'", - "python_full_version >= '3.12.4' and python_full_version < '3.13'", - "python_full_version >= '3.11' and python_full_version < '3.12.4'", - "python_full_version >= '4.0'", + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, @@ -791,9 +785,9 @@ dependencies = [ { name = "traitlets", marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/02/63a84444a7409b3c0acd1de9ffe524660e0e5d82ee473e78b45e5bfb64a4/ipython-9.2.0.tar.gz", hash = "sha256:62a9373dbc12f28f9feaf4700d052195bf89806279fc8ca11f3f54017d04751b", size = 4424394, upload-time = "2025-04-25T17:55:40.498Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/80/406f9e3bde1c1fd9bf5a0be9d090f8ae623e401b7670d8f6fdf2ab679891/ipython-9.4.0.tar.gz", hash = "sha256:c033c6d4e7914c3d9768aabe76bbe87ba1dc66a92a05db6bfa1125d81f2ee270", size = 4385338, upload-time = "2025-07-01T11:11:30.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/ce/5e897ee51b7d26ab4e47e5105e7368d40ce6cfae2367acdf3165396d50be/ipython-9.2.0-py3-none-any.whl", hash = "sha256:fef5e33c4a1ae0759e0bba5917c9db4eb8c53fee917b6a526bd973e1ca5159f6", size = 604277, upload-time = "2025-04-25T17:55:37.625Z" }, + { url = "https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl", hash = "sha256:25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066", size = 611021, upload-time = "2025-07-01T11:11:27.85Z" }, ] [[package]] @@ -815,8 +809,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "comm" }, { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "ipython", version = "9.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyterlab-widgets" }, { name = "traitlets" }, { name = "widgetsnbextension" }, @@ -1015,8 +1009,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ipykernel" }, { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "ipython", version = "9.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "prompt-toolkit" }, @@ -1120,7 +1114,7 @@ wheels = [ [[package]] name = "jupyterlab" -version = "4.4.3" +version = "4.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1139,9 +1133,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/2d/d1678dcf2db66cb4a38a80d9e5fcf48c349f3ac12f2d38882993353ae768/jupyterlab-4.4.3.tar.gz", hash = "sha256:a94c32fd7f8b93e82a49dc70a6ec45a5c18281ca2a7228d12765e4e210e5bca2", size = 23032376, upload-time = "2025-05-26T11:18:00.996Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/4d/7ca5b46ea56742880d71a768a9e6fb8f8482228427eb89492d55c5d0bb7d/jupyterlab-4.4.4.tar.gz", hash = "sha256:163fee1ef702e0a057f75d2eed3ed1da8a986d59eb002cbeb6f0c2779e6cd153", size = 23044296, upload-time = "2025-06-28T13:07:20.708Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/4d/7dd5c2ffbb960930452a031dc8410746183c924580f2ab4e68ceb5b3043f/jupyterlab-4.4.3-py3-none-any.whl", hash = "sha256:164302f6d4b6c44773dfc38d585665a4db401a16e5296c37df5cba63904fbdea", size = 12295480, upload-time = "2025-05-26T11:17:56.607Z" }, + { url = "https://files.pythonhosted.org/packages/f8/82/66910ce0995dbfdb33609f41c99fe32ce483b9624a3e7d672af14ff63b9f/jupyterlab-4.4.4-py3-none-any.whl", hash = "sha256:711611e4f59851152eb93316c3547c3ec6291f16bb455f1f4fa380d25637e0dd", size = 12296310, upload-time = "2025-06-28T13:07:15.676Z" }, ] [[package]] @@ -1280,32 +1274,32 @@ dev = [ [[package]] name = "langgraph-api" -version = "0.2.36" +version = "0.2.75" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cloudpickle", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "cryptography", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "httpx", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "jsonschema-rs", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langchain-core", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph-sdk", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langsmith", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "orjson", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "pyjwt", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "sse-starlette", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "starlette", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "structlog", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "tenacity", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "truststore", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "uvicorn", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "watchfiles", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "cloudpickle", marker = "python_full_version >= '3.11'" }, + { name = "cryptography", marker = "python_full_version >= '3.11'" }, + { name = "httpx", marker = "python_full_version >= '3.11'" }, + { name = "jsonschema-rs", marker = "python_full_version >= '3.11'" }, + { name = "langchain-core", marker = "python_full_version >= '3.11'" }, + { name = "langgraph", marker = "python_full_version >= '3.11'" }, + { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" }, + { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11'" }, + { name = "langgraph-sdk", marker = "python_full_version >= '3.11'" }, + { name = "langsmith", marker = "python_full_version >= '3.11'" }, + { name = "orjson", marker = "python_full_version >= '3.11'" }, + { name = "pyjwt", marker = "python_full_version >= '3.11'" }, + { name = "sse-starlette", marker = "python_full_version >= '3.11'" }, + { name = "starlette", marker = "python_full_version >= '3.11'" }, + { name = "structlog", marker = "python_full_version >= '3.11'" }, + { name = "tenacity", marker = "python_full_version >= '3.11'" }, + { name = "truststore", marker = "python_full_version >= '3.11'" }, + { name = "uvicorn", marker = "python_full_version >= '3.11'" }, + { name = "watchfiles", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/57/ea1a163198909f0e8713584d49858ef8884f2d1f14273fd3c40344480606/langgraph_api-0.2.36.tar.gz", hash = "sha256:5d45dce8733b954e61b142abc28d0962a37b03ffc578e20f2ccbc98d48579ece", size = 213623, upload-time = "2025-05-29T20:55:34.628Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/96/ddd4f965b66122ccf46924f9135b719e15140ef4f379d6133092fb2173a9/langgraph_api-0.2.75.tar.gz", hash = "sha256:96afc3bafe34d13f4a2acb3ab256b930888cd74facf394e4f5d4c23a3843e971", size = 231565, upload-time = "2025-06-30T23:10:43.479Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/b7/0d05bbcd42a9070aa200e6001b466ef29d274c4715b654d4f6741a7ed8f5/langgraph_api-0.2.36-py3-none-any.whl", hash = "sha256:e01579d4322c9ce65d0eb8361092a301bcd12d375adb2e806274c7692fe3d8da", size = 177962, upload-time = "2025-05-29T20:55:33.446Z" }, + { url = "https://files.pythonhosted.org/packages/29/18/17b77fa7facfebc6642f2d5dc8d2a10723c2c551adc3090e608e349b4e58/langgraph_api-0.2.75-py3-none-any.whl", hash = "sha256:87fd6352916ad54a851b8add4378ef2ac30a5b1fde6917b8383b9964e84676b9", size = 188024, upload-time = "2025-06-30T23:10:42.083Z" }, ] [[package]] @@ -1403,21 +1397,22 @@ dev = [ [[package]] name = "langgraph-cli" -version = "0.2.10" +version = "0.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "langgraph-sdk", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8d/5e/b12bc8140cd4f797ad7f596bf90558994fd6891df8974bc3fc4747eabdc7/langgraph_cli-0.2.10.tar.gz", hash = "sha256:0c215b364daeaf10de681e4960ecaafc7c9cd2a4100b41052d78d95cababf422", size = 31690, upload-time = "2025-05-09T20:18:27.542Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/c2/53aaae208a3a08f727ef9d03edfd8f499403e017fc451c8ca5b52e95c930/langgraph_cli-0.3.3.tar.gz", hash = "sha256:120adc44064786bb11f1376a7b324b2125276a2e2c3a04bbfab7b8c1622ad4d7", size = 722842, upload-time = "2025-06-13T20:17:52.639Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/06/7151d7c8d6c2bccc0919ddb35a63caf3707b96c94561f47f14b08d73ef5e/langgraph_cli-0.2.10-py3-none-any.whl", hash = "sha256:4aaa8d828d8d3bf0f55d2b2a36b2d9944021d65a4b06ed708c6d5eea725f65a7", size = 34833, upload-time = "2025-05-09T20:18:26.173Z" }, + { url = "https://files.pythonhosted.org/packages/50/aa/f52742987a30f9f2d80c00e1caccb310185dd511d07b56c3e611fe31adb5/langgraph_cli-0.3.3-py3-none-any.whl", hash = "sha256:a0c6b04d6a37191431ede17f7a358f1944c6e5ac9e1b7d2004b9f973fe940094", size = 35880, upload-time = "2025-06-13T20:17:51.437Z" }, ] [package.optional-dependencies] inmem = [ - { name = "langgraph-api", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "langgraph-api", marker = "python_full_version >= '3.11'" }, + { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11'" }, { name = "python-dotenv" }, ] @@ -1454,19 +1449,19 @@ dev = [ [[package]] name = "langgraph-runtime-inmem" -version = "0.2.0" +version = "0.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "blockbuster", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "sse-starlette", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "starlette", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "structlog", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "blockbuster", marker = "python_full_version >= '3.11'" }, + { name = "langgraph", marker = "python_full_version >= '3.11'" }, + { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" }, + { name = "sse-starlette", marker = "python_full_version >= '3.11'" }, + { name = "starlette", marker = "python_full_version >= '3.11'" }, + { name = "structlog", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/8b/54452b0336674afe0ddbba0d5f52ddb323d9fa838e5bab694e1947697323/langgraph_runtime_inmem-0.2.0.tar.gz", hash = "sha256:3eed0bd7e241fbad6c7954e8494ddb133af7587f110c7eb4e3be15df5731a05a", size = 72666, upload-time = "2025-05-21T21:22:12.022Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/17/7ff669ff44a53ab342903c2996fff75a77494af9fe56abcfbca64fe2342b/langgraph_runtime_inmem-0.3.4.tar.gz", hash = "sha256:eda7828f3ea07126e5265024b74a3fa9bf611633ad83ba3296ab9f51d89b7c0c", size = 77424, upload-time = "2025-07-01T14:45:07.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/1f/5676d5237b0682257957338f5f7ee1f063f6d33fa158f878ad926800e95d/langgraph_runtime_inmem-0.2.0-py3-none-any.whl", hash = "sha256:6ad30c493d5963afbeac3136560303fba205b3850c5181f57f1794d8ecb8a461", size = 28406, upload-time = "2025-05-21T21:22:10.674Z" }, + { url = "https://files.pythonhosted.org/packages/95/0e/39c13ca7229a9425a0e5744a1d3817f80d38dd5ca7703494fb9cf836ba45/langgraph_runtime_inmem-0.3.4-py3-none-any.whl", hash = "sha256:dcb9ac68ac90b3fb1ddaf666d14a367ab70e69d5bb5589b77a72c318e29104ae", size = 29139, upload-time = "2025-07-01T14:45:06.472Z" }, ] [[package]] @@ -1607,7 +1602,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.16.0" +version = "1.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, @@ -1615,39 +1610,39 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/5e/a0485f0608a3d67029d3d73cec209278b025e3493a3acfda3ef3a88540fd/mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c", size = 10967416, upload-time = "2025-05-29T13:34:17.783Z" }, - { url = "https://files.pythonhosted.org/packages/4b/53/5837c221f74c0d53a4bfc3003296f8179c3a2a7f336d7de7bbafbe96b688/mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571", size = 10087654, upload-time = "2025-05-29T13:32:37.878Z" }, - { url = "https://files.pythonhosted.org/packages/29/59/5fd2400352c3093bed4c09017fe671d26bc5bb7e6ef2d4bf85f2a2488104/mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491", size = 11875192, upload-time = "2025-05-29T13:34:54.281Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3e/4bfec74663a64c2012f3e278dbc29ffe82b121bc551758590d1b6449ec0c/mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777", size = 12612939, upload-time = "2025-05-29T13:33:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/88/1f/fecbe3dcba4bf2ca34c26ca016383a9676711907f8db4da8354925cbb08f/mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b", size = 12874719, upload-time = "2025-05-29T13:21:52.09Z" }, - { url = "https://files.pythonhosted.org/packages/f3/51/c2d280601cd816c43dfa512a759270d5a5ef638d7ac9bea9134c8305a12f/mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93", size = 9487053, upload-time = "2025-05-29T13:33:29.797Z" }, - { url = "https://files.pythonhosted.org/packages/24/c4/ff2f79db7075c274fe85b5fff8797d29c6b61b8854c39e3b7feb556aa377/mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab", size = 10884498, upload-time = "2025-05-29T13:18:54.066Z" }, - { url = "https://files.pythonhosted.org/packages/02/07/12198e83006235f10f6a7808917376b5d6240a2fd5dce740fe5d2ebf3247/mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2", size = 10011755, upload-time = "2025-05-29T13:34:00.851Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9b/5fd5801a72b5d6fb6ec0105ea1d0e01ab2d4971893076e558d4b6d6b5f80/mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff", size = 11800138, upload-time = "2025-05-29T13:32:55.082Z" }, - { url = "https://files.pythonhosted.org/packages/2e/81/a117441ea5dfc3746431e51d78a4aca569c677aa225bca2cc05a7c239b61/mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666", size = 12533156, upload-time = "2025-05-29T13:19:12.963Z" }, - { url = "https://files.pythonhosted.org/packages/3f/38/88ec57c6c86014d3f06251e00f397b5a7daa6888884d0abf187e4f5f587f/mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c", size = 12742426, upload-time = "2025-05-29T13:20:22.72Z" }, - { url = "https://files.pythonhosted.org/packages/bd/53/7e9d528433d56e6f6f77ccf24af6ce570986c2d98a5839e4c2009ef47283/mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b", size = 9478319, upload-time = "2025-05-29T13:21:17.582Z" }, - { url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" }, - { url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" }, - { url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" }, - { url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" }, - { url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" }, - { url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" }, - { url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" }, - { url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" }, - { url = "https://files.pythonhosted.org/packages/bd/eb/c0759617fe2159aee7a653f13cceafbf7f0b6323b4197403f2e587ca947d/mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3", size = 10956081, upload-time = "2025-05-29T13:19:32.264Z" }, - { url = "https://files.pythonhosted.org/packages/70/35/df3c74a2967bdf86edea58b265feeec181d693432faed1c3b688b7c231e3/mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92", size = 10084422, upload-time = "2025-05-29T13:18:01.437Z" }, - { url = "https://files.pythonhosted.org/packages/b3/07/145ffe29f4b577219943b7b1dc0a71df7ead3c5bed4898686bd87c5b5cc2/mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436", size = 11879670, upload-time = "2025-05-29T13:17:45.971Z" }, - { url = "https://files.pythonhosted.org/packages/c6/94/0421562d6b046e22986758c9ae31865d10ea0ba607ae99b32c9d18b16f66/mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2", size = 12610528, upload-time = "2025-05-29T13:34:36.983Z" }, - { url = "https://files.pythonhosted.org/packages/1a/f1/39a22985b78c766a594ae1e0bbb6f8bdf5f31ea8d0c52291a3c211fd3cd5/mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20", size = 12871923, upload-time = "2025-05-29T13:32:21.823Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8e/84db4fb0d01f43d2c82fa9072ca72a42c49e52d58f44307bbd747c977bc2/mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21", size = 9482931, upload-time = "2025-05-29T13:21:32.326Z" }, - { url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" }, + { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, + { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, + { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, + { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, + { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, + { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, + { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, + { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, + { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, + { url = "https://files.pythonhosted.org/packages/49/5e/ed1e6a7344005df11dfd58b0fdd59ce939a0ba9f7ed37754bf20670b74db/mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069", size = 10959511, upload-time = "2025-06-16T16:47:21.945Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/a7cbc2541e91fe04f43d9e4577264b260fecedb9bccb64ffb1a34b7e6c22/mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da", size = 10075555, upload-time = "2025-06-16T16:50:14.084Z" }, + { url = "https://files.pythonhosted.org/packages/93/f7/c62b1e31a32fbd1546cca5e0a2e5f181be5761265ad1f2e94f2a306fa906/mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c", size = 11874169, upload-time = "2025-06-16T16:49:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/c8/15/db580a28034657fb6cb87af2f8996435a5b19d429ea4dcd6e1c73d418e60/mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383", size = 12610060, upload-time = "2025-06-16T16:34:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/ec/78/c17f48f6843048fa92d1489d3095e99324f2a8c420f831a04ccc454e2e51/mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40", size = 12875199, upload-time = "2025-06-16T16:35:14.448Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d6/ed42167d0a42680381653fd251d877382351e1bd2c6dd8a818764be3beb1/mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b", size = 9487033, upload-time = "2025-06-16T16:49:57.907Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, ] [[package]] @@ -1726,7 +1721,7 @@ wheels = [ [[package]] name = "notebook" -version = "7.4.3" +version = "7.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, @@ -1735,9 +1730,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/21/4f83b15e483da4f4f63928edd0cb08b6e7d33f8a15c23b116a90c44c6235/notebook-7.4.3.tar.gz", hash = "sha256:a1567481cd3853f2610ee0ecf5dfa12bb508e878ee8f92152c134ef7f0568a76", size = 13881668, upload-time = "2025-05-26T14:27:21.656Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/4e/a40b5a94eb01fc51746db7854296d88b84905ab18ee0fcef853a60d708a3/notebook-7.4.4.tar.gz", hash = "sha256:392fd501e266f2fb3466c6fcd3331163a2184968cb5c5accf90292e01dfe528c", size = 13883628, upload-time = "2025-06-30T13:04:18.099Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/1b/16c809d799e3ddd7a97c8b43734f79624b74ddef9707e7d92275a13777bc/notebook-7.4.3-py3-none-any.whl", hash = "sha256:9cdeee954e04101cadb195d90e2ab62b7c9286c1d4f858bf3bb54e40df16c0c3", size = 14286402, upload-time = "2025-05-26T14:27:17.339Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c0/e64d2047fd752249b0b69f6aee2a7049eb94e7273e5baabc8b8ad05cc068/notebook-7.4.4-py3-none-any.whl", hash = "sha256:32840f7f777b6bff79bb101159336e9b332bdbfba1495b8739e34d1d65cbc1c0", size = 14288000, upload-time = "2025-06-30T13:04:14.584Z" }, ] [[package]] @@ -1956,11 +1951,11 @@ wheels = [ [[package]] name = "prometheus-client" -version = "0.22.0" +version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5b/5a/3fa1fa7e91a203759aaf316be394f70f2ef598d589b9785a8611b6094c00/prometheus_client-0.22.0.tar.gz", hash = "sha256:18da1d2241ac2d10c8d2110f13eedcd5c7c0c8af18c926e8731f04fc10cd575c", size = 74443, upload-time = "2025-05-16T20:50:18.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/cf/40dde0a2be27cc1eb41e333d1a674a74ce8b8b0457269cc640fd42b07cf7/prometheus_client-0.22.1.tar.gz", hash = "sha256:190f1331e783cf21eb60bca559354e0a4d4378facecf78f5428c39b675d20d28", size = 69746, upload-time = "2025-06-02T14:29:01.152Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/c7/cee159ba3d7192e84a4c166ec1752f44a5fa859ac0eeda2d73a1da65ab47/prometheus_client-0.22.0-py3-none-any.whl", hash = "sha256:c8951bbe64e62b96cd8e8f5d917279d1b9b91ab766793f33d4dce6c228558713", size = 62658, upload-time = "2025-05-16T20:50:16.978Z" }, + { url = "https://files.pythonhosted.org/packages/32/ae/ec06af4fe3ee72d16973474f122541746196aaa16cea6f66d18b963c6177/prometheus_client-0.22.1-py3-none-any.whl", hash = "sha256:cca895342e308174341b2cbf99a56bef291fbc0ef7b9e5412a0f26d653ba7094", size = 58694, upload-time = "2025-06-02T14:29:00.068Z" }, ] [[package]] @@ -2267,11 +2262,11 @@ wheels = [ [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] @@ -2297,7 +2292,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.3.5" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -2305,24 +2300,26 @@ dependencies = [ { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, + { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] [[package]] name = "pytest-cov" -version = "6.1.1" +version = "6.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" }, + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, ] [[package]] @@ -2377,15 +2374,15 @@ wheels = [ [[package]] name = "pytest-xdist" -version = "3.7.0" +version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "execnet" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/dc/865845cfe987b21658e871d16e0a24e871e00884c545f246dd8f6f69edda/pytest_xdist-3.7.0.tar.gz", hash = "sha256:f9248c99a7c15b7d2f90715df93610353a485827bc06eefb6566d23f6400f126", size = 87550, upload-time = "2025-05-26T21:18:20.251Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/b2/0e802fde6f1c5b2f7ae7e9ad42b83fd4ecebac18a8a8c2f2f14e39dce6e1/pytest_xdist-3.7.0-py3-none-any.whl", hash = "sha256:7d3fbd255998265052435eb9daa4e99b62e6fb9cfb6efd1f858d4d8c0c7f0ca0", size = 46142, upload-time = "2025-05-26T21:18:18.759Z" }, + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, ] [package.optional-dependencies] @@ -2407,11 +2404,11 @@ wheels = [ [[package]] name = "python-dotenv" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, ] [[package]] @@ -2516,91 +2513,77 @@ wheels = [ [[package]] name = "pyzmq" -version = "26.4.0" +version = "27.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/11/b9213d25230ac18a71b39b3723494e57adebe36e066397b961657b3b41c1/pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d", size = 278293, upload-time = "2025-04-04T12:05:44.049Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/06/50a4e9648b3e8b992bef8eb632e457307553a89d294103213cfd47b3da69/pyzmq-27.0.0.tar.gz", hash = "sha256:b1f08eeb9ce1510e6939b6e5dcd46a17765e2333daae78ecf4606808442e52cf", size = 280478, upload-time = "2025-06-13T14:09:07.087Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/b8/af1d814ffc3ff9730f9a970cbf216b6f078e5d251a25ef5201d7bc32a37c/pyzmq-26.4.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:0329bdf83e170ac133f44a233fc651f6ed66ef8e66693b5af7d54f45d1ef5918", size = 1339238, upload-time = "2025-04-04T12:03:07.022Z" }, - { url = "https://files.pythonhosted.org/packages/ee/e4/5aafed4886c264f2ea6064601ad39c5fc4e9b6539c6ebe598a859832eeee/pyzmq-26.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:398a825d2dea96227cf6460ce0a174cf7657d6f6827807d4d1ae9d0f9ae64315", size = 672848, upload-time = "2025-04-04T12:03:08.591Z" }, - { url = "https://files.pythonhosted.org/packages/79/39/026bf49c721cb42f1ef3ae0ee3d348212a7621d2adb739ba97599b6e4d50/pyzmq-26.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d52d62edc96787f5c1dfa6c6ccff9b581cfae5a70d94ec4c8da157656c73b5b", size = 911299, upload-time = "2025-04-04T12:03:10Z" }, - { url = "https://files.pythonhosted.org/packages/03/23/b41f936a9403b8f92325c823c0f264c6102a0687a99c820f1aaeb99c1def/pyzmq-26.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1410c3a3705db68d11eb2424d75894d41cff2f64d948ffe245dd97a9debfebf4", size = 867920, upload-time = "2025-04-04T12:03:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3e/2de5928cdadc2105e7c8f890cc5f404136b41ce5b6eae5902167f1d5641c/pyzmq-26.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7dacb06a9c83b007cc01e8e5277f94c95c453c5851aac5e83efe93e72226353f", size = 862514, upload-time = "2025-04-04T12:03:13.013Z" }, - { url = "https://files.pythonhosted.org/packages/ce/57/109569514dd32e05a61d4382bc88980c95bfd2f02e58fea47ec0ccd96de1/pyzmq-26.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6bab961c8c9b3a4dc94d26e9b2cdf84de9918931d01d6ff38c721a83ab3c0ef5", size = 1204494, upload-time = "2025-04-04T12:03:14.795Z" }, - { url = "https://files.pythonhosted.org/packages/aa/02/dc51068ff2ca70350d1151833643a598625feac7b632372d229ceb4de3e1/pyzmq-26.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a5c09413b924d96af2aa8b57e76b9b0058284d60e2fc3730ce0f979031d162a", size = 1514525, upload-time = "2025-04-04T12:03:16.246Z" }, - { url = "https://files.pythonhosted.org/packages/48/2a/a7d81873fff0645eb60afaec2b7c78a85a377af8f1d911aff045d8955bc7/pyzmq-26.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7d489ac234d38e57f458fdbd12a996bfe990ac028feaf6f3c1e81ff766513d3b", size = 1414659, upload-time = "2025-04-04T12:03:17.652Z" }, - { url = "https://files.pythonhosted.org/packages/ef/ea/813af9c42ae21845c1ccfe495bd29c067622a621e85d7cda6bc437de8101/pyzmq-26.4.0-cp310-cp310-win32.whl", hash = "sha256:dea1c8db78fb1b4b7dc9f8e213d0af3fc8ecd2c51a1d5a3ca1cde1bda034a980", size = 580348, upload-time = "2025-04-04T12:03:19.384Z" }, - { url = "https://files.pythonhosted.org/packages/20/68/318666a89a565252c81d3fed7f3b4c54bd80fd55c6095988dfa2cd04a62b/pyzmq-26.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:fa59e1f5a224b5e04dc6c101d7186058efa68288c2d714aa12d27603ae93318b", size = 643838, upload-time = "2025-04-04T12:03:20.795Z" }, - { url = "https://files.pythonhosted.org/packages/91/f8/fb1a15b5f4ecd3e588bfde40c17d32ed84b735195b5c7d1d7ce88301a16f/pyzmq-26.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:a651fe2f447672f4a815e22e74630b6b1ec3a1ab670c95e5e5e28dcd4e69bbb5", size = 559565, upload-time = "2025-04-04T12:03:22.676Z" }, - { url = "https://files.pythonhosted.org/packages/32/6d/234e3b0aa82fd0290b1896e9992f56bdddf1f97266110be54d0177a9d2d9/pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54", size = 1339723, upload-time = "2025-04-04T12:03:24.358Z" }, - { url = "https://files.pythonhosted.org/packages/4f/11/6d561efe29ad83f7149a7cd48e498e539ed09019c6cd7ecc73f4cc725028/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030", size = 672645, upload-time = "2025-04-04T12:03:25.693Z" }, - { url = "https://files.pythonhosted.org/packages/19/fd/81bfe3e23f418644660bad1a90f0d22f0b3eebe33dd65a79385530bceb3d/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01", size = 910133, upload-time = "2025-04-04T12:03:27.625Z" }, - { url = "https://files.pythonhosted.org/packages/97/68/321b9c775595ea3df832a9516252b653fe32818db66fdc8fa31c9b9fce37/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e", size = 867428, upload-time = "2025-04-04T12:03:29.004Z" }, - { url = "https://files.pythonhosted.org/packages/4e/6e/159cbf2055ef36aa2aa297e01b24523176e5b48ead283c23a94179fb2ba2/pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88", size = 862409, upload-time = "2025-04-04T12:03:31.032Z" }, - { url = "https://files.pythonhosted.org/packages/05/1c/45fb8db7be5a7d0cadea1070a9cbded5199a2d578de2208197e592f219bd/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6", size = 1205007, upload-time = "2025-04-04T12:03:32.687Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fa/658c7f583af6498b463f2fa600f34e298e1b330886f82f1feba0dc2dd6c3/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df", size = 1514599, upload-time = "2025-04-04T12:03:34.084Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d7/44d641522353ce0a2bbd150379cb5ec32f7120944e6bfba4846586945658/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef", size = 1414546, upload-time = "2025-04-04T12:03:35.478Z" }, - { url = "https://files.pythonhosted.org/packages/72/76/c8ed7263218b3d1e9bce07b9058502024188bd52cc0b0a267a9513b431fc/pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca", size = 579247, upload-time = "2025-04-04T12:03:36.846Z" }, - { url = "https://files.pythonhosted.org/packages/c3/d0/2d9abfa2571a0b1a67c0ada79a8aa1ba1cce57992d80f771abcdf99bb32c/pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896", size = 644727, upload-time = "2025-04-04T12:03:38.578Z" }, - { url = "https://files.pythonhosted.org/packages/0d/d1/c8ad82393be6ccedfc3c9f3adb07f8f3976e3c4802640fe3f71441941e70/pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3", size = 559942, upload-time = "2025-04-04T12:03:40.143Z" }, - { url = "https://files.pythonhosted.org/packages/10/44/a778555ebfdf6c7fc00816aad12d185d10a74d975800341b1bc36bad1187/pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b", size = 1341586, upload-time = "2025-04-04T12:03:41.954Z" }, - { url = "https://files.pythonhosted.org/packages/9c/4f/f3a58dc69ac757e5103be3bd41fb78721a5e17da7cc617ddb56d973a365c/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905", size = 665880, upload-time = "2025-04-04T12:03:43.45Z" }, - { url = "https://files.pythonhosted.org/packages/fe/45/50230bcfb3ae5cb98bee683b6edeba1919f2565d7cc1851d3c38e2260795/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b", size = 902216, upload-time = "2025-04-04T12:03:45.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/59/56bbdc5689be5e13727491ad2ba5efd7cd564365750514f9bc8f212eef82/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63", size = 859814, upload-time = "2025-04-04T12:03:47.188Z" }, - { url = "https://files.pythonhosted.org/packages/81/b1/57db58cfc8af592ce94f40649bd1804369c05b2190e4cbc0a2dad572baeb/pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5", size = 855889, upload-time = "2025-04-04T12:03:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/e8/92/47542e629cbac8f221c230a6d0f38dd3d9cff9f6f589ed45fdf572ffd726/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b", size = 1197153, upload-time = "2025-04-04T12:03:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/07/e5/b10a979d1d565d54410afc87499b16c96b4a181af46e7645ab4831b1088c/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84", size = 1507352, upload-time = "2025-04-04T12:03:52.473Z" }, - { url = "https://files.pythonhosted.org/packages/ab/58/5a23db84507ab9c01c04b1232a7a763be66e992aa2e66498521bbbc72a71/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f", size = 1406834, upload-time = "2025-04-04T12:03:54Z" }, - { url = "https://files.pythonhosted.org/packages/22/74/aaa837b331580c13b79ac39396601fb361454ee184ca85e8861914769b99/pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44", size = 577992, upload-time = "2025-04-04T12:03:55.815Z" }, - { url = "https://files.pythonhosted.org/packages/30/0f/55f8c02c182856743b82dde46b2dc3e314edda7f1098c12a8227eeda0833/pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be", size = 640466, upload-time = "2025-04-04T12:03:57.231Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/073779afc3ef6f830b8de95026ef20b2d1ec22d0324d767748d806e57379/pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0", size = 556342, upload-time = "2025-04-04T12:03:59.218Z" }, - { url = "https://files.pythonhosted.org/packages/d7/20/fb2c92542488db70f833b92893769a569458311a76474bda89dc4264bd18/pyzmq-26.4.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:c43fac689880f5174d6fc864857d1247fe5cfa22b09ed058a344ca92bf5301e3", size = 1339484, upload-time = "2025-04-04T12:04:00.671Z" }, - { url = "https://files.pythonhosted.org/packages/58/29/2f06b9cabda3a6ea2c10f43e67ded3e47fc25c54822e2506dfb8325155d4/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902aca7eba477657c5fb81c808318460328758e8367ecdd1964b6330c73cae43", size = 666106, upload-time = "2025-04-04T12:04:02.366Z" }, - { url = "https://files.pythonhosted.org/packages/77/e4/dcf62bd29e5e190bd21bfccaa4f3386e01bf40d948c239239c2f1e726729/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e48a830bfd152fe17fbdeaf99ac5271aa4122521bf0d275b6b24e52ef35eb6", size = 902056, upload-time = "2025-04-04T12:04:03.919Z" }, - { url = "https://files.pythonhosted.org/packages/1a/cf/b36b3d7aea236087d20189bec1a87eeb2b66009731d7055e5c65f845cdba/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31be2b6de98c824c06f5574331f805707c667dc8f60cb18580b7de078479891e", size = 860148, upload-time = "2025-04-04T12:04:05.581Z" }, - { url = "https://files.pythonhosted.org/packages/18/a6/f048826bc87528c208e90604c3bf573801e54bd91e390cbd2dfa860e82dc/pyzmq-26.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6332452034be001bbf3206ac59c0d2a7713de5f25bb38b06519fc6967b7cf771", size = 855983, upload-time = "2025-04-04T12:04:07.096Z" }, - { url = "https://files.pythonhosted.org/packages/0a/27/454d34ab6a1d9772a36add22f17f6b85baf7c16e14325fa29e7202ca8ee8/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:da8c0f5dd352136853e6a09b1b986ee5278dfddfebd30515e16eae425c872b30", size = 1197274, upload-time = "2025-04-04T12:04:08.523Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3d/7abfeab6b83ad38aa34cbd57c6fc29752c391e3954fd12848bd8d2ec0df6/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f4ccc1a0a2c9806dda2a2dd118a3b7b681e448f3bb354056cad44a65169f6d86", size = 1507120, upload-time = "2025-04-04T12:04:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/13/ff/bc8d21dbb9bc8705126e875438a1969c4f77e03fc8565d6901c7933a3d01/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c0b5fceadbab461578daf8d1dcc918ebe7ddd2952f748cf30c7cf2de5d51101", size = 1406738, upload-time = "2025-04-04T12:04:12.509Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5d/d4cd85b24de71d84d81229e3bbb13392b2698432cf8fdcea5afda253d587/pyzmq-26.4.0-cp313-cp313-win32.whl", hash = "sha256:28e2b0ff5ba4b3dd11062d905682bad33385cfa3cc03e81abd7f0822263e6637", size = 577826, upload-time = "2025-04-04T12:04:14.289Z" }, - { url = "https://files.pythonhosted.org/packages/c6/6c/f289c1789d7bb6e5a3b3bef7b2a55089b8561d17132be7d960d3ff33b14e/pyzmq-26.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:23ecc9d241004c10e8b4f49d12ac064cd7000e1643343944a10df98e57bc544b", size = 640406, upload-time = "2025-04-04T12:04:15.757Z" }, - { url = "https://files.pythonhosted.org/packages/b3/99/676b8851cb955eb5236a0c1e9ec679ea5ede092bf8bf2c8a68d7e965cac3/pyzmq-26.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:1edb0385c7f025045d6e0f759d4d3afe43c17a3d898914ec6582e6f464203c08", size = 556216, upload-time = "2025-04-04T12:04:17.212Z" }, - { url = "https://files.pythonhosted.org/packages/65/c2/1fac340de9d7df71efc59d9c50fc7a635a77b103392d1842898dd023afcb/pyzmq-26.4.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:93a29e882b2ba1db86ba5dd5e88e18e0ac6b627026c5cfbec9983422011b82d4", size = 1333769, upload-time = "2025-04-04T12:04:18.665Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c7/6c03637e8d742c3b00bec4f5e4cd9d1c01b2f3694c6f140742e93ca637ed/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45684f276f57110bb89e4300c00f1233ca631f08f5f42528a5c408a79efc4a", size = 658826, upload-time = "2025-04-04T12:04:20.405Z" }, - { url = "https://files.pythonhosted.org/packages/a5/97/a8dca65913c0f78e0545af2bb5078aebfc142ca7d91cdaffa1fbc73e5dbd/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72073e75260cb301aad4258ad6150fa7f57c719b3f498cb91e31df16784d89b", size = 891650, upload-time = "2025-04-04T12:04:22.413Z" }, - { url = "https://files.pythonhosted.org/packages/7d/7e/f63af1031eb060bf02d033732b910fe48548dcfdbe9c785e9f74a6cc6ae4/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be37e24b13026cfedd233bcbbccd8c0bcd2fdd186216094d095f60076201538d", size = 849776, upload-time = "2025-04-04T12:04:23.959Z" }, - { url = "https://files.pythonhosted.org/packages/f6/fa/1a009ce582802a895c0d5fe9413f029c940a0a8ee828657a3bb0acffd88b/pyzmq-26.4.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:237b283044934d26f1eeff4075f751b05d2f3ed42a257fc44386d00df6a270cf", size = 842516, upload-time = "2025-04-04T12:04:25.449Z" }, - { url = "https://files.pythonhosted.org/packages/6e/bc/f88b0bad0f7a7f500547d71e99f10336f2314e525d4ebf576a1ea4a1d903/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b30f862f6768b17040929a68432c8a8be77780317f45a353cb17e423127d250c", size = 1189183, upload-time = "2025-04-04T12:04:27.035Z" }, - { url = "https://files.pythonhosted.org/packages/d9/8c/db446a3dd9cf894406dec2e61eeffaa3c07c3abb783deaebb9812c4af6a5/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:c80fcd3504232f13617c6ab501124d373e4895424e65de8b72042333316f64a8", size = 1495501, upload-time = "2025-04-04T12:04:28.833Z" }, - { url = "https://files.pythonhosted.org/packages/05/4c/bf3cad0d64c3214ac881299c4562b815f05d503bccc513e3fd4fdc6f67e4/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:26a2a7451606b87f67cdeca2c2789d86f605da08b4bd616b1a9981605ca3a364", size = 1395540, upload-time = "2025-04-04T12:04:30.562Z" }, - { url = "https://files.pythonhosted.org/packages/06/91/21d3af57bc77e86e9d1e5384f256fd25cdb4c8eed4c45c8119da8120915f/pyzmq-26.4.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:a88643de8abd000ce99ca72056a1a2ae15881ee365ecb24dd1d9111e43d57842", size = 1340634, upload-time = "2025-04-04T12:04:47.661Z" }, - { url = "https://files.pythonhosted.org/packages/54/e6/58cd825023e998a0e49db7322b3211e6cf93f0796710b77d1496304c10d1/pyzmq-26.4.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a744ce209ecb557406fb928f3c8c55ce79b16c3eeb682da38ef5059a9af0848", size = 907880, upload-time = "2025-04-04T12:04:49.294Z" }, - { url = "https://files.pythonhosted.org/packages/72/83/619e44a766ef738cb7e8ed8e5a54565627801bdb027ca6dfb70762385617/pyzmq-26.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9434540f333332224ecb02ee6278b6c6f11ea1266b48526e73c903119b2f420f", size = 863003, upload-time = "2025-04-04T12:04:51Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6a/a59af31320598bdc63d2c5a3181d14a89673c2c794540678285482e8a342/pyzmq-26.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6c6f0a23e55cd38d27d4c89add963294ea091ebcb104d7fdab0f093bc5abb1c", size = 673432, upload-time = "2025-04-04T12:04:52.611Z" }, - { url = "https://files.pythonhosted.org/packages/29/ae/64dd6c18b08ce2cb009c60f11cf01c87f323acd80344d8b059c0304a7370/pyzmq-26.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6145df55dc2309f6ef72d70576dcd5aabb0fd373311613fe85a5e547c722b780", size = 1205221, upload-time = "2025-04-04T12:04:54.31Z" }, - { url = "https://files.pythonhosted.org/packages/d0/0b/c583ab750957b025244a66948831bc9ca486d11c820da4626caf6480ee1a/pyzmq-26.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2ea81823840ef8c56e5d2f9918e4d571236294fea4d1842b302aebffb9e40997", size = 1515299, upload-time = "2025-04-04T12:04:56.063Z" }, - { url = "https://files.pythonhosted.org/packages/22/ba/95ba76292c49dd9c6dff1f127b4867033020b708d101cba6e4fc5a3d166d/pyzmq-26.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc2abc385dc37835445abe206524fbc0c9e3fce87631dfaa90918a1ba8f425eb", size = 1415366, upload-time = "2025-04-04T12:04:58.241Z" }, - { url = "https://files.pythonhosted.org/packages/6e/65/51abe36169effda26ac7400ffac96f463e09dff40d344cdc2629d9a59162/pyzmq-26.4.0-cp39-cp39-win32.whl", hash = "sha256:41a2508fe7bed4c76b4cf55aacfb8733926f59d440d9ae2b81ee8220633b4d12", size = 580773, upload-time = "2025-04-04T12:04:59.786Z" }, - { url = "https://files.pythonhosted.org/packages/89/68/d9ac94086c63a0ed8d73e9e8aec54b39f481696698a5a939a7207629fb30/pyzmq-26.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:d4000e8255d6cbce38982e5622ebb90823f3409b7ffe8aeae4337ef7d6d2612a", size = 644340, upload-time = "2025-04-04T12:05:01.389Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8f/66c261d657c1b0791ee5b372c90b1646b453adb581fcdc1dc5c94e5b03e3/pyzmq-26.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f6919d9c120488246bdc2a2f96662fa80d67b35bd6d66218f457e722b3ff64", size = 560075, upload-time = "2025-04-04T12:05:02.975Z" }, - { url = "https://files.pythonhosted.org/packages/47/03/96004704a84095f493be8d2b476641f5c967b269390173f85488a53c1c13/pyzmq-26.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:98d948288ce893a2edc5ec3c438fe8de2daa5bbbd6e2e865ec5f966e237084ba", size = 834408, upload-time = "2025-04-04T12:05:04.569Z" }, - { url = "https://files.pythonhosted.org/packages/e4/7f/68d8f3034a20505db7551cb2260248be28ca66d537a1ac9a257913d778e4/pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9f34f5c9e0203ece706a1003f1492a56c06c0632d86cb77bcfe77b56aacf27b", size = 569580, upload-time = "2025-04-04T12:05:06.283Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a6/2b0d6801ec33f2b2a19dd8d02e0a1e8701000fec72926e6787363567d30c/pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80c9b48aef586ff8b698359ce22f9508937c799cc1d2c9c2f7c95996f2300c94", size = 798250, upload-time = "2025-04-04T12:05:07.88Z" }, - { url = "https://files.pythonhosted.org/packages/96/2a/0322b3437de977dcac8a755d6d7ce6ec5238de78e2e2d9353730b297cf12/pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f2a5b74009fd50b53b26f65daff23e9853e79aa86e0aa08a53a7628d92d44a", size = 756758, upload-time = "2025-04-04T12:05:09.483Z" }, - { url = "https://files.pythonhosted.org/packages/c2/33/43704f066369416d65549ccee366cc19153911bec0154da7c6b41fca7e78/pyzmq-26.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:61c5f93d7622d84cb3092d7f6398ffc77654c346545313a3737e266fc11a3beb", size = 555371, upload-time = "2025-04-04T12:05:11.062Z" }, - { url = "https://files.pythonhosted.org/packages/04/52/a70fcd5592715702248306d8e1729c10742c2eac44529984413b05c68658/pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb", size = 834405, upload-time = "2025-04-04T12:05:13.3Z" }, - { url = "https://files.pythonhosted.org/packages/25/f9/1a03f1accff16b3af1a6fa22cbf7ced074776abbf688b2e9cb4629700c62/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1", size = 569578, upload-time = "2025-04-04T12:05:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/76/0c/3a633acd762aa6655fcb71fa841907eae0ab1e8582ff494b137266de341d/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494", size = 798248, upload-time = "2025-04-04T12:05:17.376Z" }, - { url = "https://files.pythonhosted.org/packages/cd/cc/6c99c84aa60ac1cc56747bed6be8ce6305b9b861d7475772e7a25ce019d3/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9", size = 756757, upload-time = "2025-04-04T12:05:19.19Z" }, - { url = "https://files.pythonhosted.org/packages/13/9c/d8073bd898eb896e94c679abe82e47506e2b750eb261cf6010ced869797c/pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0", size = 555371, upload-time = "2025-04-04T12:05:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/af/b2/71a644b629e1a93ccae9e22a45aec9d23065dfcc24c399cb837f81cd08c2/pyzmq-26.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:552b0d2e39987733e1e9e948a0ced6ff75e0ea39ab1a1db2fc36eb60fd8760db", size = 834397, upload-time = "2025-04-04T12:05:31.217Z" }, - { url = "https://files.pythonhosted.org/packages/a9/dd/052a25651eaaff8f5fd652fb40a3abb400e71207db2d605cf6faf0eac598/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd670a8aa843f2ee637039bbd412e0d7294a5e588e1ecc9ad98b0cdc050259a4", size = 569571, upload-time = "2025-04-04T12:05:32.877Z" }, - { url = "https://files.pythonhosted.org/packages/a5/5d/201ca10b5d12ab187a418352c06d70c3e2087310af038b11056aba1359be/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d367b7b775a0e1e54a59a2ba3ed4d5e0a31566af97cc9154e34262777dab95ed", size = 798243, upload-time = "2025-04-04T12:05:34.91Z" }, - { url = "https://files.pythonhosted.org/packages/bd/d4/2c64e54749536ad1633400f28d71e71e19375d00ce1fe9bb1123364dc927/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112af16c406e4a93df2caef49f884f4c2bb2b558b0b5577ef0b2465d15c1abc", size = 756751, upload-time = "2025-04-04T12:05:37.12Z" }, - { url = "https://files.pythonhosted.org/packages/08/e6/34d119af43d06a8dcd88bf7a62dac69597eaba52b49ecce76ff06b40f1fd/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76c298683f82669cab0b6da59071f55238c039738297c69f187a542c6d40099", size = 745400, upload-time = "2025-04-04T12:05:40.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/49/b5e471d74a63318e51f30d329b17d2550bdededaab55baed2e2499de7ce4/pyzmq-26.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:49b6ca2e625b46f499fb081aaf7819a177f41eeb555acb05758aa97f4f95d147", size = 555367, upload-time = "2025-04-04T12:05:42.356Z" }, + { url = "https://files.pythonhosted.org/packages/9c/09/1681d4b047626d352c083770618ac29655ab1f5c20eee31dc94c000b9b7b/pyzmq-27.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b973ee650e8f442ce482c1d99ca7ab537c69098d53a3d046676a484fd710c87a", size = 1329291, upload-time = "2025-06-13T14:06:57.945Z" }, + { url = "https://files.pythonhosted.org/packages/9d/b2/9c9385225fdd54db9506ed8accbb9ea63ca813ba59d43d7f282a6a16a30b/pyzmq-27.0.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:661942bc7cd0223d569d808f2e5696d9cc120acc73bf3e88a1f1be7ab648a7e4", size = 905952, upload-time = "2025-06-13T14:07:03.232Z" }, + { url = "https://files.pythonhosted.org/packages/41/73/333c72c7ec182cdffe25649e3da1c3b9f3cf1cede63cfdc23d1384d4a601/pyzmq-27.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50360fb2a056ffd16e5f4177eee67f1dd1017332ea53fb095fe7b5bf29c70246", size = 666165, upload-time = "2025-06-13T14:07:04.667Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fe/fc7b9c1a50981928e25635a926653cb755364316db59ccd6e79cfb9a0b4f/pyzmq-27.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf209a6dc4b420ed32a7093642843cbf8703ed0a7d86c16c0b98af46762ebefb", size = 853755, upload-time = "2025-06-13T14:07:06.93Z" }, + { url = "https://files.pythonhosted.org/packages/8c/4c/740ed4b6e8fa160cd19dc5abec8db68f440564b2d5b79c1d697d9862a2f7/pyzmq-27.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c2dace4a7041cca2fba5357a2d7c97c5effdf52f63a1ef252cfa496875a3762d", size = 1654868, upload-time = "2025-06-13T14:07:08.224Z" }, + { url = "https://files.pythonhosted.org/packages/97/00/875b2ecfcfc78ab962a59bd384995186818524ea957dc8ad3144611fae12/pyzmq-27.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:63af72b2955fc77caf0a77444baa2431fcabb4370219da38e1a9f8d12aaebe28", size = 2033443, upload-time = "2025-06-13T14:07:09.653Z" }, + { url = "https://files.pythonhosted.org/packages/60/55/6dd9c470c42d713297c5f2a56f7903dc1ebdb4ab2edda996445c21651900/pyzmq-27.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e8c4adce8e37e75c4215297d7745551b8dcfa5f728f23ce09bf4e678a9399413", size = 1891288, upload-time = "2025-06-13T14:07:11.099Z" }, + { url = "https://files.pythonhosted.org/packages/28/5d/54b0ef50d40d7c65a627f4a4b4127024ba9820f2af8acd933a4d30ae192e/pyzmq-27.0.0-cp310-cp310-win32.whl", hash = "sha256:5d5ef4718ecab24f785794e0e7536436698b459bfbc19a1650ef55280119d93b", size = 567936, upload-time = "2025-06-13T14:07:12.468Z" }, + { url = "https://files.pythonhosted.org/packages/18/ea/dedca4321de748ca48d3bcdb72274d4d54e8d84ea49088d3de174bd45d88/pyzmq-27.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:e40609380480b3d12c30f841323f42451c755b8fece84235236f5fe5ffca8c1c", size = 628686, upload-time = "2025-06-13T14:07:14.051Z" }, + { url = "https://files.pythonhosted.org/packages/d4/a7/fcdeedc306e71e94ac262cba2d02337d885f5cdb7e8efced8e5ffe327808/pyzmq-27.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6b0397b0be277b46762956f576e04dc06ced265759e8c2ff41a0ee1aa0064198", size = 559039, upload-time = "2025-06-13T14:07:15.289Z" }, + { url = "https://files.pythonhosted.org/packages/44/df/84c630654106d9bd9339cdb564aa941ed41b023a0264251d6743766bb50e/pyzmq-27.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:21457825249b2a53834fa969c69713f8b5a79583689387a5e7aed880963ac564", size = 1332718, upload-time = "2025-06-13T14:07:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8e/f6a5461a07654d9840d256476434ae0ff08340bba562a455f231969772cb/pyzmq-27.0.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1958947983fef513e6e98eff9cb487b60bf14f588dc0e6bf35fa13751d2c8251", size = 908248, upload-time = "2025-06-13T14:07:18.033Z" }, + { url = "https://files.pythonhosted.org/packages/7c/93/82863e8d695a9a3ae424b63662733ae204a295a2627d52af2f62c2cd8af9/pyzmq-27.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0dc628b5493f9a8cd9844b8bee9732ef587ab00002157c9329e4fc0ef4d3afa", size = 668647, upload-time = "2025-06-13T14:07:19.378Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/15278769b348121eacdbfcbd8c4d40f1102f32fa6af5be1ffc032ed684be/pyzmq-27.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7bbe9e1ed2c8d3da736a15694d87c12493e54cc9dc9790796f0321794bbc91f", size = 856600, upload-time = "2025-06-13T14:07:20.906Z" }, + { url = "https://files.pythonhosted.org/packages/d4/af/1c469b3d479bd095edb28e27f12eee10b8f00b356acbefa6aeb14dd295d1/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dc1091f59143b471d19eb64f54bae4f54bcf2a466ffb66fe45d94d8d734eb495", size = 1657748, upload-time = "2025-06-13T14:07:22.549Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f4/17f965d0ee6380b1d6326da842a50e4b8b9699745161207945f3745e8cb5/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7011ade88c8e535cf140f8d1a59428676fbbce7c6e54fefce58bf117aefb6667", size = 2034311, upload-time = "2025-06-13T14:07:23.966Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6e/7c391d81fa3149fd759de45d298003de6cfab343fb03e92c099821c448db/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c386339d7e3f064213aede5d03d054b237937fbca6dd2197ac8cf3b25a6b14e", size = 1893630, upload-time = "2025-06-13T14:07:25.899Z" }, + { url = "https://files.pythonhosted.org/packages/0e/e0/eaffe7a86f60e556399e224229e7769b717f72fec0706b70ab2c03aa04cb/pyzmq-27.0.0-cp311-cp311-win32.whl", hash = "sha256:0546a720c1f407b2172cb04b6b094a78773491497e3644863cf5c96c42df8cff", size = 567706, upload-time = "2025-06-13T14:07:27.595Z" }, + { url = "https://files.pythonhosted.org/packages/c9/05/89354a8cffdcce6e547d48adaaf7be17007fc75572123ff4ca90a4ca04fc/pyzmq-27.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:15f39d50bd6c9091c67315ceb878a4f531957b121d2a05ebd077eb35ddc5efed", size = 630322, upload-time = "2025-06-13T14:07:28.938Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/4ab976d5e1e63976719389cc4f3bfd248a7f5f2bb2ebe727542363c61b5f/pyzmq-27.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c5817641eebb391a2268c27fecd4162448e03538387093cdbd8bf3510c316b38", size = 558435, upload-time = "2025-06-13T14:07:30.256Z" }, + { url = "https://files.pythonhosted.org/packages/93/a7/9ad68f55b8834ede477842214feba6a4c786d936c022a67625497aacf61d/pyzmq-27.0.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:cbabc59dcfaac66655c040dfcb8118f133fb5dde185e5fc152628354c1598e52", size = 1305438, upload-time = "2025-06-13T14:07:31.676Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ee/26aa0f98665a22bc90ebe12dced1de5f3eaca05363b717f6fb229b3421b3/pyzmq-27.0.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:cb0ac5179cba4b2f94f1aa208fbb77b62c4c9bf24dd446278b8b602cf85fcda3", size = 895095, upload-time = "2025-06-13T14:07:33.104Z" }, + { url = "https://files.pythonhosted.org/packages/cf/85/c57e7ab216ecd8aa4cc7e3b83b06cc4e9cf45c87b0afc095f10cd5ce87c1/pyzmq-27.0.0-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53a48f0228eab6cbf69fde3aa3c03cbe04e50e623ef92ae395fce47ef8a76152", size = 651826, upload-time = "2025-06-13T14:07:34.831Z" }, + { url = "https://files.pythonhosted.org/packages/69/9a/9ea7e230feda9400fb0ae0d61d7d6ddda635e718d941c44eeab22a179d34/pyzmq-27.0.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:111db5f395e09f7e775f759d598f43cb815fc58e0147623c4816486e1a39dc22", size = 839750, upload-time = "2025-06-13T14:07:36.553Z" }, + { url = "https://files.pythonhosted.org/packages/08/66/4cebfbe71f3dfbd417011daca267539f62ed0fbc68105357b68bbb1a25b7/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c8878011653dcdc27cc2c57e04ff96f0471e797f5c19ac3d7813a245bcb24371", size = 1641357, upload-time = "2025-06-13T14:07:38.21Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f6/b0f62578c08d2471c791287149cb8c2aaea414ae98c6e995c7dbe008adfb/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:c0ed2c1f335ba55b5fdc964622254917d6b782311c50e138863eda409fbb3b6d", size = 2020281, upload-time = "2025-06-13T14:07:39.599Z" }, + { url = "https://files.pythonhosted.org/packages/37/b9/4f670b15c7498495da9159edc374ec09c88a86d9cd5a47d892f69df23450/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e918d70862d4cfd4b1c187310015646a14e1f5917922ab45b29f28f345eeb6be", size = 1877110, upload-time = "2025-06-13T14:07:41.027Z" }, + { url = "https://files.pythonhosted.org/packages/66/31/9dee25c226295b740609f0d46db2fe972b23b6f5cf786360980524a3ba92/pyzmq-27.0.0-cp312-abi3-win32.whl", hash = "sha256:88b4e43cab04c3c0f0d55df3b1eef62df2b629a1a369b5289a58f6fa8b07c4f4", size = 559297, upload-time = "2025-06-13T14:07:42.533Z" }, + { url = "https://files.pythonhosted.org/packages/9b/12/52da5509800f7ff2d287b2f2b4e636e7ea0f001181cba6964ff6c1537778/pyzmq-27.0.0-cp312-abi3-win_amd64.whl", hash = "sha256:dce4199bf5f648a902ce37e7b3afa286f305cd2ef7a8b6ec907470ccb6c8b371", size = 619203, upload-time = "2025-06-13T14:07:43.843Z" }, + { url = "https://files.pythonhosted.org/packages/93/6d/7f2e53b19d1edb1eb4f09ec7c3a1f945ca0aac272099eab757d15699202b/pyzmq-27.0.0-cp312-abi3-win_arm64.whl", hash = "sha256:56e46bbb85d52c1072b3f809cc1ce77251d560bc036d3a312b96db1afe76db2e", size = 551927, upload-time = "2025-06-13T14:07:45.51Z" }, + { url = "https://files.pythonhosted.org/packages/19/62/876b27c4ff777db4ceba1c69ea90d3c825bb4f8d5e7cd987ce5802e33c55/pyzmq-27.0.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c36ad534c0c29b4afa088dc53543c525b23c0797e01b69fef59b1a9c0e38b688", size = 1340826, upload-time = "2025-06-13T14:07:46.881Z" }, + { url = "https://files.pythonhosted.org/packages/43/69/58ef8f4f59d3bcd505260c73bee87b008850f45edca40ddaba54273c35f4/pyzmq-27.0.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:67855c14173aec36395d7777aaba3cc527b393821f30143fd20b98e1ff31fd38", size = 897283, upload-time = "2025-06-13T14:07:49.562Z" }, + { url = "https://files.pythonhosted.org/packages/43/15/93a0d0396700a60475ad3c5d42c5f1c308d3570bc94626b86c71ef9953e0/pyzmq-27.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8617c7d43cd8ccdb62aebe984bfed77ca8f036e6c3e46dd3dddda64b10f0ab7a", size = 660567, upload-time = "2025-06-13T14:07:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b3/fe055513e498ca32f64509abae19b9c9eb4d7c829e02bd8997dd51b029eb/pyzmq-27.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:67bfbcbd0a04c575e8103a6061d03e393d9f80ffdb9beb3189261e9e9bc5d5e9", size = 847681, upload-time = "2025-06-13T14:07:52.77Z" }, + { url = "https://files.pythonhosted.org/packages/b6/4f/ff15300b00b5b602191f3df06bbc8dd4164e805fdd65bb77ffbb9c5facdc/pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5cd11d46d7b7e5958121b3eaf4cd8638eff3a720ec527692132f05a57f14341d", size = 1650148, upload-time = "2025-06-13T14:07:54.178Z" }, + { url = "https://files.pythonhosted.org/packages/c4/6f/84bdfff2a224a6f26a24249a342e5906993c50b0761e311e81b39aef52a7/pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:b801c2e40c5aa6072c2f4876de8dccd100af6d9918d4d0d7aa54a1d982fd4f44", size = 2023768, upload-time = "2025-06-13T14:07:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/64/39/dc2db178c26a42228c5ac94a9cc595030458aa64c8d796a7727947afbf55/pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20d5cb29e8c5f76a127c75b6e7a77e846bc4b655c373baa098c26a61b7ecd0ef", size = 1885199, upload-time = "2025-06-13T14:07:57.166Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/dae7b06a1f8cdee5d8e7a63d99c5d129c401acc40410bef2cbf42025e26f/pyzmq-27.0.0-cp313-cp313t-win32.whl", hash = "sha256:a20528da85c7ac7a19b7384e8c3f8fa707841fd85afc4ed56eda59d93e3d98ad", size = 575439, upload-time = "2025-06-13T14:07:58.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/bc/1709dc55f0970cf4cb8259e435e6773f9946f41a045c2cb90e870b7072da/pyzmq-27.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d8229f2efece6a660ee211d74d91dbc2a76b95544d46c74c615e491900dc107f", size = 639933, upload-time = "2025-06-13T14:08:00.777Z" }, + { url = "https://files.pythonhosted.org/packages/19/dc/95210fe17e5d7dba89bd663e1d88f50a8003f296284731b09f1d95309a42/pyzmq-27.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:100f6e5052ba42b2533011d34a018a5ace34f8cac67cb03cfa37c8bdae0ca617", size = 1330656, upload-time = "2025-06-13T14:08:17.414Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7e/63f742b578316258e03ecb393d35c0964348d80834bdec8a100ed7bb9c91/pyzmq-27.0.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:bf6c6b061efd00404b9750e2cfbd9507492c8d4b3721ded76cb03786131be2ed", size = 906522, upload-time = "2025-06-13T14:08:18.945Z" }, + { url = "https://files.pythonhosted.org/packages/1f/bf/f0b2b67f5a9bfe0fbd0e978a2becd901f802306aa8e29161cb0963094352/pyzmq-27.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee05728c0b0b2484a9fc20466fa776fffb65d95f7317a3419985b8c908563861", size = 863545, upload-time = "2025-06-13T14:08:20.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/0e/7d90ccd2ef577c8bae7f926acd2011a6d960eea8a068c5fd52b419206960/pyzmq-27.0.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7cdf07fe0a557b131366f80727ec8ccc4b70d89f1e3f920d94a594d598d754f0", size = 666796, upload-time = "2025-06-13T14:08:21.836Z" }, + { url = "https://files.pythonhosted.org/packages/4f/6d/ca8007a313baa73361778773aef210f4902e68f468d1f93b6c8b908fabbd/pyzmq-27.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:90252fa2ff3a104219db1f5ced7032a7b5fc82d7c8d2fec2b9a3e6fd4e25576b", size = 1655599, upload-time = "2025-06-13T14:08:23.343Z" }, + { url = "https://files.pythonhosted.org/packages/46/de/5cb4f99d6c0dd8f33d729c9ebd49af279586e5ab127e93aa6ef0ecd08c4c/pyzmq-27.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ea6d441c513bf18c578c73c323acf7b4184507fc244762193aa3a871333c9045", size = 2034119, upload-time = "2025-06-13T14:08:26.369Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8d/57cc90c8b5f30a97a7e86ec91a3b9822ec7859d477e9c30f531fb78f4a97/pyzmq-27.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ae2b34bcfaae20c064948a4113bf8709eee89fd08317eb293ae4ebd69b4d9740", size = 1891955, upload-time = "2025-06-13T14:08:28.39Z" }, + { url = "https://files.pythonhosted.org/packages/24/f5/a7012022573188903802ab75b5314b00e5c629228f3a36fadb421a42ebff/pyzmq-27.0.0-cp39-cp39-win32.whl", hash = "sha256:5b10bd6f008937705cf6e7bf8b6ece5ca055991e3eb130bca8023e20b86aa9a3", size = 568497, upload-time = "2025-06-13T14:08:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f3/2a4b2798275a574801221d94d599ed3e26d19f6378a7364cdfa3bee53944/pyzmq-27.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:00387d12a8af4b24883895f7e6b9495dc20a66027b696536edac35cb988c38f3", size = 629315, upload-time = "2025-06-13T14:08:31.877Z" }, + { url = "https://files.pythonhosted.org/packages/da/eb/386a70314f305816142d6e8537f5557e5fd9614c03698d6c88cbd6c41190/pyzmq-27.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:4c19d39c04c29a6619adfeb19e3735c421b3bfee082f320662f52e59c47202ba", size = 559596, upload-time = "2025-06-13T14:08:33.357Z" }, + { url = "https://files.pythonhosted.org/packages/09/6f/be6523a7f3821c0b5370912ef02822c028611360e0d206dd945bdbf9eaef/pyzmq-27.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:656c1866505a5735d0660b7da6d7147174bbf59d4975fc2b7f09f43c9bc25745", size = 835950, upload-time = "2025-06-13T14:08:35Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1e/a50fdd5c15018de07ab82a61bc460841be967ee7bbe7abee3b714d66f7ac/pyzmq-27.0.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74175b9e12779382432dd1d1f5960ebe7465d36649b98a06c6b26be24d173fab", size = 799876, upload-time = "2025-06-13T14:08:36.849Z" }, + { url = "https://files.pythonhosted.org/packages/88/a1/89eb5b71f5a504f8f887aceb8e1eb3626e00c00aa8085381cdff475440dc/pyzmq-27.0.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8c6de908465697a8708e4d6843a1e884f567962fc61eb1706856545141d0cbb", size = 567400, upload-time = "2025-06-13T14:08:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/56/aa/4571dbcff56cfb034bac73fde8294e123c975ce3eea89aff31bf6dc6382b/pyzmq-27.0.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c644aaacc01d0df5c7072826df45e67301f191c55f68d7b2916d83a9ddc1b551", size = 747031, upload-time = "2025-06-13T14:08:40.413Z" }, + { url = "https://files.pythonhosted.org/packages/46/e0/d25f30fe0991293c5b2f5ef3b070d35fa6d57c0c7428898c3ab4913d0297/pyzmq-27.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:10f70c1d9a446a85013a36871a296007f6fe4232b530aa254baf9da3f8328bc0", size = 544726, upload-time = "2025-06-13T14:08:41.997Z" }, + { url = "https://files.pythonhosted.org/packages/98/a6/92394373b8dbc1edc9d53c951e8d3989d518185174ee54492ec27711779d/pyzmq-27.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd1dc59763effd1576f8368047c9c31468fce0af89d76b5067641137506792ae", size = 835948, upload-time = "2025-06-13T14:08:43.516Z" }, + { url = "https://files.pythonhosted.org/packages/56/f3/4dc38d75d9995bfc18773df3e41f2a2ca9b740b06f1a15dbf404077e7588/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:60e8cc82d968174650c1860d7b716366caab9973787a1c060cf8043130f7d0f7", size = 799874, upload-time = "2025-06-13T14:08:45.017Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ba/64af397e0f421453dc68e31d5e0784d554bf39013a2de0872056e96e58af/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14fe7aaac86e4e93ea779a821967360c781d7ac5115b3f1a171ced77065a0174", size = 567400, upload-time = "2025-06-13T14:08:46.855Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/ec956cbe98809270b59a22891d5758edae147a258e658bf3024a8254c855/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ad0562d4e6abb785be3e4dd68599c41be821b521da38c402bc9ab2a8e7ebc7e", size = 747031, upload-time = "2025-06-13T14:08:48.419Z" }, + { url = "https://files.pythonhosted.org/packages/be/8a/4a3764a68abc02e2fbb0668d225b6fda5cd39586dd099cee8b2ed6ab0452/pyzmq-27.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:9df43a2459cd3a3563404c1456b2c4c69564daa7dbaf15724c09821a3329ce46", size = 544726, upload-time = "2025-06-13T14:08:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/03/f6/11b2a6c8cd13275c31cddc3f89981a1b799a3c41dec55289fa18dede96b5/pyzmq-27.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:39ddd3ba0a641f01d8f13a3cfd4c4924eb58e660d8afe87e9061d6e8ca6f7ac3", size = 835944, upload-time = "2025-06-13T14:08:59.189Z" }, + { url = "https://files.pythonhosted.org/packages/73/34/aa39076f4e07ae1912fa4b966fe24e831e01d736d4c1c7e8a3aa28a555b5/pyzmq-27.0.0-pp39-pypy39_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8ca7e6a0388dd9e1180b14728051068f4efe83e0d2de058b5ff92c63f399a73f", size = 799869, upload-time = "2025-06-13T14:09:00.758Z" }, + { url = "https://files.pythonhosted.org/packages/65/f3/81ed6b3dd242408ee79c0d8a88734644acf208baee8666ecd7e52664cf55/pyzmq-27.0.0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2524c40891be6a3106885a3935d58452dd83eb7a5742a33cc780a1ad4c49dec0", size = 758371, upload-time = "2025-06-13T14:09:02.461Z" }, + { url = "https://files.pythonhosted.org/packages/e1/04/dac4ca674764281caf744e8adefd88f7e325e1605aba0f9a322094b903fa/pyzmq-27.0.0-pp39-pypy39_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a56e3e5bd2d62a01744fd2f1ce21d760c7c65f030e9522738d75932a14ab62a", size = 567393, upload-time = "2025-06-13T14:09:04.037Z" }, + { url = "https://files.pythonhosted.org/packages/51/8b/619a9ee2fa4d3c724fbadde946427735ade64da03894b071bbdc3b789d83/pyzmq-27.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:096af9e133fec3a72108ddefba1e42985cb3639e9de52cfd336b6fc23aa083e9", size = 544715, upload-time = "2025-06-13T14:09:05.579Z" }, ] [[package]] @@ -2619,7 +2602,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -2627,9 +2610,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, ] [[package]] @@ -2791,27 +2774,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.11.12" +version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/0a/92416b159ec00cdf11e5882a9d80d29bf84bba3dbebc51c4898bfbca1da6/ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603", size = 4202289, upload-time = "2025-05-29T13:31:40.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/38/796a101608a90494440856ccfb52b1edae90de0b817e76bfade66b12d320/ruff-0.12.1.tar.gz", hash = "sha256:806bbc17f1104fd57451a98a58df35388ee3ab422e029e8f5cf30aa4af2c138c", size = 4413426, upload-time = "2025-06-26T20:34:14.784Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/cc/53eb79f012d15e136d40a8e8fc519ba8f55a057f60b29c2df34efd47c6e3/ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc", size = 10285597, upload-time = "2025-05-29T13:30:57.539Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d7/73386e9fb0232b015a23f62fea7503f96e29c29e6c45461d4a73bac74df9/ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3", size = 11053154, upload-time = "2025-05-29T13:31:00.865Z" }, - { url = "https://files.pythonhosted.org/packages/4e/eb/3eae144c5114e92deb65a0cb2c72326c8469e14991e9bc3ec0349da1331c/ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa", size = 10403048, upload-time = "2025-05-29T13:31:03.413Z" }, - { url = "https://files.pythonhosted.org/packages/29/64/20c54b20e58b1058db6689e94731f2a22e9f7abab74e1a758dfba058b6ca/ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012", size = 10597062, upload-time = "2025-05-29T13:31:05.539Z" }, - { url = "https://files.pythonhosted.org/packages/29/3a/79fa6a9a39422a400564ca7233a689a151f1039110f0bbbabcb38106883a/ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a", size = 10155152, upload-time = "2025-05-29T13:31:07.986Z" }, - { url = "https://files.pythonhosted.org/packages/e5/a4/22c2c97b2340aa968af3a39bc38045e78d36abd4ed3fa2bde91c31e712e3/ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7", size = 11723067, upload-time = "2025-05-29T13:31:10.57Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cf/3e452fbd9597bcd8058856ecd42b22751749d07935793a1856d988154151/ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a", size = 12460807, upload-time = "2025-05-29T13:31:12.88Z" }, - { url = "https://files.pythonhosted.org/packages/2f/ec/8f170381a15e1eb7d93cb4feef8d17334d5a1eb33fee273aee5d1f8241a3/ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13", size = 12063261, upload-time = "2025-05-29T13:31:15.236Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/57208f8c0a8153a14652a85f4116c0002148e83770d7a41f2e90b52d2b4e/ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be", size = 11329601, upload-time = "2025-05-29T13:31:18.68Z" }, - { url = "https://files.pythonhosted.org/packages/c3/56/edf942f7fdac5888094d9ffa303f12096f1a93eb46570bcf5f14c0c70880/ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd", size = 11522186, upload-time = "2025-05-29T13:31:21.216Z" }, - { url = "https://files.pythonhosted.org/packages/ed/63/79ffef65246911ed7e2290aeece48739d9603b3a35f9529fec0fc6c26400/ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef", size = 10449032, upload-time = "2025-05-29T13:31:23.417Z" }, - { url = "https://files.pythonhosted.org/packages/88/19/8c9d4d8a1c2a3f5a1ea45a64b42593d50e28b8e038f1aafd65d6b43647f3/ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5", size = 10129370, upload-time = "2025-05-29T13:31:25.777Z" }, - { url = "https://files.pythonhosted.org/packages/bc/0f/2d15533eaa18f460530a857e1778900cd867ded67f16c85723569d54e410/ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02", size = 11123529, upload-time = "2025-05-29T13:31:28.396Z" }, - { url = "https://files.pythonhosted.org/packages/4f/e2/4c2ac669534bdded835356813f48ea33cfb3a947dc47f270038364587088/ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c", size = 11577642, upload-time = "2025-05-29T13:31:30.647Z" }, - { url = "https://files.pythonhosted.org/packages/a7/9b/c9ddf7f924d5617a1c94a93ba595f4b24cb5bc50e98b94433ab3f7ad27e5/ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6", size = 10475511, upload-time = "2025-05-29T13:31:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d6/74fb6d3470c1aada019ffff33c0f9210af746cca0a4de19a1f10ce54968a/ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832", size = 11523573, upload-time = "2025-05-29T13:31:35.782Z" }, - { url = "https://files.pythonhosted.org/packages/44/42/d58086ec20f52d2b0140752ae54b355ea2be2ed46f914231136dd1effcc7/ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5", size = 10697770, upload-time = "2025-05-29T13:31:38.009Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/3dba52c1d12ab5e78d75bd78ad52fb85a6a1f29cc447c2423037b82bed0d/ruff-0.12.1-py3-none-linux_armv6l.whl", hash = "sha256:6013a46d865111e2edb71ad692fbb8262e6c172587a57c0669332a449384a36b", size = 10305649, upload-time = "2025-06-26T20:33:39.242Z" }, + { url = "https://files.pythonhosted.org/packages/8c/65/dab1ba90269bc8c81ce1d499a6517e28fe6f87b2119ec449257d0983cceb/ruff-0.12.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b3f75a19e03a4b0757d1412edb7f27cffb0c700365e9d6b60bc1b68d35bc89e0", size = 11120201, upload-time = "2025-06-26T20:33:42.207Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3e/2d819ffda01defe857fa2dd4cba4d19109713df4034cc36f06bbf582d62a/ruff-0.12.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9a256522893cb7e92bb1e1153283927f842dea2e48619c803243dccc8437b8be", size = 10466769, upload-time = "2025-06-26T20:33:44.102Z" }, + { url = "https://files.pythonhosted.org/packages/63/37/bde4cf84dbd7821c8de56ec4ccc2816bce8125684f7b9e22fe4ad92364de/ruff-0.12.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:069052605fe74c765a5b4272eb89880e0ff7a31e6c0dbf8767203c1fbd31c7ff", size = 10660902, upload-time = "2025-06-26T20:33:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3a/390782a9ed1358c95e78ccc745eed1a9d657a537e5c4c4812fce06c8d1a0/ruff-0.12.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a684f125a4fec2d5a6501a466be3841113ba6847827be4573fddf8308b83477d", size = 10167002, upload-time = "2025-06-26T20:33:47.81Z" }, + { url = "https://files.pythonhosted.org/packages/6d/05/f2d4c965009634830e97ffe733201ec59e4addc5b1c0efa035645baa9e5f/ruff-0.12.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdecdef753bf1e95797593007569d8e1697a54fca843d78f6862f7dc279e23bd", size = 11751522, upload-time = "2025-06-26T20:33:49.857Z" }, + { url = "https://files.pythonhosted.org/packages/35/4e/4bfc519b5fcd462233f82fc20ef8b1e5ecce476c283b355af92c0935d5d9/ruff-0.12.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:70d52a058c0e7b88b602f575d23596e89bd7d8196437a4148381a3f73fcd5010", size = 12520264, upload-time = "2025-06-26T20:33:52.199Z" }, + { url = "https://files.pythonhosted.org/packages/85/b2/7756a6925da236b3a31f234b4167397c3e5f91edb861028a631546bad719/ruff-0.12.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84d0a69d1e8d716dfeab22d8d5e7c786b73f2106429a933cee51d7b09f861d4e", size = 12133882, upload-time = "2025-06-26T20:33:54.231Z" }, + { url = "https://files.pythonhosted.org/packages/dd/00/40da9c66d4a4d51291e619be6757fa65c91b92456ff4f01101593f3a1170/ruff-0.12.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cc32e863adcf9e71690248607ccdf25252eeeab5193768e6873b901fd441fed", size = 11608941, upload-time = "2025-06-26T20:33:56.202Z" }, + { url = "https://files.pythonhosted.org/packages/91/e7/f898391cc026a77fbe68dfea5940f8213622474cb848eb30215538a2dadf/ruff-0.12.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fd49a4619f90d5afc65cf42e07b6ae98bb454fd5029d03b306bd9e2273d44cc", size = 11602887, upload-time = "2025-06-26T20:33:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/f6/02/0891872fc6aab8678084f4cf8826f85c5d2d24aa9114092139a38123f94b/ruff-0.12.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ed5af6aaaea20710e77698e2055b9ff9b3494891e1b24d26c07055459bb717e9", size = 10521742, upload-time = "2025-06-26T20:34:00.465Z" }, + { url = "https://files.pythonhosted.org/packages/2a/98/d6534322c74a7d47b0f33b036b2498ccac99d8d8c40edadb552c038cecf1/ruff-0.12.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:801d626de15e6bf988fbe7ce59b303a914ff9c616d5866f8c79eb5012720ae13", size = 10149909, upload-time = "2025-06-26T20:34:02.603Z" }, + { url = "https://files.pythonhosted.org/packages/34/5c/9b7ba8c19a31e2b6bd5e31aa1e65b533208a30512f118805371dbbbdf6a9/ruff-0.12.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2be9d32a147f98a1972c1e4df9a6956d612ca5f5578536814372113d09a27a6c", size = 11136005, upload-time = "2025-06-26T20:34:04.723Z" }, + { url = "https://files.pythonhosted.org/packages/dc/34/9bbefa4d0ff2c000e4e533f591499f6b834346025e11da97f4ded21cb23e/ruff-0.12.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:49b7ce354eed2a322fbaea80168c902de9504e6e174fd501e9447cad0232f9e6", size = 11648579, upload-time = "2025-06-26T20:34:06.766Z" }, + { url = "https://files.pythonhosted.org/packages/6f/1c/20cdb593783f8f411839ce749ec9ae9e4298c2b2079b40295c3e6e2089e1/ruff-0.12.1-py3-none-win32.whl", hash = "sha256:d973fa626d4c8267848755bd0414211a456e99e125dcab147f24daa9e991a245", size = 10519495, upload-time = "2025-06-26T20:34:08.718Z" }, + { url = "https://files.pythonhosted.org/packages/cf/56/7158bd8d3cf16394928f47c637d39a7d532268cd45220bdb6cd622985760/ruff-0.12.1-py3-none-win_amd64.whl", hash = "sha256:9e1123b1c033f77bd2590e4c1fe7e8ea72ef990a85d2484351d408224d603013", size = 11547485, upload-time = "2025-06-26T20:34:11.008Z" }, + { url = "https://files.pythonhosted.org/packages/91/d0/6902c0d017259439d6fd2fd9393cea1cfe30169940118b007d5e0ea7e954/ruff-0.12.1-py3-none-win_arm64.whl", hash = "sha256:78ad09a022c64c13cc6077707f036bab0fac8cd7088772dcd1e5be21c5002efc", size = 10691209, upload-time = "2025-06-26T20:34:12.928Z" }, ] [[package]] @@ -2876,9 +2859,9 @@ name = "sse-starlette" version = "2.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "starlette", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "uvicorn", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "anyio", marker = "python_full_version >= '3.11'" }, + { name = "starlette", marker = "python_full_version >= '3.11'" }, + { name = "uvicorn", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/72/fc/56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496/sse_starlette-2.1.3.tar.gz", hash = "sha256:9cd27eb35319e1414e3d2558ee7414487f9529ce3b3cf9b21434fd110e017169", size = 19678, upload-time = "2024-08-01T08:52:50.248Z" } wheels = [ @@ -2901,23 +2884,24 @@ wheels = [ [[package]] name = "starlette" -version = "0.47.0" +version = "0.47.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "anyio", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/d0/0332bd8a25779a0e2082b0e179805ad39afad642938b371ae0882e7f880d/starlette-0.47.0.tar.gz", hash = "sha256:1f64887e94a447fed5f23309fb6890ef23349b7e478faa7b24a851cd4eb844af", size = 2582856, upload-time = "2025-05-29T15:45:27.628Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/69/662169fdb92fb96ec3eaee218cf540a629d629c86d7993d9651226a6789b/starlette-0.47.1.tar.gz", hash = "sha256:aef012dd2b6be325ffa16698f9dc533614fb1cebd593a906b90dc1025529a79b", size = 2583072, upload-time = "2025-06-21T04:03:17.337Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/81/c60b35fe9674f63b38a8feafc414fca0da378a9dbd5fa1e0b8d23fcc7a9b/starlette-0.47.0-py3-none-any.whl", hash = "sha256:9d052d4933683af40ffd47c7465433570b4949dc937e20ad1d73b34e72f10c37", size = 72796, upload-time = "2025-05-29T15:45:26.305Z" }, + { url = "https://files.pythonhosted.org/packages/82/95/38ef0cd7fa11eaba6a99b3c4f5ac948d8bc6ff199aabd327a29cc000840c/starlette-0.47.1-py3-none-any.whl", hash = "sha256:5e11c9f5c7c3f24959edbf2dffdc01bba860228acf657129467d8a7468591527", size = 72747, upload-time = "2025-06-21T04:03:15.705Z" }, ] [[package]] name = "structlog" -version = "25.3.0" +version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/6a/b0b6d440e429d2267076c4819300d9929563b1da959cf1f68afbcd69fe45/structlog-25.3.0.tar.gz", hash = "sha256:8dab497e6f6ca962abad0c283c46744185e0c9ba900db52a423cb6db99f7abeb", size = 1367514, upload-time = "2025-04-25T16:00:39.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/6e672db4fec07349e7a8a8172c1a6ae235c58679ca29c3f86a61b5e59ff3/structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4", size = 1369138, upload-time = "2025-06-02T08:21:12.971Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/52/7a2c7a317b254af857464da3d60a0d3730c44f912f8c510c76a738a207fd/structlog-25.3.0-py3-none-any.whl", hash = "sha256:a341f5524004c158498c3127eecded091eb67d3a611e7a3093deca30db06e172", size = 68240, upload-time = "2025-04-25T16:00:37.295Z" }, + { url = "https://files.pythonhosted.org/packages/a0/4a/97ee6973e3a73c74c8120d59829c3861ea52210667ec3e7a16045c62b64d/structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c", size = 68720, upload-time = "2025-06-02T08:21:11.43Z" }, ] [[package]] @@ -3054,23 +3038,23 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.0.20250515" +version = "2.32.4.20250611" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/c1/cdc4f9b8cfd9130fbe6276db574f114541f4231fcc6fb29648289e6e3390/types_requests-2.32.0.20250515.tar.gz", hash = "sha256:09c8b63c11318cb2460813871aaa48b671002e59fda67ca909e9883777787581", size = 23012, upload-time = "2025-05-15T03:04:31.817Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/0f/68a997c73a129287785f418c1ebb6004f81e46b53b3caba88c0e03fcd04a/types_requests-2.32.0.20250515-py3-none-any.whl", hash = "sha256:f8eba93b3a892beee32643ff836993f15a785816acca21ea0ffa006f05ef0fb2", size = 20635, upload-time = "2025-05-15T03:04:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, ] [[package]] name = "typing-extensions" -version = "4.13.2" +version = "4.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, ] [[package]] @@ -3093,24 +3077,24 @@ wheels = [ [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, ] [[package]] name = "uvicorn" -version = "0.34.2" +version = "0.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "h11", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "h11", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, ] [[package]] @@ -3190,83 +3174,118 @@ wheels = [ [[package]] name = "watchfiles" -version = "1.0.5" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "anyio", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/e2/8ed598c42057de7aa5d97c472254af4906ff0a59a66699d426fc9ef795d7/watchfiles-1.0.5.tar.gz", hash = "sha256:b7529b5dcc114679d43827d8c35a07c493ad6f083633d573d81c660abc5979e9", size = 94537, upload-time = "2025-04-08T10:36:26.722Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/4d/d02e6ea147bb7fff5fd109c694a95109612f419abed46548a930e7f7afa3/watchfiles-1.0.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5c40fe7dd9e5f81e0847b1ea64e1f5dd79dd61afbedb57759df06767ac719b40", size = 405632, upload-time = "2025-04-08T10:34:41.832Z" }, - { url = "https://files.pythonhosted.org/packages/60/31/9ee50e29129d53a9a92ccf1d3992751dc56fc3c8f6ee721be1c7b9c81763/watchfiles-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0db396e6003d99bb2d7232c957b5f0b5634bbd1b24e381a5afcc880f7373fb", size = 395734, upload-time = "2025-04-08T10:34:44.236Z" }, - { url = "https://files.pythonhosted.org/packages/ad/8c/759176c97195306f028024f878e7f1c776bda66ccc5c68fa51e699cf8f1d/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b551d4fb482fc57d852b4541f911ba28957d051c8776e79c3b4a51eb5e2a1b11", size = 455008, upload-time = "2025-04-08T10:34:45.617Z" }, - { url = "https://files.pythonhosted.org/packages/55/1a/5e977250c795ee79a0229e3b7f5e3a1b664e4e450756a22da84d2f4979fe/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:830aa432ba5c491d52a15b51526c29e4a4b92bf4f92253787f9726fe01519487", size = 459029, upload-time = "2025-04-08T10:34:46.814Z" }, - { url = "https://files.pythonhosted.org/packages/e6/17/884cf039333605c1d6e296cf5be35fad0836953c3dfd2adb71b72f9dbcd0/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a16512051a822a416b0d477d5f8c0e67b67c1a20d9acecb0aafa3aa4d6e7d256", size = 488916, upload-time = "2025-04-08T10:34:48.571Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e0/bcb6e64b45837056c0a40f3a2db3ef51c2ced19fda38484fa7508e00632c/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe0cbc787770e52a96c6fda6726ace75be7f840cb327e1b08d7d54eadc3bc85", size = 523763, upload-time = "2025-04-08T10:34:50.268Z" }, - { url = "https://files.pythonhosted.org/packages/24/e9/f67e9199f3bb35c1837447ecf07e9830ec00ff5d35a61e08c2cd67217949/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d363152c5e16b29d66cbde8fa614f9e313e6f94a8204eaab268db52231fe5358", size = 502891, upload-time = "2025-04-08T10:34:51.419Z" }, - { url = "https://files.pythonhosted.org/packages/23/ed/a6cf815f215632f5c8065e9c41fe872025ffea35aa1f80499f86eae922db/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee32c9a9bee4d0b7bd7cbeb53cb185cf0b622ac761efaa2eba84006c3b3a614", size = 454921, upload-time = "2025-04-08T10:34:52.67Z" }, - { url = "https://files.pythonhosted.org/packages/92/4c/e14978599b80cde8486ab5a77a821e8a982ae8e2fcb22af7b0886a033ec8/watchfiles-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29c7fd632ccaf5517c16a5188e36f6612d6472ccf55382db6c7fe3fcccb7f59f", size = 631422, upload-time = "2025-04-08T10:34:53.985Z" }, - { url = "https://files.pythonhosted.org/packages/b2/1a/9263e34c3458f7614b657f974f4ee61fd72f58adce8b436e16450e054efd/watchfiles-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e637810586e6fe380c8bc1b3910accd7f1d3a9a7262c8a78d4c8fb3ba6a2b3d", size = 625675, upload-time = "2025-04-08T10:34:55.173Z" }, - { url = "https://files.pythonhosted.org/packages/96/1f/1803a18bd6ab04a0766386a19bcfe64641381a04939efdaa95f0e3b0eb58/watchfiles-1.0.5-cp310-cp310-win32.whl", hash = "sha256:cd47d063fbeabd4c6cae1d4bcaa38f0902f8dc5ed168072874ea11d0c7afc1ff", size = 277921, upload-time = "2025-04-08T10:34:56.318Z" }, - { url = "https://files.pythonhosted.org/packages/c2/3b/29a89de074a7d6e8b4dc67c26e03d73313e4ecf0d6e97e942a65fa7c195e/watchfiles-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:86c0df05b47a79d80351cd179893f2f9c1b1cae49d96e8b3290c7f4bd0ca0a92", size = 291526, upload-time = "2025-04-08T10:34:57.95Z" }, - { url = "https://files.pythonhosted.org/packages/39/f4/41b591f59021786ef517e1cdc3b510383551846703e03f204827854a96f8/watchfiles-1.0.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:237f9be419e977a0f8f6b2e7b0475ababe78ff1ab06822df95d914a945eac827", size = 405336, upload-time = "2025-04-08T10:34:59.359Z" }, - { url = "https://files.pythonhosted.org/packages/ae/06/93789c135be4d6d0e4f63e96eea56dc54050b243eacc28439a26482b5235/watchfiles-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0da39ff917af8b27a4bdc5a97ac577552a38aac0d260a859c1517ea3dc1a7c4", size = 395977, upload-time = "2025-04-08T10:35:00.522Z" }, - { url = "https://files.pythonhosted.org/packages/d2/db/1cd89bd83728ca37054512d4d35ab69b5f12b8aa2ac9be3b0276b3bf06cc/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cfcb3952350e95603f232a7a15f6c5f86c5375e46f0bd4ae70d43e3e063c13d", size = 455232, upload-time = "2025-04-08T10:35:01.698Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/d8a4d44ffe960517e487c9c04f77b06b8abf05eb680bed71c82b5f2cad62/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b2dddba7a4e6151384e252a5632efcaa9bc5d1c4b567f3cb621306b2ca9f63", size = 459151, upload-time = "2025-04-08T10:35:03.358Z" }, - { url = "https://files.pythonhosted.org/packages/6c/da/267a1546f26465dead1719caaba3ce660657f83c9d9c052ba98fb8856e13/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95cf944fcfc394c5f9de794ce581914900f82ff1f855326f25ebcf24d5397418", size = 489054, upload-time = "2025-04-08T10:35:04.561Z" }, - { url = "https://files.pythonhosted.org/packages/b1/31/33850dfd5c6efb6f27d2465cc4c6b27c5a6f5ed53c6fa63b7263cf5f60f6/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf6cd9f83d7c023b1aba15d13f705ca7b7d38675c121f3cc4a6e25bd0857ee9", size = 523955, upload-time = "2025-04-08T10:35:05.786Z" }, - { url = "https://files.pythonhosted.org/packages/09/84/b7d7b67856efb183a421f1416b44ca975cb2ea6c4544827955dfb01f7dc2/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:852de68acd6212cd6d33edf21e6f9e56e5d98c6add46f48244bd479d97c967c6", size = 502234, upload-time = "2025-04-08T10:35:07.187Z" }, - { url = "https://files.pythonhosted.org/packages/71/87/6dc5ec6882a2254cfdd8b0718b684504e737273903b65d7338efaba08b52/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5730f3aa35e646103b53389d5bc77edfbf578ab6dab2e005142b5b80a35ef25", size = 454750, upload-time = "2025-04-08T10:35:08.859Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6c/3786c50213451a0ad15170d091570d4a6554976cf0df19878002fc96075a/watchfiles-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:18b3bd29954bc4abeeb4e9d9cf0b30227f0f206c86657674f544cb032296acd5", size = 631591, upload-time = "2025-04-08T10:35:10.64Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b3/1427425ade4e359a0deacce01a47a26024b2ccdb53098f9d64d497f6684c/watchfiles-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ba5552a1b07c8edbf197055bc9d518b8f0d98a1c6a73a293bc0726dce068ed01", size = 625370, upload-time = "2025-04-08T10:35:12.412Z" }, - { url = "https://files.pythonhosted.org/packages/15/ba/f60e053b0b5b8145d682672024aa91370a29c5c921a88977eb565de34086/watchfiles-1.0.5-cp311-cp311-win32.whl", hash = "sha256:2f1fefb2e90e89959447bc0420fddd1e76f625784340d64a2f7d5983ef9ad246", size = 277791, upload-time = "2025-04-08T10:35:13.719Z" }, - { url = "https://files.pythonhosted.org/packages/50/ed/7603c4e164225c12c0d4e8700b64bb00e01a6c4eeea372292a3856be33a4/watchfiles-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:b6e76ceb1dd18c8e29c73f47d41866972e891fc4cc7ba014f487def72c1cf096", size = 291622, upload-time = "2025-04-08T10:35:15.071Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c2/99bb7c96b4450e36877fde33690ded286ff555b5a5c1d925855d556968a1/watchfiles-1.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:266710eb6fddc1f5e51843c70e3bebfb0f5e77cf4f27129278c70554104d19ed", size = 283699, upload-time = "2025-04-08T10:35:16.732Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8c/4f0b9bdb75a1bfbd9c78fad7d8854369283f74fe7cf03eb16be77054536d/watchfiles-1.0.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5eb568c2aa6018e26da9e6c86f3ec3fd958cee7f0311b35c2630fa4217d17f2", size = 401511, upload-time = "2025-04-08T10:35:17.956Z" }, - { url = "https://files.pythonhosted.org/packages/dc/4e/7e15825def77f8bd359b6d3f379f0c9dac4eb09dd4ddd58fd7d14127179c/watchfiles-1.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a04059f4923ce4e856b4b4e5e783a70f49d9663d22a4c3b3298165996d1377f", size = 392715, upload-time = "2025-04-08T10:35:19.202Z" }, - { url = "https://files.pythonhosted.org/packages/58/65/b72fb817518728e08de5840d5d38571466c1b4a3f724d190cec909ee6f3f/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e380c89983ce6e6fe2dd1e1921b9952fb4e6da882931abd1824c092ed495dec", size = 454138, upload-time = "2025-04-08T10:35:20.586Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a4/86833fd2ea2e50ae28989f5950b5c3f91022d67092bfec08f8300d8b347b/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe43139b2c0fdc4a14d4f8d5b5d967f7a2777fd3d38ecf5b1ec669b0d7e43c21", size = 458592, upload-time = "2025-04-08T10:35:21.87Z" }, - { url = "https://files.pythonhosted.org/packages/38/7e/42cb8df8be9a37e50dd3a818816501cf7a20d635d76d6bd65aae3dbbff68/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee0822ce1b8a14fe5a066f93edd20aada932acfe348bede8aa2149f1a4489512", size = 487532, upload-time = "2025-04-08T10:35:23.143Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fd/13d26721c85d7f3df6169d8b495fcac8ab0dc8f0945ebea8845de4681dab/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a0dbcb1c2d8f2ab6e0a81c6699b236932bd264d4cef1ac475858d16c403de74d", size = 522865, upload-time = "2025-04-08T10:35:24.702Z" }, - { url = "https://files.pythonhosted.org/packages/a1/0d/7f9ae243c04e96c5455d111e21b09087d0eeaf9a1369e13a01c7d3d82478/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2014a2b18ad3ca53b1f6c23f8cd94a18ce930c1837bd891262c182640eb40a6", size = 499887, upload-time = "2025-04-08T10:35:25.969Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0f/a257766998e26aca4b3acf2ae97dff04b57071e991a510857d3799247c67/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f6ae86d5cb647bf58f9f655fcf577f713915a5d69057a0371bc257e2553234", size = 454498, upload-time = "2025-04-08T10:35:27.353Z" }, - { url = "https://files.pythonhosted.org/packages/81/79/8bf142575a03e0af9c3d5f8bcae911ee6683ae93a625d349d4ecf4c8f7df/watchfiles-1.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1a7bac2bde1d661fb31f4d4e8e539e178774b76db3c2c17c4bb3e960a5de07a2", size = 630663, upload-time = "2025-04-08T10:35:28.685Z" }, - { url = "https://files.pythonhosted.org/packages/f1/80/abe2e79f610e45c63a70d271caea90c49bbf93eb00fa947fa9b803a1d51f/watchfiles-1.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ab626da2fc1ac277bbf752446470b367f84b50295264d2d313e28dc4405d663", size = 625410, upload-time = "2025-04-08T10:35:30.42Z" }, - { url = "https://files.pythonhosted.org/packages/91/6f/bc7fbecb84a41a9069c2c6eb6319f7f7df113adf113e358c57fc1aff7ff5/watchfiles-1.0.5-cp312-cp312-win32.whl", hash = "sha256:9f4571a783914feda92018ef3901dab8caf5b029325b5fe4558c074582815249", size = 277965, upload-time = "2025-04-08T10:35:32.023Z" }, - { url = "https://files.pythonhosted.org/packages/99/a5/bf1c297ea6649ec59e935ab311f63d8af5faa8f0b86993e3282b984263e3/watchfiles-1.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:360a398c3a19672cf93527f7e8d8b60d8275119c5d900f2e184d32483117a705", size = 291693, upload-time = "2025-04-08T10:35:33.225Z" }, - { url = "https://files.pythonhosted.org/packages/7f/7b/fd01087cc21db5c47e5beae507b87965db341cce8a86f9eb12bf5219d4e0/watchfiles-1.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:1a2902ede862969077b97523987c38db28abbe09fb19866e711485d9fbf0d417", size = 283287, upload-time = "2025-04-08T10:35:34.568Z" }, - { url = "https://files.pythonhosted.org/packages/c7/62/435766874b704f39b2fecd8395a29042db2b5ec4005bd34523415e9bd2e0/watchfiles-1.0.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0b289572c33a0deae62daa57e44a25b99b783e5f7aed81b314232b3d3c81a11d", size = 401531, upload-time = "2025-04-08T10:35:35.792Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a6/e52a02c05411b9cb02823e6797ef9bbba0bfaf1bb627da1634d44d8af833/watchfiles-1.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a056c2f692d65bf1e99c41045e3bdcaea3cb9e6b5a53dcaf60a5f3bd95fc9763", size = 392417, upload-time = "2025-04-08T10:35:37.048Z" }, - { url = "https://files.pythonhosted.org/packages/3f/53/c4af6819770455932144e0109d4854437769672d7ad897e76e8e1673435d/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9dca99744991fc9850d18015c4f0438865414e50069670f5f7eee08340d8b40", size = 453423, upload-time = "2025-04-08T10:35:38.357Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d1/8e88df58bbbf819b8bc5cfbacd3c79e01b40261cad0fc84d1e1ebd778a07/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:894342d61d355446d02cd3988a7326af344143eb33a2fd5d38482a92072d9563", size = 458185, upload-time = "2025-04-08T10:35:39.708Z" }, - { url = "https://files.pythonhosted.org/packages/ff/70/fffaa11962dd5429e47e478a18736d4e42bec42404f5ee3b92ef1b87ad60/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab44e1580924d1ffd7b3938e02716d5ad190441965138b4aa1d1f31ea0877f04", size = 486696, upload-time = "2025-04-08T10:35:41.469Z" }, - { url = "https://files.pythonhosted.org/packages/39/db/723c0328e8b3692d53eb273797d9a08be6ffb1d16f1c0ba2bdbdc2a3852c/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6f9367b132078b2ceb8d066ff6c93a970a18c3029cea37bfd7b2d3dd2e5db8f", size = 522327, upload-time = "2025-04-08T10:35:43.289Z" }, - { url = "https://files.pythonhosted.org/packages/cd/05/9fccc43c50c39a76b68343484b9da7b12d42d0859c37c61aec018c967a32/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2e55a9b162e06e3f862fb61e399fe9f05d908d019d87bf5b496a04ef18a970a", size = 499741, upload-time = "2025-04-08T10:35:44.574Z" }, - { url = "https://files.pythonhosted.org/packages/23/14/499e90c37fa518976782b10a18b18db9f55ea73ca14641615056f8194bb3/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0125f91f70e0732a9f8ee01e49515c35d38ba48db507a50c5bdcad9503af5827", size = 453995, upload-time = "2025-04-08T10:35:46.336Z" }, - { url = "https://files.pythonhosted.org/packages/61/d9/f75d6840059320df5adecd2c687fbc18960a7f97b55c300d20f207d48aef/watchfiles-1.0.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:13bb21f8ba3248386337c9fa51c528868e6c34a707f729ab041c846d52a0c69a", size = 629693, upload-time = "2025-04-08T10:35:48.161Z" }, - { url = "https://files.pythonhosted.org/packages/fc/17/180ca383f5061b61406477218c55d66ec118e6c0c51f02d8142895fcf0a9/watchfiles-1.0.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:839ebd0df4a18c5b3c1b890145b5a3f5f64063c2a0d02b13c76d78fe5de34936", size = 624677, upload-time = "2025-04-08T10:35:49.65Z" }, - { url = "https://files.pythonhosted.org/packages/bf/15/714d6ef307f803f236d69ee9d421763707899d6298d9f3183e55e366d9af/watchfiles-1.0.5-cp313-cp313-win32.whl", hash = "sha256:4a8ec1e4e16e2d5bafc9ba82f7aaecfeec990ca7cd27e84fb6f191804ed2fcfc", size = 277804, upload-time = "2025-04-08T10:35:51.093Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b4/c57b99518fadf431f3ef47a610839e46e5f8abf9814f969859d1c65c02c7/watchfiles-1.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:f436601594f15bf406518af922a89dcaab416568edb6f65c4e5bbbad1ea45c11", size = 291087, upload-time = "2025-04-08T10:35:52.458Z" }, - { url = "https://files.pythonhosted.org/packages/c5/95/94f3dd15557f5553261e407551c5e4d340e50161c55aa30812c79da6cb04/watchfiles-1.0.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2cfb371be97d4db374cba381b9f911dd35bb5f4c58faa7b8b7106c8853e5d225", size = 405686, upload-time = "2025-04-08T10:35:53.86Z" }, - { url = "https://files.pythonhosted.org/packages/f4/aa/b99e968153f8b70159ecca7b3daf46a6f46d97190bdaa3a449ad31b921d7/watchfiles-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3904d88955fda461ea2531fcf6ef73584ca921415d5cfa44457a225f4a42bc1", size = 396047, upload-time = "2025-04-08T10:35:55.232Z" }, - { url = "https://files.pythonhosted.org/packages/23/cb/90d3d760ad4bc7290e313fb9236c7d60598627a25a5a72764e48d9652064/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b7a21715fb12274a71d335cff6c71fe7f676b293d322722fe708a9ec81d91f5", size = 456081, upload-time = "2025-04-08T10:35:57.102Z" }, - { url = "https://files.pythonhosted.org/packages/3e/65/79c6cebe5bcb695cdac145946ad5a09b9f66762549e82fb2d064ea960c95/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dfd6ae1c385ab481766b3c61c44aca2b3cd775f6f7c0fa93d979ddec853d29d5", size = 459838, upload-time = "2025-04-08T10:35:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/3f/84/699f52632cdaa777f6df7f6f1cc02a23a75b41071b7e6765b9a412495f61/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b659576b950865fdad31fa491d31d37cf78b27113a7671d39f919828587b429b", size = 489753, upload-time = "2025-04-08T10:36:00.237Z" }, - { url = "https://files.pythonhosted.org/packages/25/68/3241f82ad414fd969de6bf3a93805682e5eb589aeab510322f2aa14462f8/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1909e0a9cd95251b15bff4261de5dd7550885bd172e3536824bf1cf6b121e200", size = 525015, upload-time = "2025-04-08T10:36:02.159Z" }, - { url = "https://files.pythonhosted.org/packages/85/c4/30d879e252f52b01660f545c193e6b81c48aac2e0eeec71263af3add905b/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:832ccc221927c860e7286c55c9b6ebcc0265d5e072f49c7f6456c7798d2b39aa", size = 503816, upload-time = "2025-04-08T10:36:03.869Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7d/fa34750f6f4b1a70d96fa6b685fe2948d01e3936328ea528f182943eb373/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fbb6102b3296926d0c62cfc9347f6237fb9400aecd0ba6bbda94cae15f2b3b", size = 456137, upload-time = "2025-04-08T10:36:05.226Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0c/a1569709aaeccb1dd74b0dd304d0de29e3ea1fdf11e08c78f489628f9ebb/watchfiles-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:15ac96dd567ad6c71c71f7b2c658cb22b7734901546cd50a475128ab557593ca", size = 632673, upload-time = "2025-04-08T10:36:06.752Z" }, - { url = "https://files.pythonhosted.org/packages/90/b6/645eaaca11f3ac625cf3b6e008e543acf0bf2581f68b5e205a13b05618b6/watchfiles-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b6227351e11c57ae997d222e13f5b6f1f0700d84b8c52304e8675d33a808382", size = 626659, upload-time = "2025-04-08T10:36:08.18Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c4/e741d9b92b0a2c74b976ff78bbc9a1276b4d904c590878e8fe0ec9fecca5/watchfiles-1.0.5-cp39-cp39-win32.whl", hash = "sha256:974866e0db748ebf1eccab17862bc0f0303807ed9cda465d1324625b81293a18", size = 278471, upload-time = "2025-04-08T10:36:10.546Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/36b0cb6add99105f78931994b30bc1dd24118c0e659ab6a3ffe0dd8734d4/watchfiles-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:9848b21ae152fe79c10dd0197304ada8f7b586d3ebc3f27f43c506e5a52a863c", size = 292027, upload-time = "2025-04-08T10:36:11.901Z" }, - { url = "https://files.pythonhosted.org/packages/1a/03/81f9fcc3963b3fc415cd4b0b2b39ee8cc136c42fb10a36acf38745e9d283/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f59b870db1f1ae5a9ac28245707d955c8721dd6565e7f411024fa374b5362d1d", size = 405947, upload-time = "2025-04-08T10:36:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/54/97/8c4213a852feb64807ec1d380f42d4fc8bfaef896bdbd94318f8fd7f3e4e/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9475b0093767e1475095f2aeb1d219fb9664081d403d1dff81342df8cd707034", size = 397276, upload-time = "2025-04-08T10:36:15.131Z" }, - { url = "https://files.pythonhosted.org/packages/78/12/d4464d19860cb9672efa45eec1b08f8472c478ed67dcd30647c51ada7aef/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc533aa50664ebd6c628b2f30591956519462f5d27f951ed03d6c82b2dfd9965", size = 455550, upload-time = "2025-04-08T10:36:16.635Z" }, - { url = "https://files.pythonhosted.org/packages/90/fb/b07bcdf1034d8edeaef4c22f3e9e3157d37c5071b5f9492ffdfa4ad4bed7/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed1cd825158dcaae36acce7b2db33dcbfd12b30c34317a88b8ed80f0541cc57", size = 455542, upload-time = "2025-04-08T10:36:18.655Z" }, - { url = "https://files.pythonhosted.org/packages/5b/84/7b69282c0df2bf2dff4e50be2c54669cddf219a5a5fb077891c00c00e5c8/watchfiles-1.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:554389562c29c2c182e3908b149095051f81d28c2fec79ad6c8997d7d63e0009", size = 405783, upload-time = "2025-04-08T10:36:20.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ae/03fca0545d99b7ea21df49bead7b51e7dca9ce3b45bb6d34530aa18c16a2/watchfiles-1.0.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a74add8d7727e6404d5dc4dcd7fac65d4d82f95928bbee0cf5414c900e86773e", size = 397133, upload-time = "2025-04-08T10:36:22.439Z" }, - { url = "https://files.pythonhosted.org/packages/1a/07/c2b6390003e933b2e187a3f7070c00bd87da8a58d6f2393e039b06a88c2e/watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb1489f25b051a89fae574505cc26360c8e95e227a9500182a7fe0afcc500ce0", size = 456198, upload-time = "2025-04-08T10:36:23.884Z" }, - { url = "https://files.pythonhosted.org/packages/46/d3/ecc62cbd7054f0812f3a7ca7c1c9f7ba99ba45efcfc8297a9fcd2c87b31c/watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0901429650652d3f0da90bad42bdafc1f9143ff3605633c455c999a2d786cac", size = 456511, upload-time = "2025-04-08T10:36:25.42Z" }, + { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757, upload-time = "2025-06-15T19:04:51.058Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511, upload-time = "2025-06-15T19:04:52.79Z" }, + { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739, upload-time = "2025-06-15T19:04:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106, upload-time = "2025-06-15T19:04:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264, upload-time = "2025-06-15T19:04:57.009Z" }, + { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612, upload-time = "2025-06-15T19:04:58.409Z" }, + { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242, upload-time = "2025-06-15T19:04:59.786Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148, upload-time = "2025-06-15T19:05:01.103Z" }, + { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574, upload-time = "2025-06-15T19:05:02.582Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378, upload-time = "2025-06-15T19:05:03.719Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829, upload-time = "2025-06-15T19:05:04.822Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192, upload-time = "2025-06-15T19:05:06.348Z" }, + { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" }, + { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" }, + { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" }, + { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" }, + { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" }, + { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" }, + { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" }, + { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, + { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, + { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, + { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, + { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, + { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, + { url = "https://files.pythonhosted.org/packages/47/8a/a45db804b9f0740f8408626ab2bca89c3136432e57c4673b50180bf85dd9/watchfiles-1.1.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:865c8e95713744cf5ae261f3067861e9da5f1370ba91fc536431e29b418676fa", size = 406400, upload-time = "2025-06-15T19:06:30.233Z" }, + { url = "https://files.pythonhosted.org/packages/64/06/a08684f628fb41addd451845aceedc2407dc3d843b4b060a7c4350ddee0c/watchfiles-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42f92befc848bb7a19658f21f3e7bae80d7d005d13891c62c2cd4d4d0abb3433", size = 397920, upload-time = "2025-06-15T19:06:31.315Z" }, + { url = "https://files.pythonhosted.org/packages/79/e6/e10d5675af653b1b07d4156906858041149ca222edaf8995877f2605ba9e/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0cc8365ab29487eb4f9979fd41b22549853389e22d5de3f134a6796e1b05a4", size = 451196, upload-time = "2025-06-15T19:06:32.435Z" }, + { url = "https://files.pythonhosted.org/packages/f6/8a/facd6988100cd0f39e89f6c550af80edb28e3a529e1ee662e750663e6b36/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90ebb429e933645f3da534c89b29b665e285048973b4d2b6946526888c3eb2c7", size = 458218, upload-time = "2025-06-15T19:06:33.503Z" }, + { url = "https://files.pythonhosted.org/packages/90/26/34cbcbc4d0f2f8f9cc243007e65d741ae039f7a11ef8ec6e9cd25bee08d1/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c588c45da9b08ab3da81d08d7987dae6d2a3badd63acdb3e206a42dbfa7cb76f", size = 484851, upload-time = "2025-06-15T19:06:34.541Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1f/f59faa9fc4b0e36dbcdd28a18c430416443b309d295d8b82e18192d120ad/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c55b0f9f68590115c25272b06e63f0824f03d4fc7d6deed43d8ad5660cabdbf", size = 599520, upload-time = "2025-06-15T19:06:35.785Z" }, + { url = "https://files.pythonhosted.org/packages/83/72/3637abecb3bf590529f5154ca000924003e5f4bbb9619744feeaf6f0b70b/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd17a1e489f02ce9117b0de3c0b1fab1c3e2eedc82311b299ee6b6faf6c23a29", size = 477956, upload-time = "2025-06-15T19:06:36.965Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f3/d14ffd9acc0c1bd4790378995e320981423263a5d70bd3929e2e0dc87fff/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da71945c9ace018d8634822f16cbc2a78323ef6c876b1d34bbf5d5222fd6a72e", size = 453196, upload-time = "2025-06-15T19:06:38.024Z" }, + { url = "https://files.pythonhosted.org/packages/7f/38/78ad77bd99e20c0fdc82262be571ef114fc0beef9b43db52adb939768c38/watchfiles-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:51556d5004887045dba3acdd1fdf61dddea2be0a7e18048b5e853dcd37149b86", size = 627479, upload-time = "2025-06-15T19:06:39.442Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/549d50a22fcc83f1017c6427b1c76c053233f91b526f4ad7a45971e70c0b/watchfiles-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04e4ed5d1cd3eae68c89bcc1a485a109f39f2fd8de05f705e98af6b5f1861f1f", size = 624414, upload-time = "2025-06-15T19:06:40.859Z" }, + { url = "https://files.pythonhosted.org/packages/72/de/57d6e40dc9140af71c12f3a9fc2d3efc5529d93981cd4d265d484d7c9148/watchfiles-1.1.0-cp39-cp39-win32.whl", hash = "sha256:c600e85f2ffd9f1035222b1a312aff85fd11ea39baff1d705b9b047aad2ce267", size = 280020, upload-time = "2025-06-15T19:06:41.89Z" }, + { url = "https://files.pythonhosted.org/packages/88/bb/7d287fc2a762396b128a0fca2dbae29386e0a242b81d1046daf389641db3/watchfiles-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3aba215958d88182e8d2acba0fdaf687745180974946609119953c0e112397dc", size = 292758, upload-time = "2025-06-15T19:06:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748, upload-time = "2025-06-15T19:06:44.2Z" }, + { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801, upload-time = "2025-06-15T19:06:45.774Z" }, + { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528, upload-time = "2025-06-15T19:06:46.791Z" }, + { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095, upload-time = "2025-06-15T19:06:48.211Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" }, + { url = "https://files.pythonhosted.org/packages/48/93/5c96bdb65e7f88f7da40645f34c0a3c317a2931ed82161e93c91e8eddd27/watchfiles-1.1.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7b3443f4ec3ba5aa00b0e9fa90cf31d98321cbff8b925a7c7b84161619870bc9", size = 406640, upload-time = "2025-06-15T19:06:54.868Z" }, + { url = "https://files.pythonhosted.org/packages/e3/25/09204836e93e1b99cce88802ce87264a1d20610c7a8f6de24def27ad95b1/watchfiles-1.1.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7049e52167fc75fc3cc418fc13d39a8e520cbb60ca08b47f6cedb85e181d2f2a", size = 398543, upload-time = "2025-06-15T19:06:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/5e/dc/6f324a6f32c5ab73b54311b5f393a79df34c1584b8d2404cf7e6d780aa5d/watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54062ef956807ba806559b3c3d52105ae1827a0d4ab47b621b31132b6b7e2866", size = 451787, upload-time = "2025-06-15T19:06:56.998Z" }, + { url = "https://files.pythonhosted.org/packages/45/5d/1d02ef4caa4ec02389e72d5594cdf9c67f1800a7c380baa55063c30c6598/watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a7bd57a1bb02f9d5c398c0c1675384e7ab1dd39da0ca50b7f09af45fa435277", size = 454272, upload-time = "2025-06-15T19:06:58.055Z" }, ] [[package]] @@ -3409,11 +3428,11 @@ wheels = [ [[package]] name = "zipp" -version = "3.22.0" +version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/b6/7b3d16792fdf94f146bed92be90b4eb4563569eca91513c8609aebf0c167/zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", size = 25257, upload-time = "2025-05-26T14:46:32.217Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/da/f64669af4cae46f17b90798a827519ce3737d31dbafad65d391e49643dc4/zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343", size = 9796, upload-time = "2025-05-26T14:46:30.775Z" }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ] [[package]] diff --git a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py index 6425c7c3c..a6ac2f9d4 100644 --- a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py +++ b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py @@ -34,6 +34,7 @@ from langchain_core.tools import BaseTool from pydantic import BaseModel from typing_extensions import Annotated, TypedDict +from langgraph._internal._runnable import RunnableCallable, RunnableLike from langgraph.errors import ErrorCode, create_error_message from langgraph.graph import END, StateGraph from langgraph.graph.message import add_messages @@ -42,7 +43,6 @@ from langgraph.managed import IsLastStep, RemainingSteps from langgraph.prebuilt.tool_node import ToolNode from langgraph.store.base import BaseStore from langgraph.types import Checkpointer, Send -from langgraph.utils.runnable import RunnableCallable, RunnableLike StructuredResponse = Union[dict, BaseModel] StructuredResponseSchema = Union[dict, type[BaseModel]] diff --git a/libs/prebuilt/langgraph/prebuilt/tool_node.py b/libs/prebuilt/langgraph/prebuilt/tool_node.py index 111f1f228..3de19db62 100644 --- a/libs/prebuilt/langgraph/prebuilt/tool_node.py +++ b/libs/prebuilt/langgraph/prebuilt/tool_node.py @@ -37,10 +37,10 @@ from langchain_core.tools.base import ( from pydantic import BaseModel from typing_extensions import Annotated, get_args, get_origin +from langgraph._internal._runnable import RunnableCallable from langgraph.errors import GraphBubbleUp from langgraph.store.base import BaseStore from langgraph.types import Command, Send -from langgraph.utils.runnable import RunnableCallable INVALID_TOOL_NAME_ERROR_TEMPLATE = ( "Error: {requested_tool} is not a valid tool, try one of [{available_tools}]." diff --git a/libs/prebuilt/langgraph/prebuilt/tool_validator.py b/libs/prebuilt/langgraph/prebuilt/tool_validator.py index 0e58c7d6c..d63e0c535 100644 --- a/libs/prebuilt/langgraph/prebuilt/tool_validator.py +++ b/libs/prebuilt/langgraph/prebuilt/tool_validator.py @@ -34,7 +34,7 @@ from pydantic import BaseModel, ValidationError from pydantic.v1 import BaseModel as BaseModelV1 from pydantic.v1 import ValidationError as ValidationErrorV1 -from langgraph.utils.runnable import RunnableCallable +from langgraph._internal._runnable import RunnableCallable def _default_format_error( diff --git a/libs/prebuilt/tests/memory_assert.py b/libs/prebuilt/tests/memory_assert.py index 10b93fdbd..872ea57ab 100644 --- a/libs/prebuilt/tests/memory_assert.py +++ b/libs/prebuilt/tests/memory_assert.py @@ -15,7 +15,7 @@ from langgraph.checkpoint.base import ( SerializerProtocol, ) from langgraph.checkpoint.memory import InMemorySaver, PersistentDict -from langgraph.pregel.checkpoint import copy_checkpoint +from langgraph.pregel._checkpoint import copy_checkpoint class NoopSerializer(SerializerProtocol): diff --git a/libs/prebuilt/tests/test_react_agent.py b/libs/prebuilt/tests/test_react_agent.py index 8c110126b..d704616db 100644 --- a/libs/prebuilt/tests/test_react_agent.py +++ b/libs/prebuilt/tests/test_react_agent.py @@ -29,6 +29,7 @@ from pydantic.v1 import BaseModel as BaseModelV1 from typing_extensions import TypedDict from langgraph.checkpoint.base import BaseCheckpointSaver +from langgraph.config import get_stream_writer from langgraph.graph import START, MessagesState, StateGraph, add_messages from langgraph.graph.message import REMOVE_ALL_MESSAGES from langgraph.prebuilt import ( @@ -53,7 +54,6 @@ from langgraph.prebuilt.tool_node import ( from langgraph.store.base import BaseStore from langgraph.store.memory import InMemoryStore from langgraph.types import Command, Interrupt, interrupt -from langgraph.utils.config import get_stream_writer from tests.any_str import AnyStr from tests.messages import _AnyIdHumanMessage, _AnyIdToolMessage from tests.model import FakeToolCallingModel From 1d3fd9a46b75d18eb1099c916f7d88ef43a77804 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:13:31 -0400 Subject: [PATCH 03/22] chore: merge `main` into `v1` (#5324) --- .github/dependabot.yml | 17 +- .github/workflows/ci.yml | 12 +- .github/workflows/uv_lock_ugprade.yml | 45 + Makefile | 10 + .../concepts/data_storage_and_privacy.md | 54 + docs/docs/cloud/how-tos/use_stream_react.md | 2 +- docs/docs/cloud/reference/api/openapi.json | 1713 +++++++++++++---- docs/mkdocs.yml | 1 + libs/checkpoint-postgres/pyproject.toml | 2 +- libs/checkpoint-sqlite/pyproject.toml | 2 +- libs/cli/examples/graphs/storm.py | 4 +- libs/cli/langgraph_cli/config.py | 5 +- libs/cli/langgraph_cli/templates.py | 2 +- libs/cli/pyproject.toml | 4 +- libs/cli/tests/unit_tests/cli/test_cli.py | 12 +- .../tests/unit_tests/cli/test_templates.py | 18 +- libs/cli/uv.lock | 573 +++--- libs/langgraph/langgraph/_internal/_fields.py | 6 +- libs/langgraph/langgraph/graph/state.py | 28 +- libs/langgraph/langgraph/pregel/main.py | 15 +- libs/langgraph/pyproject.toml | 6 +- .../tests/__snapshots__/test_large_cases.ambr | 12 +- .../tests/__snapshots__/test_pregel.ambr | 12 +- libs/langgraph/tests/test_large_cases.py | 12 +- libs/langgraph/tests/test_pregel.py | 22 +- libs/langgraph/tests/test_pregel_async.py | 4 +- libs/langgraph/tests/test_state.py | 17 +- libs/langgraph/uv.lock | 197 +- libs/prebuilt/pyproject.toml | 2 +- libs/sdk-js/package.json | 2 +- libs/sdk-js/src/types.messages.ts | 39 +- 31 files changed, 2006 insertions(+), 844 deletions(-) create mode 100644 .github/workflows/uv_lock_ugprade.yml create mode 100644 docs/docs/cloud/concepts/data_storage_and_privacy.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fcf1bd801..84770db13 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,18 @@ -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates -# and -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" + + - package-ecosystem: "pip" + directories: + - "libs/checkpoint" + - "libs/checkpoint-postgres" + - "libs/checkpoint-sqlite" + - "libs/cli" + - "libs/langgraph" + - "libs/prebuilt" + - "libs/sdk-py" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ece56b460..43752eddf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,7 @@ jobs: outputs: python: ${{ steps.filter.outputs.python }} sdk-js: ${{ steps.filter.outputs.sdk-js }} + deps: ${{ steps.filter.outputs.deps }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 @@ -38,6 +39,9 @@ jobs: - 'libs/prebuilt/**' sdk-js: - 'libs/sdk-js/**' + deps: + - '**/pyproject.toml' + - '**/uv.lock' lint: needs: changes @@ -55,7 +59,7 @@ jobs: "libs/prebuilt", ] - if: needs.changes.outputs.python == 'true' + if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true' uses: ./.github/workflows/_lint.yml with: working-directory: ${{ matrix.working-directory }} @@ -74,7 +78,7 @@ jobs: "libs/checkpoint-postgres", "libs/prebuilt", ] - if: needs.changes.outputs.python == 'true' + if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true' uses: ./.github/workflows/_test.yml with: working-directory: ${{ matrix.working-directory }} @@ -83,7 +87,7 @@ jobs: # NOTE: we're testing langgraph separately because it requires a different matrix test-langgraph: needs: changes - if: needs.changes.outputs.python == 'true' + if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true' name: "cd libs/langgraph" uses: ./.github/workflows/_test_langgraph.yml secrets: inherit @@ -140,7 +144,7 @@ jobs: integration-test: needs: changes - if: needs.changes.outputs.python == 'true' + if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true' name: CLI integration test uses: ./.github/workflows/_integration_test.yml secrets: inherit diff --git a/.github/workflows/uv_lock_ugprade.yml b/.github/workflows/uv_lock_ugprade.yml new file mode 100644 index 000000000..8acd39393 --- /dev/null +++ b/.github/workflows/uv_lock_ugprade.yml @@ -0,0 +1,45 @@ +name: UV Lock Upgrade + +on: + schedule: + # run at midnight every Sunday + - cron: '0 0 * * 0' + # allow manual triggering + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + upgrade-dependencies: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + # use minimum supported Python version + python-version: "3.9" + enable-cache: true + cache-suffix: "uv-lock-upgrade" + + - name: Run uv lock --upgrade in all Python packages + run: make lock-upgrade + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: upgrade dependencies with `uv lock --upgrade`" + title: "chore: upgrade dependencies with `uv lock --upgrade`" + body: | + This PR updates the dependencies in all Python packages using `uv lock --upgrade`. + + This is an automated PR created by the UV Lock Upgrade workflow. + branch: deps/uv-lock-upgrade + delete-branch: true + labels: | + dependencies diff --git a/Makefile b/Makefile index aa4eb9945..477e03123 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,16 @@ lock: fi; \ done +# Lock all projects and upgrade dependencies +.PHONY: lock-upgrade +lock-upgrade: + @for dir in $(LIBS_DIRS); do \ + if [ -f $$dir/Makefile ]; then \ + echo "Running lock-upgrade in $$dir"; \ + (cd $$dir && uv lock --upgrade); \ + fi; \ + done + # Test all projects .PHONY: test test: diff --git a/docs/docs/cloud/concepts/data_storage_and_privacy.md b/docs/docs/cloud/concepts/data_storage_and_privacy.md new file mode 100644 index 000000000..c9ca1f59d --- /dev/null +++ b/docs/docs/cloud/concepts/data_storage_and_privacy.md @@ -0,0 +1,54 @@ +# Data Storage and Privacy + +This document describes how data is processed in the LangGraph CLI and the LangGraph Server for both the in-memory server (`langgraph dev`) and the local Docker server (`langgraph up`). It also describes what data is tracked when interacting with the hosted LangGraph Studio frontend. + +## CLI + +LangGraph **CLI** is the command-line interface for building and running LangGraph applications; see the [CLI guide](../../concepts/langgraph_cli.md) to learn more. + +By default, calls to most CLI commands log a single analytics event upon invocation. This helps us better prioritize improvements to the CLI experience. Each telemetry event contains the calling process's OS, OS version, Python version, the CLI version, the command name (`dev`, `up`, `run`, etc.), and booleans representing whether a flag was passed to the command. You can see the full analytics logic [here](https://github.com/langchain-ai/langgraph/blob/main/libs/cli/langgraph_cli/analytics.py). + +You can disable all CLI telemetry by setting `LANGGRAPH_CLI_NO_ANALYTICS=1`. + +## LangGraph Server (in-memory & docker) + +The [LangGraph Server](../../concepts/langgraph_server.md) provides a durable execution runtime that relies on persisting checkpoints of your application state, long-term memories, thread metadata, assistants, and similar resources to the local file system or a database. Unless you have deliberately customized the storage location, this information is either written to local disk (for `langgraph dev`) or a PostgreSQL database (for `langgraph up` and in all deployments). + +### LangSmith Tracing + +When running the LangGraph server (either in-memory or in Docker), LangSmith tracing may be enabled to facilitate faster debugging and offer observability of graph state and LLM prompts in production. You can always disable tracing by setting `LANGSMITH_TRACING=false` in your server's runtime environment. + +### In-memory development server (`langgraph dev`) + +`langgraph dev` runs an [in-memory development server](../../tutorials/langgraph-platform/local-server.md) as a single Python process, designed for quick development and testing. It saves all checkpointing and memory data to disk within a `.langgraph_api` directory in the current working directory. Apart from the telemetry data described in the [CLI](#cli) section, no data leaves the machine unless you have enabled tracing or your graph code explicitly contacts an external service. + +### Standalone Container (`langgraph up`) + +`langgraph up` builds your local package into a Docker image and runs the server as a [standalone container](../../concepts/deployment_options.md#standalone-container) consisting of three containers: the API server, a PostgreSQL container, and a Redis container. All persistent data (checkpoints, assistants, etc.) are stored in the PostgreSQL database. Redis is used as a pubsub connection for real-time streaming of events. You can encrypt all checkpoints before saving to the database by setting a valid `LANGGRAPH_AES_KEY` environment variable. You can also specify [TTLs](../../how-tos/ttl/configure_ttl.md) for checkpoints and cross-thread memories in `langgraph.json` to control how long data is stored. All persisted threads, memories, and other data can be deleted via the relevant API endpoints. + +Additional API calls are made to confirm that the server has a valid license and to track the number of executed runs and tasks. Periodically, the API server validates the provided license key (or API key). + +If you've disabled [tracing](#langsmith-tracing), no user data is persisted externally unless your graph code explicitly contacts an external service. + +## Studio + +[LangGraph Studio](../../concepts/langgraph_studio.md) is a graphical interface for interacting with your LangGraph server. It does not persist any private data (the data you send to your server is not sent to LangSmith). Though the studio interface is served at [smith.langchain.com](https://smith.langchain.com), it is run in your browser and connects directly to your local LangGraph server so that no data needs to be sent to LangSmith. + +If you are logged in, LangSmith does collect some usage analytics to help improve studio's user experience. This includes: + +- Page visits and navigation patterns +- User actions (button clicks) +- Browser type and version +- Screen resolution and viewport size + +Importantly, no application data or code (or other sensitive configuration details) are collected. All of that is stored in the persistence layer of your LangGraph server. When using Studio anonymously, no account creation is required and usage analytics are not collected. + +## Quick reference + +In summary, you can opt-out of server-side telemetry by turning off CLI analytics and disabling tracing. + +| Variable | Purpose | Default | +| ------------------------------ | ------------------------- | -------------------------------- | +| `LANGGRAPH_CLI_NO_ANALYTICS=1` | Disable CLI analytics | Analytics enabled | +| `LANGSMITH_API_KEY` | Enable LangSmith tracing | Tracing disabled | +| `LANGSMITH_TRACING=false` | Disable LangSmith tracing | Depends on environment | diff --git a/docs/docs/cloud/how-tos/use_stream_react.md b/docs/docs/cloud/how-tos/use_stream_react.md index 29703761e..3ddcbe5b4 100644 --- a/docs/docs/cloud/how-tos/use_stream_react.md +++ b/docs/docs/cloud/how-tos/use_stream_react.md @@ -1,4 +1,4 @@ -How to integrate LangGraph into your React application# How to integrate LangGraph into your React application +# How to integrate LangGraph into your React application !!! info "Prerequisites" diff --git a/docs/docs/cloud/reference/api/openapi.json b/docs/docs/cloud/reference/api/openapi.json index 782619368..8a3b7f5fc 100644 --- a/docs/docs/cloud/reference/api/openapi.json +++ b/docs/docs/cloud/reference/api/openapi.json @@ -1,6 +1,9 @@ { "openapi": "3.1.0", - "info": { "title": "LangGraph Platform", "version": "0.1.0" }, + "info": { + "title": "LangGraph Platform", + "version": "0.1.0" + }, "tags": [ { "name": "Assistants", @@ -30,14 +33,18 @@ "paths": { "/assistants": { "post": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Create Assistant", "description": "Create an assistant.\n\nAn initial version of the assistant will be created and the assistant is set to that version. To change versions, use the `POST /assistants/{assistant_id}/latest` endpoint.", "operationId": "create_assistant_assistants_post", "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AssistantCreate" } + "schema": { + "$ref": "#/components/schemas/AssistantCreate" + } } }, "required": true @@ -47,7 +54,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Assistant" } + "schema": { + "$ref": "#/components/schemas/Assistant" + } } } }, @@ -55,7 +64,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -63,7 +74,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -71,7 +84,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -80,7 +95,9 @@ }, "/assistants/search": { "post": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Search Assistants", "description": "Search for assistants.\n\nThis endpoint also functions as the endpoint to list all assistants.", "operationId": "search_assistants_assistants_search_post", @@ -100,7 +117,9 @@ "content": { "application/json": { "schema": { - "items": { "$ref": "#/components/schemas/Assistant" }, + "items": { + "$ref": "#/components/schemas/Assistant" + }, "type": "array", "title": "Response Search Assistants Assistants Search Post" } @@ -111,7 +130,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -119,7 +140,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -128,7 +151,9 @@ }, "/assistants/{assistant_id}": { "get": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Get Assistant", "description": "Get an assistant by ID.", "operationId": "get_assistant_assistants__assistant_id__get", @@ -151,7 +176,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Assistant" } + "schema": { + "$ref": "#/components/schemas/Assistant" + } } } }, @@ -159,14 +186,18 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "delete": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Delete Assistant", "description": "Delete an assistant by ID.\n\nAll versions of the assistant will be deleted as well.", "operationId": "delete_assistant_assistants__assistant_id__delete", @@ -187,13 +218,19 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -201,14 +238,18 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "patch": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Patch Assistant", "description": "Update an assistant.", "operationId": "patch_assistant_assistants__assistant_id__patch", @@ -229,7 +270,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AssistantPatch" } + "schema": { + "$ref": "#/components/schemas/AssistantPatch" + } } }, "required": true @@ -239,7 +282,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Assistant" } + "schema": { + "$ref": "#/components/schemas/Assistant" + } } } }, @@ -247,7 +292,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -255,7 +302,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -264,7 +313,9 @@ }, "/assistants/{assistant_id}/graph": { "get": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Get Assistant Graph", "description": "Get an assistant by ID.", "operationId": "get_assistant_graph_assistants__assistant_id__graph_get", @@ -294,7 +345,14 @@ "description": "Include graph representation of subgraphs. If an integer value is provided, only subgraphs with a depth less than or equal to the value will be included.", "required": false, "schema": { - "oneOf": [{ "type": "boolean" }, { "type": "integer" }], + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + } + ], "title": "Xray", "default": false, "description": "Include graph representation of subgraphs. If an integer value is provided, only subgraphs with a depth less than or equal to the value will be included." @@ -310,7 +368,9 @@ "application/json": { "schema": { "additionalProperties": { - "items": { "type": "object" }, + "items": { + "type": "object" + }, "type": "array" }, "type": "object", @@ -323,7 +383,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -331,7 +393,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -340,7 +404,9 @@ }, "/assistants/{assistant_id}/subgraphs": { "get": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Get Assistant Subgraphs", "description": "Get an assistant's subgraphs.", "operationId": "get_assistant_subgraphs_assistants__assistant_id__subgraphs_get", @@ -373,7 +439,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Subgraphs" } + "schema": { + "$ref": "#/components/schemas/Subgraphs" + } } } }, @@ -381,7 +449,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -389,7 +459,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -398,7 +470,9 @@ }, "/assistants/{assistant_id}/subgraphs/{namespace}": { "get": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Get Assistant Subgraphs by Namespace", "description": "Get an assistant's subgraphs filtered by namespace.", "operationId": "get_assistant_subgraphs_assistants__assistant_id__subgraphs__namespace__get", @@ -417,7 +491,10 @@ { "description": "Namespace of the subgraph to filter by.", "required": true, - "schema": { "type": "string", "title": "Namespace" }, + "schema": { + "type": "string", + "title": "Namespace" + }, "name": "namespace", "in": "path" }, @@ -438,7 +515,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Subgraphs" } + "schema": { + "$ref": "#/components/schemas/Subgraphs" + } } } }, @@ -446,7 +525,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -455,7 +536,9 @@ }, "/assistants/{assistant_id}/schemas": { "get": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Get Assistant Schemas", "description": "Get an assistant by ID.", "operationId": "get_assistant_schemas_assistants__assistant_id__schemas_get", @@ -478,7 +561,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/GraphSchema" } + "schema": { + "$ref": "#/components/schemas/GraphSchema" + } } } }, @@ -486,7 +571,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -494,7 +581,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -503,7 +592,9 @@ }, "/assistants/{assistant_id}/versions": { "post": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Get Assistant Versions", "description": "Get all versions of an assistant.", "operationId": "get_assistant_versions_assistants__assistant_id__versions_get", @@ -527,7 +618,9 @@ "content": { "application/json": { "schema": { - "items": { "$ref": "#/components/schemas/Assistant" }, + "items": { + "$ref": "#/components/schemas/Assistant" + }, "type": "array", "title": "Response Search Assistants Assistants Search Post" } @@ -538,7 +631,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -547,7 +642,9 @@ }, "/assistants/{assistant_id}/latest": { "post": { - "tags": ["Assistants"], + "tags": [ + "Assistants" + ], "summary": "Set Latest Assistant Version", "description": "Set the latest version for an assistant.", "operationId": "set_latest_assistant_version_assistants__assistant_id__versions_post", @@ -581,7 +678,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Assistant" } + "schema": { + "$ref": "#/components/schemas/Assistant" + } } } }, @@ -589,7 +688,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -597,7 +698,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -606,14 +709,18 @@ }, "/threads": { "post": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Create Thread", "description": "Create a thread.", "operationId": "create_thread_threads_post", "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ThreadCreate" } + "schema": { + "$ref": "#/components/schemas/ThreadCreate" + } } }, "required": true @@ -623,7 +730,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Thread" } + "schema": { + "$ref": "#/components/schemas/Thread" + } } } }, @@ -631,7 +740,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -639,7 +750,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -648,7 +761,9 @@ }, "/threads/search": { "post": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Search Threads", "description": "Search for threads.\n\nThis endpoint also functions as the endpoint to list all threads.", "operationId": "search_threads_threads_search_post", @@ -668,7 +783,9 @@ "content": { "application/json": { "schema": { - "items": { "$ref": "#/components/schemas/Thread" }, + "items": { + "$ref": "#/components/schemas/Thread" + }, "type": "array", "title": "Response Search Threads Threads Search Post" } @@ -679,7 +796,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -688,7 +807,9 @@ }, "/threads/{thread_id}/state": { "get": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Get Thread State", "description": "Get state for a thread.\n\nThe latest state of the thread (i.e. latest checkpoint) is returned.", "operationId": "get_latest_thread_state_threads__thread_id__state_get", @@ -722,7 +843,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ThreadState" } + "schema": { + "$ref": "#/components/schemas/ThreadState" + } } } }, @@ -730,14 +853,18 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "post": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Update Thread State", "description": "Add state to a thread.", "operationId": "update_thread_state_threads__thread_id__state_post", @@ -758,7 +885,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ThreadStateUpdate" } + "schema": { + "$ref": "#/components/schemas/ThreadStateUpdate" + } } }, "required": true @@ -778,7 +907,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -787,7 +918,9 @@ }, "/threads/{thread_id}/state/{checkpoint_id}": { "get": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Get Thread State At Checkpoint", "description": "Get state for a thread at a specific checkpoint.", "operationId": "get_thread_state_at_checkpoint_threads__thread_id__state__checkpoint_id__get", @@ -833,7 +966,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ThreadState" } + "schema": { + "$ref": "#/components/schemas/ThreadState" + } } } }, @@ -841,7 +976,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -850,7 +987,9 @@ }, "/threads/{thread_id}/state/checkpoint": { "post": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Get Thread State At Checkpoint", "description": "Get state for a thread at a specific checkpoint.", "operationId": "post_thread_state_at_checkpoint_threads__thread_id__state__checkpoint_id__get", @@ -893,7 +1032,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ThreadState" } + "schema": { + "$ref": "#/components/schemas/ThreadState" + } } } }, @@ -901,7 +1042,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -910,7 +1053,9 @@ }, "/threads/{thread_id}/history": { "get": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Get Thread History", "description": "Get all past states for a thread.", "operationId": "get_thread_history_threads__thread_id__history_get", @@ -929,13 +1074,20 @@ }, { "required": false, - "schema": { "type": "integer", "title": "Limit", "default": 10 }, + "schema": { + "type": "integer", + "title": "Limit", + "default": 10 + }, "name": "limit", "in": "query" }, { "required": false, - "schema": { "type": "string", "title": "Before" }, + "schema": { + "type": "string", + "title": "Before" + }, "name": "before", "in": "query" } @@ -959,14 +1111,18 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "post": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Get Thread History Post", "description": "Get all past states for a thread.", "operationId": "get_thread_history_post_threads__thread_id__history_post", @@ -987,7 +1143,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ThreadStateSearch" } + "schema": { + "$ref": "#/components/schemas/ThreadStateSearch" + } } }, "required": true @@ -1011,7 +1169,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1020,7 +1180,9 @@ }, "/threads/{thread_id}/copy": { "post": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Copy Thread", "description": "Create a new thread with a copy of the state and checkpoints from an existing thread.", "operationId": "copy_thread_post_threads__thread_id__copy_post", @@ -1043,7 +1205,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Thread" } + "schema": { + "$ref": "#/components/schemas/Thread" + } } } }, @@ -1051,7 +1215,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1059,7 +1225,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1068,7 +1236,9 @@ }, "/threads/{thread_id}": { "get": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Get Thread", "description": "Get a thread by ID.", "operationId": "get_thread_threads__thread_id__get", @@ -1091,7 +1261,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Thread" } + "schema": { + "$ref": "#/components/schemas/Thread" + } } } }, @@ -1099,7 +1271,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1107,14 +1281,18 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "delete": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Delete Thread", "description": "Delete a thread by ID.", "operationId": "delete_thread_threads__thread_id__delete", @@ -1135,13 +1313,19 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1149,14 +1333,18 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "patch": { - "tags": ["Threads"], + "tags": [ + "Threads" + ], "summary": "Patch Thread", "description": "Update a thread.", "operationId": "patch_thread_threads__thread_id__patch", @@ -1177,7 +1365,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ThreadPatch" } + "schema": { + "$ref": "#/components/schemas/ThreadPatch" + } } }, "required": true @@ -1187,7 +1377,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Thread" } + "schema": { + "$ref": "#/components/schemas/Thread" + } } } }, @@ -1195,7 +1387,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1203,7 +1397,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1212,7 +1408,9 @@ }, "/threads/{thread_id}/runs": { "get": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "List Runs", "description": "List runs for a thread.", "operationId": "list_runs_http_threads__thread_id__runs_get", @@ -1231,13 +1429,21 @@ }, { "required": false, - "schema": { "type": "integer", "title": "Limit", "default": 10 }, + "schema": { + "type": "integer", + "title": "Limit", + "default": 10 + }, "name": "limit", "in": "query" }, { "required": false, - "schema": { "type": "integer", "title": "Offset", "default": 0 }, + "schema": { + "type": "integer", + "title": "Offset", + "default": 0 + }, "name": "offset", "in": "query" }, @@ -1245,7 +1451,13 @@ "required": false, "schema": { "type": "string", - "enum": ["pending", "error", "success", "timeout", "interrupted"] + "enum": [ + "pending", + "error", + "success", + "timeout", + "interrupted" + ] }, "name": "status", "in": "query" @@ -1257,7 +1469,9 @@ "content": { "application/json": { "schema": { - "items": { "$ref": "#/components/schemas/Run" }, + "items": { + "$ref": "#/components/schemas/Run" + }, "type": "array" } } @@ -1267,7 +1481,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1275,14 +1491,18 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "post": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Create Background Run", "description": "Create a run in existing thread, return the run ID immediately. Don't wait for the final run output.", "operationId": "create_run_threads__thread_id__runs_post", @@ -1303,7 +1523,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RunCreateStateful" } + "schema": { + "$ref": "#/components/schemas/RunCreateStateful" + } } }, "required": true @@ -1313,7 +1535,17 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Run" } + "schema": { + "$ref": "#/components/schemas/Run" + } + } + }, + "headers": { + "Content-Location": { + "description": "The URL of the run that was created. Can be used to later join the stream.", + "schema": { + "type": "string" + } } } }, @@ -1321,7 +1553,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1329,7 +1563,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1337,7 +1573,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1346,7 +1584,9 @@ }, "/threads/{thread_id}/runs/crons": { "post": { - "tags": ["Crons (Plus tier)"], + "tags": [ + "Crons (Plus tier)" + ], "summary": "Create Thread Cron", "description": "Create a cron to schedule runs on a thread.", "operationId": "create_thread_cron_threads__thread_id__runs_crons_post", @@ -1367,7 +1607,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CronCreate" } + "schema": { + "$ref": "#/components/schemas/CronCreate" + } } }, "required": true @@ -1377,7 +1619,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Cron" } + "schema": { + "$ref": "#/components/schemas/Cron" + } } } }, @@ -1385,7 +1629,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1393,7 +1639,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1402,7 +1650,9 @@ }, "/threads/{thread_id}/runs/stream": { "post": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Create Run, Stream Output", "description": "Create a run in existing thread. Stream the output.", "operationId": "stream_run_threads__thread_id__runs_stream_post", @@ -1423,7 +1673,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RunCreateStateful" } + "schema": { + "$ref": "#/components/schemas/RunCreateStateful" + } } }, "required": true @@ -1438,13 +1690,23 @@ "description": "The server will send a stream of events in SSE format.\n\n**Example event**:\n\nid: 1\n\nevent: message\n\ndata: {}" } } + }, + "headers": { + "Content-Location": { + "description": "The URL of the run that was created. Can be used to later join the stream.", + "schema": { + "type": "string" + } + } } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1452,7 +1714,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1460,7 +1724,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1469,7 +1735,9 @@ }, "/threads/{thread_id}/runs/wait": { "post": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Create Run, Wait for Output", "description": "Create a run in existing thread. Wait for the final output and then return it.", "operationId": "wait_run_threads__thread_id__runs_wait_post", @@ -1490,7 +1758,9 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RunCreateStateful" } + "schema": { + "$ref": "#/components/schemas/RunCreateStateful" + } } }, "required": true @@ -1498,13 +1768,27 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + }, + "headers": { + "Content-Location": { + "description": "The URL of the run that was created. Can be used to later join the stream.", + "schema": { + "type": "string" + } + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1512,7 +1796,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1520,7 +1806,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1529,7 +1817,9 @@ }, "/threads/{thread_id}/runs/{run_id}": { "get": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Get Run", "description": "Get a run by ID.", "operationId": "get_run_http_threads__thread_id__runs__run_id__get", @@ -1564,7 +1854,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Run" } + "schema": { + "$ref": "#/components/schemas/Run" + } } } }, @@ -1572,7 +1864,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1580,14 +1874,18 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "delete": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Delete Run", "description": "Delete a run by ID.", "operationId": "delete_run_threads__thread_id__runs__run_id__delete", @@ -1620,13 +1918,19 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1634,7 +1938,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1643,7 +1949,9 @@ }, "/threads/{thread_id}/runs/{run_id}/join": { "get": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Join Run", "description": "Wait for a run to finish.", "operationId": "join_run_http_threads__thread_id__runs__run_id__join_get", @@ -1687,13 +1995,19 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1701,7 +2015,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1710,9 +2026,11 @@ }, "/threads/{thread_id}/runs/{run_id}/stream": { "get": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Join Run Stream", - "description": "Join a run stream. This endpoint streams output in real-time from a run similar to the /threads/__THREAD_ID__/runs/stream endpoint. Only output produced after this endpoint is called will be streamed.", + "description": "Join a run stream. This endpoint streams output in real-time from a run similar to the /threads/__THREAD_ID__/runs/stream endpoint. If the run has been created with `stream_resumable=true`, the stream can be resumed from the last seen event ID.", "operationId": "stream_run_http_threads__thread_id__runs__run_id__join_get", "parameters": [ { @@ -1738,6 +2056,37 @@ }, "name": "run_id", "in": "path" + }, + { + "required": false, + "schema": { + "type": "string", + "title": "Last Event ID", + "description": "The ID of the last event received. Set to -1 if you want to stream all events. Requires `stream_resumable=true` to be set when creating the run." + }, + "name": "Last-Event-ID", + "in": "header" + }, + { + "required": false, + "schema": { + "type": "string", + "title": "Stream Mode", + "description": "The mode to stream the run in. If not provided, the default mode will be used." + }, + "name": "stream_mode", + "in": "query" + }, + { + "required": false, + "schema": { + "type": "boolean", + "title": "Cancel On Disconnect", + "description": "If true, the run will be cancelled if the client disconnects.", + "default": false + }, + "name": "cancel_on_disconnect", + "in": "query" } ], "responses": { @@ -1756,7 +2105,9 @@ "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1764,7 +2115,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1773,7 +2126,9 @@ }, "/threads/{thread_id}/runs/{run_id}/cancel": { "post": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Cancel Run", "operationId": "cancel_run_http_threads__thread_id__runs__run_id__cancel_post", "parameters": [ @@ -1816,7 +2171,10 @@ "required": false, "schema": { "type": "string", - "enum": ["interrupt", "rollback"], + "enum": [ + "interrupt", + "rollback" + ], "title": "Action", "default": "interrupt" }, @@ -1827,13 +2185,19 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1841,7 +2205,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1850,58 +2216,20 @@ }, "/runs/crons": { "post": { - "tags": ["Crons (Plus tier)"], + "tags": [ + "Crons (Plus tier)" + ], "summary": "Create Cron", "description": "Create a cron to schedule runs on new threads.", "operationId": "create_cron_runs_crons_post", "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CronCreate" } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/Cron" } + "schema": { + "$ref": "#/components/schemas/CronCreate" } } }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } - } - } - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } - } - } - } - } - } - }, - "/runs/crons/search": { - "post": { - "tags": ["Crons (Plus tier)"], - "summary": "Search Crons", - "description": "Search all active crons", - "operationId": "search_crons_runs_crons_post", - "requestBody": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/CronSearch" } - } - }, "required": true }, "responses": { @@ -1910,7 +2238,61 @@ "content": { "application/json": { "schema": { - "items": { "$ref": "#/components/schemas/Cron" }, + "$ref": "#/components/schemas/Cron" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/runs/crons/search": { + "post": { + "tags": [ + "Crons (Plus tier)" + ], + "summary": "Search Crons", + "description": "Search all active crons", + "operationId": "search_crons_runs_crons_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CronSearch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Cron" + }, "type": "array", "title": "Response Search Crons Search Post" } @@ -1921,7 +2303,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1930,7 +2314,9 @@ }, "/runs/stream": { "post": { - "tags": ["Stateless Runs"], + "tags": [ + "Stateless Runs" + ], "summary": "Create Run, Stream Output", "description": "Create a run in a new thread, stream the output.", "operationId": "stream_run_stateless_runs_stream_post", @@ -1954,13 +2340,23 @@ "description": "The server will send a stream of events in SSE format.\n\n**Example event**:\n\nid: 1\n\nevent: message\n\ndata: {}" } } + }, + "headers": { + "Content-Location": { + "description": "The URL of the run that was created. Can be used to later join the stream.", + "schema": { + "type": "string" + } + } } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1968,7 +2364,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -1976,7 +2374,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -1985,7 +2385,9 @@ }, "/runs/cancel": { "post": { - "tags": ["Thread Runs"], + "tags": [ + "Thread Runs" + ], "summary": "Cancel Runs", "description": "Cancel one or more runs. Can cancel runs by thread ID and run IDs, or by status filter.", "operationId": "cancel_runs_post", @@ -1995,7 +2397,10 @@ "required": false, "schema": { "type": "string", - "enum": ["interrupt", "rollback"], + "enum": [ + "interrupt", + "rollback" + ], "title": "Action", "default": "interrupt" }, @@ -2006,18 +2411,24 @@ "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RunsCancel" } + "schema": { + "$ref": "#/components/schemas/RunsCancel" + } } }, "required": true }, "responses": { - "204": { "description": "Success - Runs cancelled" }, + "204": { + "description": "Success - Runs cancelled" + }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2025,7 +2436,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2034,7 +2447,9 @@ }, "/runs/wait": { "post": { - "tags": ["Stateless Runs"], + "tags": [ + "Stateless Runs" + ], "summary": "Create Run, Wait for Output", "description": "Create a run in a new thread. Wait for the final output and then return it.", "operationId": "wait_run_stateless_runs_wait_post", @@ -2051,13 +2466,27 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + }, + "headers": { + "Content-Location": { + "description": "The URL of the run that was created. Can be used to later join the stream.", + "schema": { + "type": "string" + } + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2065,7 +2494,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2073,7 +2504,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2082,7 +2515,9 @@ }, "/runs": { "post": { - "tags": ["Stateless Runs"], + "tags": [ + "Stateless Runs" + ], "summary": "Create Background Run", "description": "Create a run in a new thread, return the run ID immediately. Don't wait for the final run output.", "operationId": "run_stateless_runs_post", @@ -2099,13 +2534,27 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + }, + "headers": { + "Content-Location": { + "description": "The URL of the run that was created. Can be used to later join the stream.", + "schema": { + "type": "string" + } + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2113,7 +2562,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2121,7 +2572,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2130,14 +2583,18 @@ }, "/runs/batch": { "post": { - "tags": ["Stateless Runs"], + "tags": [ + "Stateless Runs" + ], "summary": "Create Run Batch", "description": "Create a batch of runs in new threads, return immediately.", "operationId": "run_batch_stateless_runs_post", "requestBody": { "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RunBatchCreate" } + "schema": { + "$ref": "#/components/schemas/RunBatchCreate" + } } }, "required": true @@ -2145,13 +2602,19 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2159,7 +2622,9 @@ "description": "Conflict", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2167,7 +2632,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2176,7 +2643,9 @@ }, "/runs/crons/{cron_id}": { "delete": { - "tags": ["Crons (Plus tier)"], + "tags": [ + "Crons (Plus tier)" + ], "summary": "Delete Cron", "description": "Delete a cron by ID.", "operationId": "delete_cron_runs_crons__cron_id__delete", @@ -2195,13 +2664,19 @@ "responses": { "200": { "description": "Success", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": {} + } + } }, "404": { "description": "Not Found", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2209,7 +2684,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2218,31 +2695,41 @@ }, "/store/items": { "put": { - "tags": ["Store"], + "tags": [ + "Store" + ], "summary": "Store or update an item.", "operationId": "put_item", "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/StorePutRequest" } + "schema": { + "$ref": "#/components/schemas/StorePutRequest" + } } } }, "responses": { - "204": { "description": "Success" }, + "204": { + "description": "Success" + }, "422": { "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "delete": { - "tags": ["Store"], + "tags": [ + "Store" + ], "summary": "Delete an item.", "operationId": "delete_item", "requestBody": { @@ -2256,19 +2743,25 @@ } }, "responses": { - "204": { "description": "Success" }, + "204": { + "description": "Success" + }, "422": { "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } } }, "get": { - "tags": ["Store"], + "tags": [ + "Store" + ], "summary": "Retrieve a single item.", "operationId": "get_item", "parameters": [ @@ -2276,13 +2769,20 @@ "name": "key", "in": "query", "required": true, - "schema": { "type": "string" } + "schema": { + "type": "string" + } }, { "name": "namespace", "in": "query", "required": false, - "schema": { "type": "array", "items": { "type": "string" } } + "schema": { + "type": "array", + "items": { + "type": "string" + } + } } ], "responses": { @@ -2290,7 +2790,9 @@ "description": "Success", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Item" } + "schema": { + "$ref": "#/components/schemas/Item" + } } } }, @@ -2298,7 +2800,9 @@ "description": "Bad Request", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -2306,7 +2810,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2315,7 +2821,9 @@ }, "/store/items/search": { "post": { - "tags": ["Store"], + "tags": [ + "Store" + ], "summary": "Search for items within a namespace prefix.", "operationId": "search_items", "requestBody": { @@ -2343,7 +2851,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2352,7 +2862,9 @@ }, "/store/namespaces": { "post": { - "tags": ["Store"], + "tags": [ + "Store" + ], "summary": "List namespaces with optional match conditions.", "operationId": "list_namespaces", "requestBody": { @@ -2380,7 +2892,9 @@ "description": "Validation Error", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -2399,7 +2913,9 @@ "required": true, "schema": { "type": "string", - "enum": ["application/json, text/event-stream"] + "enum": [ + "application/json, text/event-stream" + ] }, "description": "Accept header must include both 'application/json' and 'text/event-stream' media types." } @@ -2408,7 +2924,9 @@ "required": true, "content": { "application/json": { - "schema": { "type": "object" }, + "schema": { + "type": "object" + }, "description": "A JSON-RPC 2.0 request, notification, or response object.", "example": { "jsonrpc": "2.0", @@ -2430,7 +2948,11 @@ "200": { "description": "Successful JSON-RPC response.", "content": { - "application/json": { "schema": { "type": "object" } } + "application/json": { + "schema": { + "type": "object" + } + } } }, "202": { @@ -2439,12 +2961,16 @@ "400": { "description": "Bad request: invalid JSON or message format, or unacceptable Accept header." }, - "405": { "description": "HTTP method not allowed." }, + "405": { + "description": "HTTP method not allowed." + }, "500": { "description": "Internal server error or unexpected failure." } }, - "tags": ["MCP"] + "tags": [ + "MCP" + ] }, "get": { "operationId": "get_mcp", @@ -2455,14 +2981,20 @@ "description": "GET method not allowed; streaming not supported." } }, - "tags": ["MCP"] + "tags": [ + "MCP" + ] }, "delete": { "operationId": "delete_mcp", "summary": "Terminate Session", "description": "Implemented according to the Streamable HTTP Transport specification.\nTerminate an MCP session. The server implementation is stateless, so this is a no-op.\n\n", - "responses": { "404": {} }, - "tags": ["MCP"] + "responses": { + "404": {} + }, + "tags": [ + "MCP" + ] } } }, @@ -2484,7 +3016,9 @@ "config": { "properties": { "tags": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Tags" }, @@ -2492,7 +3026,10 @@ "type": "integer", "title": "Recursion Limit" }, - "configurable": { "type": "object", "title": "Configurable" } + "configurable": { + "type": "object", + "title": "Configurable" + } }, "type": "object", "title": "Config", @@ -2526,7 +3063,10 @@ "description": "The name of the assistant" }, "description": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "title": "Assistant Description", "description": "The description of the assistant" } @@ -2567,7 +3107,10 @@ }, "if_exists": { "type": "string", - "enum": ["raise", "do_nothing"], + "enum": [ + "raise", + "do_nothing" + ], "title": "If Exists", "description": "How to handle duplicate creation. Must be either 'raise' (raise error if duplicate), or 'do_nothing' (return existing assistant).", "default": "raise" @@ -2578,13 +3121,18 @@ "description": "The name of the assistant. Defaults to 'Untitled'." }, "description": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "title": "Description", "description": "The description of the assistant. Defaults to null." } }, "type": "object", - "required": ["graph_id"], + "required": [ + "graph_id" + ], "title": "AssistantCreate", "description": "Payload for creating an assistant." }, @@ -2601,7 +3149,8 @@ "description": "Configuration to use for the graph. Useful when graph is configurable and you want to update the assistant's configuration." }, "metadata": { - "type": "object", "title": "Metadata", + "type": "object", + "title": "Metadata", "description": "Metadata to merge with existing assistant metadata." }, "name": { @@ -2634,12 +3183,20 @@ "Config": { "properties": { "tags": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Tags" }, - "recursion_limit": { "type": "integer", "title": "Recursion Limit" }, - "configurable": { "type": "object", "title": "Configurable" } + "recursion_limit": { + "type": "integer", + "title": "Recursion Limit" + }, + "configurable": { + "type": "object", + "title": "Configurable" + } }, "type": "object", "title": "Config" @@ -2713,7 +3270,6 @@ "title": "End Time", "description": "The end date to stop running the cron." }, - "assistant_id": { "anyOf": [ { @@ -2721,14 +3277,24 @@ "format": "uuid", "title": "Assistant Id" }, - { "type": "string", "title": "Graph Id" } + { + "type": "string", + "title": "Graph Id" + } ], "description": "The assistant ID or graph name to run. If using graph name, will default to the assistant automatically created from that graph by the server." }, "input": { "anyOf": [ - { "items": { "type": "object" }, "type": "array" }, - { "type": "object" } + { + "items": { + "type": "object" + }, + "type": "array" + }, + { + "type": "object" + } ], "title": "Input", "description": "The input to the graph." @@ -2741,7 +3307,9 @@ "config": { "properties": { "tags": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Tags" }, @@ -2749,7 +3317,10 @@ "type": "integer", "title": "Recursion Limit" }, - "configurable": { "type": "object", "title": "Configurable" } + "configurable": { + "type": "object", + "title": "Configurable" + } }, "type": "object", "title": "Config", @@ -2765,30 +3336,58 @@ }, "interrupt_before": { "anyOf": [ - { "type": "string", "enum": ["*"] }, - { "items": { "type": "string" }, "type": "array" } + { + "type": "string", + "enum": [ + "*" + ] + }, + { + "items": { + "type": "string" + }, + "type": "array" + } ], "title": "Interrupt Before", "description": "Nodes to interrupt immediately before they get executed." }, "interrupt_after": { "anyOf": [ - { "type": "string", "enum": ["*"] }, - { "items": { "type": "string" }, "type": "array" } + { + "type": "string", + "enum": [ + "*" + ] + }, + { + "items": { + "type": "string" + }, + "type": "array" + } ], "title": "Interrupt After", "description": "Nodes to interrupt immediately after they get executed." }, "multitask_strategy": { "type": "string", - "enum": ["reject", "rollback", "interrupt", "enqueue"], + "enum": [ + "reject", + "rollback", + "interrupt", + "enqueue" + ], "title": "Multitask Strategy", "description": "Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'.", "default": "reject" } }, "type": "object", - "required": ["assistant_id", "schedule"], + "required": [ + "assistant_id", + "schedule" + ], "title": "CronCreate", "description": "Payload for creating a cron job." }, @@ -2823,15 +3422,17 @@ }, "sort_by": { "type": "string", - "enum": ["cron_id", "assistant_id", "thread_id", "next_run_date", "end_time", "created_at", "updated_at"], "title": "Sort By", - "description": "The field to sort by." + "description": "The field to sort by.", + "default": "created_at", + "enum": ["cron_id", "assistant_id", "thread_id", "next_run_date", "end_time", "created_at", "updated_at"] }, "sort_order": { "type": "string", - "enum": ["asc", "desc"], "title": "Sort Order", - "description": "The order to sort by." + "description": "The order to sort by.", + "default": "desc", + "enum": ["asc", "desc"] } }, "type": "object", @@ -2868,7 +3469,11 @@ } }, "type": "object", - "required": ["graph_id", "state_schema", "config_schema"], + "required": [ + "graph_id", + "state_schema", + "config_schema" + ], "title": "GraphSchema", "description": "Defines the structure and properties of a graph." }, @@ -2947,19 +3552,34 @@ }, "status": { "type": "string", - "enum": ["pending", "error", "success", "timeout", "interrupted"], + "enum": [ + "pending", + "running", + "error", + "success", + "timeout", + "interrupted" + ], "title": "Status", - "description": "The status of the run. One of 'pending', 'error', 'success', 'timeout', 'interrupted'." + "description": "The status of the run. One of 'pending', 'running', 'error', 'success', 'timeout', 'interrupted'." }, "metadata": { "type": "object", "title": "Metadata", "description": "The run metadata." }, - "kwargs": { "type": "object", "title": "Kwargs" }, + "kwargs": { + "type": "object", + "title": "Kwargs" + }, "multitask_strategy": { "type": "string", - "enum": ["reject", "rollback", "interrupt", "enqueue"], + "enum": [ + "reject", + "rollback", + "interrupt", + "enqueue" + ], "title": "Multitask Strategy", "description": "Strategy to handle concurrent runs on the same thread." } @@ -2989,12 +3609,22 @@ "description": "The node to send the message to." }, "input": { - "type": ["object", "array", "number", "string", "boolean", "null"], + "type": [ + "object", + "array", + "number", + "string", + "boolean", + "null" + ], "title": "Message", "description": "The message to send." } }, - "required": ["node", "input"] + "required": [ + "node", + "input" + ] }, "Command": { "type": "object", @@ -3002,25 +3632,49 @@ "description": "The command to run.", "properties": { "update": { - "type": ["object", "array", "null"], + "type": [ + "object", + "array", + "null" + ], "title": "Update", "description": "An update to the state." }, "resume": { - "type": ["object", "array", "number", "string", "boolean", "null"], + "type": [ + "object", + "array", + "number", + "string", + "boolean", + "null" + ], "title": "Resume", "description": "A value to pass to an interrupted node." }, "goto": { "anyOf": [ - { "$ref": "#/components/schemas/Send" }, + { + "$ref": "#/components/schemas/Send" + }, { "type": "array", - "items": { "$ref": "#/components/schemas/Send" } + "items": { + "$ref": "#/components/schemas/Send" + } }, - { "type": "string" }, - { "type": "array", "items": { "type": "string" } }, - { "type": "null" } + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } ], "title": "Goto", "description": "Name of the node(s) to navigate to next or node(s) to be executed with a provided input." @@ -3036,7 +3690,10 @@ "format": "uuid", "title": "Assistant Id" }, - { "type": "string", "title": "Graph Id" } + { + "type": "string", + "title": "Graph Id" + } ], "description": "The assistant ID or graph name to run. If using graph name, will default to first assistant created from that graph." }, @@ -3048,20 +3705,36 @@ }, "input": { "anyOf": [ - { "type": "object" }, - { "type": "array" }, - { "type": "string" }, - { "type": "number" }, - { "type": "boolean" }, - { "type": "null" } + { + "type": "object" + }, + { + "type": "array" + }, + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + } ], "title": "Input", "description": "The input to the graph." }, "command": { "anyOf": [ - { "$ref": "#/components/schemas/Command" }, - { "type": "null" } + { + "$ref": "#/components/schemas/Command" + }, + { + "type": "null" + } ], "title": "Input", "description": "The input to the graph." @@ -3074,7 +3747,9 @@ "config": { "properties": { "tags": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Tags" }, @@ -3082,7 +3757,10 @@ "type": "integer", "title": "Recursion Limit" }, - "configurable": { "type": "object", "title": "Configurable" } + "configurable": { + "type": "object", + "title": "Configurable" + } }, "type": "object", "title": "Config", @@ -3098,16 +3776,36 @@ }, "interrupt_before": { "anyOf": [ - { "type": "string", "enum": ["*"] }, - { "items": { "type": "string" }, "type": "array" } + { + "type": "string", + "enum": [ + "*" + ] + }, + { + "items": { + "type": "string" + }, + "type": "array" + } ], "title": "Interrupt Before", "description": "Nodes to interrupt immediately before they get executed." }, "interrupt_after": { "anyOf": [ - { "type": "string", "enum": ["*"] }, - { "items": { "type": "string" }, "type": "array" } + { + "type": "string", + "enum": [ + "*" + ] + }, + { + "items": { + "type": "string" + }, + "type": "array" + } ], "title": "Interrupt After", "description": "Nodes to interrupt immediately after they get executed." @@ -3144,7 +3842,9 @@ ], "title": "Stream Mode", "description": "The stream mode(s) to use.", - "default": ["values"] + "default": [ + "values" + ] }, "stream_subgraphs": { "type": "boolean", @@ -3152,35 +3852,54 @@ "description": "Whether to stream output from subgraphs.", "default": false }, + "stream_resumable": { + "type": "boolean", + "title": "Stream Resumable", + "description": "Whether to persist the stream chunks in order to resume the stream later.", + "default": false + }, "on_disconnect": { "type": "string", - "enum": ["cancel", "continue"], + "enum": [ + "cancel", + "continue" + ], "title": "On Disconnect", "description": "The disconnect mode to use. Must be one of 'cancel' or 'continue'.", "default": "cancel" }, "feedback_keys": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Feedback Keys", "description": "Feedback keys to assign to run." }, "multitask_strategy": { "type": "string", - "enum": ["reject", "rollback", "interrupt", "enqueue"], + "enum": [ + "reject", + "rollback", + "interrupt", + "enqueue" + ], "title": "Multitask Strategy", "description": "Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'.", "default": "reject" }, "if_not_exists": { "type": "string", - "enum": ["create", "reject"], + "enum": [ + "create", + "reject" + ], "title": "If Not Exists", "description": "How to handle missing thread. Must be either 'reject' (raise error if missing), or 'create' (create new thread).", "default": "reject" }, "after_seconds": { - "type": "integer", + "type": "number", "title": "After Seconds", "description": "The number of seconds to wait before starting the run. Use to schedule future runs." }, @@ -3192,13 +3911,17 @@ } }, "type": "object", - "required": ["assistant_id"], + "required": [ + "assistant_id" + ], "title": "RunCreateStateful", "description": "Payload for creating a run." }, "RunBatchCreate": { "type": "array", - "items": { "$ref": "#/components/schemas/RunCreateStateless" }, + "items": { + "$ref": "#/components/schemas/RunCreateStateless" + }, "minItems": 1, "title": "RunBatchCreate", "description": "Payload for creating a batch of runs." @@ -3212,26 +3935,45 @@ "format": "uuid", "title": "Assistant Id" }, - { "type": "string", "title": "Graph Id" } + { + "type": "string", + "title": "Graph Id" + } ], "description": "The assistant ID or graph name to run. If using graph name, will default to first assistant created from that graph." }, "input": { "anyOf": [ - { "type": "object" }, - { "type": "array" }, - { "type": "string" }, - { "type": "number" }, - { "type": "boolean" }, - { "type": "null" } + { + "type": "object" + }, + { + "type": "array" + }, + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + } ], "title": "Input", "description": "The input to the graph." }, "command": { "anyOf": [ - { "$ref": "#/components/schemas/Command" }, - { "type": "null" } + { + "$ref": "#/components/schemas/Command" + }, + { + "type": "null" + } ], "title": "Input", "description": "The input to the graph." @@ -3244,7 +3986,9 @@ "config": { "properties": { "tags": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Tags" }, @@ -3252,7 +3996,10 @@ "type": "integer", "title": "Recursion Limit" }, - "configurable": { "type": "object", "title": "Configurable" } + "configurable": { + "type": "object", + "title": "Configurable" + } }, "type": "object", "title": "Config", @@ -3268,16 +4015,36 @@ }, "interrupt_before": { "anyOf": [ - { "type": "string", "enum": ["*"] }, - { "items": { "type": "string" }, "type": "array" } + { + "type": "string", + "enum": [ + "*" + ] + }, + { + "items": { + "type": "string" + }, + "type": "array" + } ], "title": "Interrupt Before", "description": "Nodes to interrupt immediately before they get executed." }, "interrupt_after": { "anyOf": [ - { "type": "string", "enum": ["*"] }, - { "items": { "type": "string" }, "type": "array" } + { + "type": "string", + "enum": [ + "*" + ] + }, + { + "items": { + "type": "string" + }, + "type": "array" + } ], "title": "Interrupt After", "description": "Nodes to interrupt immediately after they get executed." @@ -3314,10 +4081,14 @@ ], "title": "Stream Mode", "description": "The stream mode(s) to use.", - "default": ["values"] + "default": [ + "values" + ] }, "feedback_keys": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Feedback Keys", "description": "Feedback keys to assign to run." @@ -3328,22 +4099,34 @@ "description": "Whether to stream output from subgraphs.", "default": false }, + "stream_resumable": { + "type": "boolean", + "title": "Stream Resumable", + "description": "Whether to persist the stream chunks in order to resume the stream later.", + "default": false + }, "on_completion": { "type": "string", - "enum": ["delete", "keep"], + "enum": [ + "delete", + "keep" + ], "title": "On Completion", "description": "Whether to delete or keep the thread created for a stateless run. Must be one of 'delete' or 'keep'.", "default": "delete" }, "on_disconnect": { "type": "string", - "enum": ["cancel", "continue"], + "enum": [ + "cancel", + "continue" + ], "title": "On Disconnect", "description": "The disconnect mode to use. Must be one of 'cancel' or 'continue'.", "default": "cancel" }, "after_seconds": { - "type": "integer", + "type": "number", "title": "After Seconds", "description": "The number of seconds to wait before starting the run. Use to schedule future runs." }, @@ -3355,7 +4138,9 @@ } }, "type": "object", - "required": ["assistant_id"], + "required": [ + "assistant_id" + ], "title": "RunCreateStateless", "description": "Payload for creating a run." }, @@ -3400,7 +4185,10 @@ }, "sort_order": { "type": "string", - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "title": "Sort Order", "description": "The order to sort by." } @@ -3450,7 +4238,12 @@ }, "status": { "type": "string", - "enum": ["idle", "busy", "interrupted", "error"], + "enum": [ + "idle", + "busy", + "interrupted", + "error" + ], "title": "Status", "description": "Thread status to filter on." }, @@ -3471,13 +4264,21 @@ }, "sort_by": { "type": "string", - "enum": ["thread_id", "status", "created_at", "updated_at"], + "enum": [ + "thread_id", + "status", + "created_at", + "updated_at" + ], "title": "Sort By", "description": "Sort by field." }, "sort_order": { "type": "string", - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "title": "Sort Order", "description": "Sort order." } @@ -3513,7 +4314,12 @@ }, "status": { "type": "string", - "enum": ["idle", "busy", "interrupted", "error"], + "enum": [ + "idle", + "busy", + "interrupted", + "error" + ], "title": "Status", "description": "The status of the thread." }, @@ -3548,7 +4354,10 @@ }, "if_exists": { "type": "string", - "enum": ["raise", "do_nothing"], + "enum": [ + "raise", + "do_nothing" + ], "title": "If Exists", "description": "How to handle duplicate creation. Must be either 'raise' (raise error if duplicate), or 'do_nothing' (return existing thread).", "default": "raise" @@ -3560,7 +4369,9 @@ "properties": { "strategy": { "type": "string", - "enum": ["delete"], + "enum": [ + "delete" + ], "description": "The TTL strategy. 'delete' removes the entire thread.", "default": "delete" }, @@ -3582,7 +4393,9 @@ } } }, - "required": ["updates"] + "required": [ + "updates" + ] } } }, @@ -3615,7 +4428,9 @@ "description": "Include subgraph states." } }, - "required": ["checkpoint"], + "required": [ + "checkpoint" + ], "type": "object", "title": "ThreadStateCheckpointRequest", "description": "Payload for getting the state of a thread at a checkpoint." @@ -3624,13 +4439,22 @@ "properties": { "values": { "anyOf": [ - { "items": { "type": "object" }, "type": "array" }, - { "type": "object" } + { + "items": { + "type": "object" + }, + "type": "array" + }, + { + "type": "object" + } ], "title": "Values" }, "next": { - "items": { "type": "string" }, + "items": { + "type": "string" + }, "type": "array", "title": "Next" }, @@ -3638,17 +4462,34 @@ "items": { "type": "object", "properties": { - "id": { "type": "string", "title": "Task Id" }, - "name": { "type": "string", "title": "Node Name" }, - "error": { "type": "string", "title": "Error" }, - "interrupts": { "type": "array", "items": {} }, + "id": { + "type": "string", + "title": "Task Id" + }, + "name": { + "type": "string", + "title": "Node Name" + }, + "error": { + "type": "string", + "title": "Error" + }, + "interrupts": { + "type": "array", + "items": {} + }, "checkpoint": { "$ref": "#/components/schemas/CheckpointConfig", "title": "Checkpoint" }, - "state": { "$ref": "#/components/schemas/ThreadState" } + "state": { + "$ref": "#/components/schemas/ThreadState" + } }, - "required": ["id", "name"] + "required": [ + "id", + "name" + ] }, "type": "array", "title": "Tasks" @@ -3657,15 +4498,27 @@ "$ref": "#/components/schemas/CheckpointConfig", "title": "Checkpoint" }, - "metadata": { "type": "object", "title": "Metadata" }, - "created_at": { "type": "string", "title": "Created At" }, + "metadata": { + "type": "object", + "title": "Metadata" + }, + "created_at": { + "type": "string", + "title": "Created At" + }, "parent_checkpoint": { "type": "object", "title": "Parent Checkpoint" } }, "type": "object", - "required": ["values", "next", "checkpoint", "metadata", "created_at"], + "required": [ + "values", + "next", + "checkpoint", + "metadata", + "created_at" + ], "title": "ThreadState" }, "ThreadStateSearch": { @@ -3701,9 +4554,16 @@ "properties": { "values": { "anyOf": [ - { "items": { "type": "object" }, "type": "array" }, - { "type": "object" }, - { "type": "null" } + { + "items": {}, + "type": "array" + }, + { + "type": "object" + }, + { + "type": "null" + } ], "title": "Values", "description": "The values to update the state with." @@ -3727,15 +4587,28 @@ "properties": { "values": { "anyOf": [ - { "type": "array", "items": { "type": "object" } }, - { "type": "object" }, - { "type": "null" } + { + "type": "array", + "items": { + "type": "object" + } + }, + { + "type": "object" + }, + { + "type": "null" + } ] }, "command": { "anyOf": [ - { "$ref": "#/components/schemas/Command" }, - { "type": "null" } + { + "$ref": "#/components/schemas/Command" + }, + { + "type": "null" + } ], "description": "The command associated with the update." }, @@ -3744,12 +4617,17 @@ "description": "Update the state as if this node had just executed." } }, - "required": ["as_node"], + "required": [ + "as_node" + ], "type": "object" }, "ThreadStateUpdateResponse": { "properties": { - "checkpoint": { "type": "object", "title": "Checkpoint" } + "checkpoint": { + "type": "object", + "title": "Checkpoint" + } }, "type": "object", "title": "ThreadStateUpdateResponse", @@ -3780,11 +4658,17 @@ }, "StorePutRequest": { "type": "object", - "required": ["namespace", "key", "value"], + "required": [ + "namespace", + "key", + "value" + ], "properties": { "namespace": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "title": "Namespace", "description": "A list of strings representing the namespace path." }, @@ -3804,11 +4688,15 @@ }, "StoreDeleteRequest": { "type": "object", - "required": ["key"], + "required": [ + "key" + ], "properties": { "namespace": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "title": "Namespace", "description": "A list of strings representing the namespace path." }, @@ -3825,25 +4713,25 @@ "type": "object", "properties": { "namespace_prefix": { - "type": ["array", "null"], - "items": { "type": "string" }, + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, "title": "Namespace Prefix", "description": "List of strings representing the namespace prefix." }, "filter": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "additionalProperties": true, "title": "Filter", "description": "Optional dictionary of key-value pairs to filter results." }, - "query": { - "type": [ - "string", - "null" - ], - "title": "Query", - "description": "Query string for semantic/vector search." - }, "limit": { "type": "integer", "default": 10, @@ -3855,6 +4743,14 @@ "default": 0, "title": "Offset", "description": "Number of items to skip before returning results (default is 0)." + }, + "query": { + "type": [ + "string", + "null" + ], + "title": "Query", + "description": "Query string for semantic/vector search." } }, "title": "StoreSearchRequest", @@ -3865,13 +4761,17 @@ "properties": { "prefix": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "title": "Prefix", "description": "Optional list of strings representing the prefix to filter namespaces." }, "suffix": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "title": "Suffix", "description": "Optional list of strings representing the suffix to filter namespaces." }, @@ -3896,11 +4796,19 @@ }, "Item": { "type": "object", - "required": ["namespace", "key", "value", "created_at", "updated_at"], + "required": [ + "namespace", + "key", + "value", + "created_at", + "updated_at" + ], "properties": { "namespace": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "The namespace of the item. A namespace is analogous to a document's directory." }, "key": { @@ -3931,7 +4839,11 @@ "properties": { "status": { "type": "string", - "enum": ["pending", "running", "all"], + "enum": [ + "pending", + "running", + "all" + ], "title": "Status", "description": "Filter runs by status to cancel. Must be one of 'pending', 'running', or 'all'." }, @@ -3943,29 +4855,50 @@ }, "run_ids": { "type": "array", - "items": { "type": "string", "format": "uuid" }, + "items": { + "type": "string", + "format": "uuid" + }, "title": "Run Ids", "description": "List of run IDs to cancel." } }, "oneOf": [ - { "required": ["status"] }, - { "required": ["thread_id", "run_ids"] } + { + "required": [ + "status" + ] + }, + { + "required": [ + "thread_id", + "run_ids" + ] + } ] }, "SearchItemsResponse": { "type": "object", - "required": ["items"], + "required": [ + "items" + ], "properties": { "items": { "type": "array", - "items": { "$ref": "#/components/schemas/Item" } + "items": { + "$ref": "#/components/schemas/Item" + } } } }, "ListNamespaceResponse": { "type": "array", - "items": { "type": "array", "items": { "type": "string" } } + "items": { + "type": "array", + "items": { + "type": "string" + } + } }, "ErrorResponse": { "type": "string", @@ -3978,7 +4911,9 @@ "description": "Successful retrieval of an item.", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/Item" } + "schema": { + "$ref": "#/components/schemas/Item" + } } } }, @@ -3994,7 +4929,9 @@ "description": "Successful search operation.", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/SearchItemsResponse" } + "schema": { + "$ref": "#/components/schemas/SearchItemsResponse" + } } } }, @@ -4002,7 +4939,9 @@ "description": "Successful retrieval of namespaces.", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ListNamespaceResponse" } + "schema": { + "$ref": "#/components/schemas/ListNamespaceResponse" + } } } }, @@ -4010,7 +4949,9 @@ "description": "An error occurred.", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 487662a16..34a352fb0 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -215,6 +215,7 @@ nav: - how-tos/http/custom_middleware.md - how-tos/http/custom_routes.md - Data management: + - cloud/concepts/data_storage_and_privacy.md - Add semantic search: cloud/deployment/semantic_search.md - Add TTLs: how-tos/ttl/configure_ttl.md - Deployment: diff --git a/libs/checkpoint-postgres/pyproject.toml b/libs/checkpoint-postgres/pyproject.toml index 0442cfcd5..15e7b9aed 100644 --- a/libs/checkpoint-postgres/pyproject.toml +++ b/libs/checkpoint-postgres/pyproject.toml @@ -12,7 +12,7 @@ readme = "README.md" license = "MIT" license-files = ['LICENSE'] dependencies = [ - "langgraph-checkpoint>=2.0.21", + "langgraph-checkpoint>=2.0.21,<3.0.0", "orjson>=3.10.1", "psycopg>=3.2.0", "psycopg-pool>=3.2.0", diff --git a/libs/checkpoint-sqlite/pyproject.toml b/libs/checkpoint-sqlite/pyproject.toml index 7f378d892..d9907153a 100644 --- a/libs/checkpoint-sqlite/pyproject.toml +++ b/libs/checkpoint-sqlite/pyproject.toml @@ -12,7 +12,7 @@ readme = "README.md" license = "MIT" license-files = ['LICENSE'] dependencies = [ - "langgraph-checkpoint>=2.0.21", + "langgraph-checkpoint>=2.0.21,<3.0.0", "aiosqlite>=0.20", "sqlite-vec>=0.1.6", ] diff --git a/libs/cli/examples/graphs/storm.py b/libs/cli/examples/graphs/storm.py index 184ed04ef..147f0fdac 100644 --- a/libs/cli/examples/graphs/storm.py +++ b/libs/cli/examples/graphs/storm.py @@ -285,7 +285,7 @@ class AnswerWithCitations(BaseModel): @property def as_str(self) -> str: return f"{self.answer}\n\nCitations:\n\n" + "\n".join( - f"[{i+1}]: {url}" for i, url in enumerate(self.cited_urls) + f"[{i + 1}]: {url}" for i, url in enumerate(self.cited_urls) ) @@ -553,7 +553,7 @@ async def conduct_interviews(state: ResearchState): def format_conversation(interview_state): messages = interview_state["messages"] convo = "\n".join(f"{m.name}: {m.content}" for m in messages) - return f'Conversation with {interview_state["editor"].name}\n\n' + convo + return f"Conversation with {interview_state['editor'].name}\n\n" + convo async def refine_outline(state: ResearchState): diff --git a/libs/cli/langgraph_cli/config.py b/libs/cli/langgraph_cli/config.py index ece457aad..cd0356350 100644 --- a/libs/cli/langgraph_cli/config.py +++ b/libs/cli/langgraph_cli/config.py @@ -602,8 +602,7 @@ def validate_config(config: Config) -> Config: if not config["graphs"]: raise click.UsageError( - "No graphs found in config. " - "Add at least one graph to 'graphs' dictionary." + "No graphs found in config. Add at least one graph to 'graphs' dictionary." ) # Validate image_distro config @@ -1181,7 +1180,7 @@ def python_config_to_docker( ) for reqpath, destpath in local_deps.pip_reqs ) - pip_reqs_str += f'{os.linesep}RUN {pip_install} {" ".join("-r " + r for _,r in local_deps.pip_reqs)}' + pip_reqs_str += f"{os.linesep}RUN {pip_install} {' '.join('-r ' + r for _, r in local_deps.pip_reqs)}" pip_reqs_str = f"""# -- Installing local requirements -- {pip_reqs_str} # -- End of local requirements install --""" diff --git a/libs/cli/langgraph_cli/templates.py b/libs/cli/langgraph_cli/templates.py index cd336e255..4ea915228 100644 --- a/libs/cli/langgraph_cli/templates.py +++ b/libs/cli/langgraph_cli/templates.py @@ -123,7 +123,7 @@ def _download_repo_with_requests(repo_url: str, path: str) -> None: ) except error.HTTPError as e: click.secho( - f"❌ Error: Failed to download repository.\n" f"Details: {e}\n", + f"❌ Error: Failed to download repository.\nDetails: {e}\n", fg="red", bold=True, err=True, diff --git a/libs/cli/pyproject.toml b/libs/cli/pyproject.toml index 839f714bd..fb2345bae 100644 --- a/libs/cli/pyproject.toml +++ b/libs/cli/pyproject.toml @@ -18,8 +18,8 @@ dependencies = [ [project.optional-dependencies] inmem = [ - "langgraph-api>=0.2.67 ; python_version >= '3.11'", - "langgraph-runtime-inmem>=0.3.4 ; python_version >= '3.11'", + "langgraph-api>=0.2.67,<0.3.0 ; python_version >= '3.11'", + "langgraph-runtime-inmem>=0.3.4,<0.4.0 ; python_version >= '3.11'", "python-dotenv>=0.8.0", ] diff --git a/libs/cli/tests/unit_tests/cli/test_cli.py b/libs/cli/tests/unit_tests/cli/test_cli.py index d07d6f7a8..268f44e00 100644 --- a/libs/cli/tests/unit_tests/cli/test_cli.py +++ b/libs/cli/tests/unit_tests/cli/test_cli.py @@ -280,9 +280,9 @@ def test_version_option() -> None: assert result.exit_code == 0, "Expected exit code 0 for --version option" # Check that the output contains the correct version information - assert ( - "LangGraph CLI, version" in result.output - ), "Expected version information in output" + assert "LangGraph CLI, version" in result.output, ( + "Expected version information in output" + ) def test_dockerfile_command_basic() -> None: @@ -570,6 +570,6 @@ def test_build_generate_proper_build_context(): build_context_pattern = re.compile(r"--build-context\s+(\w+)=([^\s]+)") build_contexts = re.findall(build_context_pattern, result.output) - assert ( - len(build_contexts) == 2 - ), f"Expected 2 build contexts, but found {len(build_contexts)}" + assert len(build_contexts) == 2, ( + f"Expected 2 build contexts, but found {len(build_contexts)}" + ) diff --git a/libs/cli/tests/unit_tests/cli/test_templates.py b/libs/cli/tests/unit_tests/cli/test_templates.py index acd7651d0..cbdf4bbe1 100644 --- a/libs/cli/tests/unit_tests/cli/test_templates.py +++ b/libs/cli/tests/unit_tests/cli/test_templates.py @@ -42,18 +42,18 @@ def test_create_new_with_mocked_download(mock_urlopen: MagicMock) -> None: # Verify CLI command execution and success assert result.exit_code == 0, result.output - assert ( - "New project created" in result.output - ), "Expected success message in output." + assert "New project created" in result.output, ( + "Expected success message in output." + ) # Verify that the directory is not empty assert os.listdir(temp_dir), "Expected files to be created in temp directory." # Check for a known file in the extracted content extracted_files = [f.name for f in Path(temp_dir).glob("*")] - assert ( - "test-file.txt" in extracted_files - ), "Expected 'test-file.txt' in the extracted content." + assert "test-file.txt" in extracted_files, ( + "Expected 'test-file.txt' in the extracted content." + ) def test_invalid_template_id() -> None: @@ -65,6 +65,6 @@ def test_invalid_template_id() -> None: # Verify the command failed and proper message is displayed assert result.exit_code != 0, "Expected non-zero exit code for invalid template." - assert ( - "Template 'invalid-template-id' not found" in result.output - ), "Expected error message in output." + assert "Template 'invalid-template-id' not found" in result.output, ( + "Expected error message in output." + ) diff --git a/libs/cli/uv.lock b/libs/cli/uv.lock index bf7ad385f..b52c20443 100644 --- a/libs/cli/uv.lock +++ b/libs/cli/uv.lock @@ -2,10 +2,9 @@ version = 1 revision = 2 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.12.4' and python_full_version < '4.0'", - "python_full_version >= '3.11' and python_full_version < '3.12.4'", - "python_full_version >= '4.0'", - "python_full_version < '3.11'", + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", ] [[package]] @@ -45,11 +44,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, ] [[package]] @@ -199,14 +198,33 @@ wheels = [ name = "click" version = "8.1.8" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + [[package]] name = "cloudpickle" version = "3.1.1" @@ -289,11 +307,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf57 [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] [[package]] @@ -423,7 +444,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.66" +version = "0.3.67" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch", marker = "python_full_version >= '3.11'" }, @@ -434,31 +455,31 @@ dependencies = [ { name = "tenacity", marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/63/470aa84393bad5d51749417af58522a691174f8b2d05843f5633d473faa0/langchain_core-0.3.66.tar.gz", hash = "sha256:350c92e792ec1401f4b740d759b95f297710a50de29e1be9fbfff8676ef62117", size = 560102, upload-time = "2025-06-20T22:08:19.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/40/875af0194024d0006874f061958fa417d3500bbfdc9a57e1bd1c2f4e6ed2/langchain_core-0.3.67.tar.gz", hash = "sha256:2c14aa44a0e78e014e96d7f2f8916ac109d0a0ba87ed67ee25bf7296bed7e7ba", size = 561952, upload-time = "2025-06-30T17:09:35.142Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/c3/8080431fd7567a340d3a42e36c0bb3970a8d00d5e27bf3ca2103b3b55996/langchain_core-0.3.66-py3-none-any.whl", hash = "sha256:65cd6c3659afa4f91de7aa681397a0c53ff9282425c281e53646dd7faf16099e", size = 438874, upload-time = "2025-06-20T22:08:17.52Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2b/a0d283089c6d08c12d47dca39a55029ff714e939ec04f4560420426ab613/langchain_core-0.3.67-py3-none-any.whl", hash = "sha256:b699f1f24b24fa2747c05e2daa280aa64478a51e01a4e82c7f8e20b6167dfa99", size = 440237, upload-time = "2025-06-30T17:09:33.323Z" }, ] [[package]] name = "langgraph" -version = "0.4.3" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "langchain-core", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "langchain-core", marker = "python_full_version >= '3.11'" }, { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" }, - { name = "langgraph-prebuilt", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph-sdk", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "langgraph-prebuilt", marker = "python_full_version >= '3.11'" }, + { name = "langgraph-sdk", marker = "python_full_version >= '3.11'" }, { name = "pydantic", marker = "python_full_version >= '3.11'" }, { name = "xxhash", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/9e/5a64602eff18a99d0216a80eff823051ffbdb7c11b5a16171cee8b1ccce5/langgraph-0.4.3.tar.gz", hash = "sha256:272d5d5903f2c2882dbeeba849846a0f2500bd83fb3734a3801ebe64c1a60bdd", size = 125407, upload-time = "2025-05-08T03:40:02.882Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/5f/f08123cbfa0384b6a4011a9547fdbca52b53d51d69b21ff2a03332fde694/langgraph-0.5.0.tar.gz", hash = "sha256:7ad6d42f2a44e93e225cc65c59fac51c55ae549c9824adc22971d00e5ac26443", size = 434232, upload-time = "2025-06-26T22:55:18.068Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/35/53/0a20edd9f41eb3707722444ec1b43752b792bbe904d1c8cc3ba27f8eb2c8/langgraph-0.4.3-py3-none-any.whl", hash = "sha256:dec926e034f4d440b92a3c52139cb6e9763bc1791e79a6ea53a233309cec864f", size = 151191, upload-time = "2025-05-08T03:40:01.07Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a4/0d5551ef675d382497765c74cdd893e2758d2dc2041e0f3671f110761a04/langgraph-0.5.0-py3-none-any.whl", hash = "sha256:74e33efb6527602b79bfcc4e3d0bfbb15b8d86fa25bb417fdd8d3306456cf8de", size = 143658, upload-time = "2025-06-26T22:55:16.599Z" }, ] [[package]] name = "langgraph-api" -version = "0.2.67" +version = "0.2.78" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle", marker = "python_full_version >= '3.11'" }, @@ -481,22 +502,22 @@ dependencies = [ { name = "uvicorn", marker = "python_full_version >= '3.11'" }, { name = "watchfiles", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/36/a1ef1b9cc71450068708ebf6fc0fa5342c3e3129f37932f195b2a1c7814b/langgraph_api-0.2.67.tar.gz", hash = "sha256:68f2237fd4612d6d0e750824170a4ed9a9566b610ca2d6a605fb136bab32d908", size = 228371, upload-time = "2025-06-26T01:53:44.07Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/88/1aa91bede22d1393a53605ff13a57d6cb3ce2831eb250a8a8f814c548d2e/langgraph_api-0.2.78.tar.gz", hash = "sha256:37aad4fe764934f358dbb26d50b4acbf4180aac673499d1502b5b468cc7acba4", size = 233581, upload-time = "2025-07-02T19:11:01.154Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/b1/69e2366b16daeec87954e04e04e478308be5dde5d54cc79d0e125c4accb4/langgraph_api-0.2.67-py3-none-any.whl", hash = "sha256:0e06e6df6152e8fb3a98ca512c7c73927d688e841fe1000df6a3badc42d9b268", size = 183672, upload-time = "2025-06-26T01:53:42.794Z" }, + { url = "https://files.pythonhosted.org/packages/7e/76/46626d99c36fce0c1ee1e1581f54b29acbed5e9f2e7cf9a198c825547d91/langgraph_api-0.2.78-py3-none-any.whl", hash = "sha256:57abe7fff9763d6f4eaf4419d103f75e77dca6d271db3816777c04bd67db4449", size = 190222, upload-time = "2025-07-02T19:10:59.793Z" }, ] [[package]] name = "langgraph-checkpoint" -version = "2.0.25" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core", marker = "python_full_version >= '3.11'" }, { name = "ormsgpack", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/72/d49828e6929cb3ded1472aa3e5e4a369d292c4f21021ac683d28fbc8f4f8/langgraph_checkpoint-2.0.25.tar.gz", hash = "sha256:77a63cab7b5f84dec1d49db561326ec28bdd48bcefb7fe4ac372069d2609287b", size = 36952, upload-time = "2025-04-26T21:00:43.5Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/30/c04abcb2ac30f200dbfde5839ca3832552fe2bd852d9e85a68e47418a11c/langgraph_checkpoint-2.1.0.tar.gz", hash = "sha256:cdaa2f0b49aa130ab185c02d82f02b40299a1fbc9ac59ac20cecce09642a1abe", size = 135501, upload-time = "2025-06-16T22:05:01.918Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/52/bceb5b5348c7a60ef0625ab0a0a0a9ff5d78f0e12aed8cc55c49d5e8a8c9/langgraph_checkpoint-2.0.25-py3-none-any.whl", hash = "sha256:23416a0f5bc9dd712ac10918fc13e8c9c4530c419d2985a441df71a38fc81602", size = 42312, upload-time = "2025-04-26T21:00:42.242Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/390a97d9d0abe5b71eea2f6fb618d8adadefa674e97f837bae6cda670bc7/langgraph_checkpoint-2.1.0-py3-none-any.whl", hash = "sha256:4cea3e512081da1241396a519cbfe4c5d92836545e2c64e85b6f5c34a1b8bc61", size = 43844, upload-time = "2025-06-16T22:05:00.758Z" }, ] [[package]] @@ -504,7 +525,8 @@ name = "langgraph-cli" version = "0.3.3" source = { editable = "." } dependencies = [ - { name = "click" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "langgraph-sdk", marker = "python_full_version >= '3.11'" }, ] @@ -530,8 +552,8 @@ dev = [ [package.metadata] requires-dist = [ { name = "click", specifier = ">=8.1.7" }, - { name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.67" }, - { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3.4" }, + { name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.67,<0.3.0" }, + { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3.4,<0.4.0" }, { name = "langgraph-sdk", marker = "python_full_version >= '3.11'", specifier = ">=0.1.0" }, { name = "python-dotenv", marker = "extra == 'inmem'", specifier = ">=0.8.0" }, ] @@ -551,15 +573,15 @@ dev = [ [[package]] name = "langgraph-prebuilt" -version = "0.1.8" +version = "0.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "langchain-core", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, - { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11' and python_full_version < '4.0'" }, + { name = "langchain-core", marker = "python_full_version >= '3.11'" }, + { name = "langgraph-checkpoint", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/30/f31f0e076c37d097b53e4cff5d479a3686e1991f6c86a1a4727d5d1f5489/langgraph_prebuilt-0.1.8.tar.gz", hash = "sha256:4de7659151829b2b955b6798df6800e580e617782c15c2c5b29b139697491831", size = 24543, upload-time = "2025-04-03T16:04:19.932Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/11/98134c47832fbde0caf0e06f1a104577da9215c358d7854093c1d835b272/langgraph_prebuilt-0.5.2.tar.gz", hash = "sha256:2c900a5be0d6a93ea2521e0d931697cad2b646f1fcda7aa5c39d8d7539772465", size = 117808, upload-time = "2025-06-30T19:52:48.307Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/72/9e092665502f8f52f2708065ed14fbbba3f95d1a1b65d62049b0c5fcdf00/langgraph_prebuilt-0.1.8-py3-none-any.whl", hash = "sha256:ae97b828ae00be2cefec503423aa782e1bff165e9b94592e224da132f2526968", size = 25903, upload-time = "2025-04-03T16:04:18.993Z" }, + { url = "https://files.pythonhosted.org/packages/c3/64/6bc45ab9e0e1112698ebff579fe21f5606ea65cd08266995a357e312a4d2/langgraph_prebuilt-0.5.2-py3-none-any.whl", hash = "sha256:1f4cd55deca49dffc3e5127eec12fcd244fc381321002f728afa88642d5ec59d", size = 23776, upload-time = "2025-06-30T19:52:47.494Z" }, ] [[package]] @@ -581,20 +603,20 @@ wheels = [ [[package]] name = "langgraph-sdk" -version = "0.1.66" +version = "0.1.72" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx", marker = "python_full_version >= '3.11'" }, { name = "orjson", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/7a/5fede018d8b9100db14211cfdb94aefd0e5f2e9ae738072f3d4cc443465b/langgraph_sdk-0.1.66.tar.gz", hash = "sha256:81474ad4555a06004cc7a2f4ab477135d5eaf7db11fbcf2a69257fb2d717582e", size = 44049, upload-time = "2025-04-30T22:59:09.085Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/a6/cf13ace9bc7f0e8b13852ced0b37ece97f3140e232821c28bc852f8c1ea2/langgraph_sdk-0.1.72.tar.gz", hash = "sha256:396d8195881830700e2d54a0a9ee273e8b1173428e667502ef9c182a3cec7ab7", size = 71600, upload-time = "2025-06-27T01:12:03.788Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/06/87ce0b8043ba5a4ec8369a243f3140f8fd9d9b7aab1d8a9351711739beea/langgraph_sdk-0.1.66-py3-none-any.whl", hash = "sha256:f781c63f3e913d3d6bedb02cb84d775cda64e3cdf3282fd387bdd8faaf53c603", size = 47584, upload-time = "2025-04-30T22:59:07.953Z" }, + { url = "https://files.pythonhosted.org/packages/4b/4b/d56b51da08d168c2315cd092faa47bc83388b116756dbd6995026ec9ba3f/langgraph_sdk-0.1.72-py3-none-any.whl", hash = "sha256:925d3fcc7a26361db04f9c4beb3ec05bc36361b2a836d181ff2ab145071ec3ce", size = 50129, upload-time = "2025-06-27T01:12:02.449Z" }, ] [[package]] name = "langsmith" -version = "0.4.2" +version = "0.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx", marker = "python_full_version >= '3.11'" }, @@ -605,9 +627,9 @@ dependencies = [ { name = "requests-toolbelt", marker = "python_full_version >= '3.11'" }, { name = "zstandard", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/b6/0ebc396baf6b69aeb9eb466bbeaccd504c901615e744b0ecf33b0d39a8a5/langsmith-0.4.2.tar.gz", hash = "sha256:51df086a9ae17ffa16538f52ef3bb8b3d85b0e52c84958980553cb6cadd9e565", size = 352208, upload-time = "2025-06-25T11:29:00.408Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c8/8d2e0fc438d2d3d8d4300f7684ea30a754344ed00d7ba9cc2705241d2a5f/langsmith-0.4.4.tar.gz", hash = "sha256:70c53bbff24a7872e88e6fa0af98270f4986a6e364f9e85db1cc5636defa4d66", size = 352105, upload-time = "2025-06-27T19:20:36.207Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/06/fdcc2e8de8934595e7fd7b3f7c93065ff25c03ddeda566823882379b66b2/langsmith-0.4.2-py3-none-any.whl", hash = "sha256:2b1a3f889e134546dc5d67e23e5e8c6be5f91fd86827276ac874e3a25a04498a", size = 367715, upload-time = "2025-06-25T11:28:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/1d/33/a3337eb70d795495a299a1640d7a75f17fb917155a64309b96106e7b9452/langsmith-0.4.4-py3-none-any.whl", hash = "sha256:014c68329bd085bd6c770a6405c61bb6881f82eb554ce8c4d1984b0035fd1716", size = 367687, upload-time = "2025-06-27T19:20:33.839Z" }, ] [[package]] @@ -655,46 +677,47 @@ wheels = [ [[package]] name = "mypy" -version = "1.15.0" +version = "1.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, + { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload-time = "2025-02-05T03:50:34.655Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433, upload-time = "2025-02-05T03:49:29.145Z" }, - { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472, upload-time = "2025-02-05T03:49:16.986Z" }, - { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424, upload-time = "2025-02-05T03:49:46.908Z" }, - { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450, upload-time = "2025-02-05T03:50:05.89Z" }, - { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765, upload-time = "2025-02-05T03:49:33.56Z" }, - { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701, upload-time = "2025-02-05T03:49:38.981Z" }, - { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload-time = "2025-02-05T03:50:17.287Z" }, - { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload-time = "2025-02-05T03:49:51.21Z" }, - { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload-time = "2025-02-05T03:50:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload-time = "2025-02-05T03:49:42.408Z" }, - { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload-time = "2025-02-05T03:49:07.707Z" }, - { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload-time = "2025-02-05T03:49:54.581Z" }, - { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload-time = "2025-02-05T03:50:28.25Z" }, - { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload-time = "2025-02-05T03:50:13.411Z" }, - { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload-time = "2025-02-05T03:50:31.421Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload-time = "2025-02-05T03:48:48.705Z" }, - { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload-time = "2025-02-05T03:49:03.628Z" }, - { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload-time = "2025-02-05T03:50:00.313Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592, upload-time = "2025-02-05T03:48:55.789Z" }, - { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611, upload-time = "2025-02-05T03:48:44.581Z" }, - { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443, upload-time = "2025-02-05T03:49:25.514Z" }, - { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541, upload-time = "2025-02-05T03:49:57.623Z" }, - { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348, upload-time = "2025-02-05T03:48:52.361Z" }, - { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648, upload-time = "2025-02-05T03:49:11.395Z" }, - { url = "https://files.pythonhosted.org/packages/5a/fa/79cf41a55b682794abe71372151dbbf856e3008f6767057229e6649d294a/mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078", size = 10737129, upload-time = "2025-02-05T03:50:24.509Z" }, - { url = "https://files.pythonhosted.org/packages/d3/33/dd8feb2597d648de29e3da0a8bf4e1afbda472964d2a4a0052203a6f3594/mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba", size = 9856335, upload-time = "2025-02-05T03:49:36.398Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b5/74508959c1b06b96674b364ffeb7ae5802646b32929b7701fc6b18447592/mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5", size = 11611935, upload-time = "2025-02-05T03:49:14.154Z" }, - { url = "https://files.pythonhosted.org/packages/6c/53/da61b9d9973efcd6507183fdad96606996191657fe79701b2c818714d573/mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b", size = 12365827, upload-time = "2025-02-05T03:48:59.458Z" }, - { url = "https://files.pythonhosted.org/packages/c1/72/965bd9ee89540c79a25778cc080c7e6ef40aa1eeac4d52cec7eae6eb5228/mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2", size = 12541924, upload-time = "2025-02-05T03:50:03.12Z" }, - { url = "https://files.pythonhosted.org/packages/46/d0/f41645c2eb263e6c77ada7d76f894c580c9ddb20d77f0c24d34273a4dab2/mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980", size = 9271176, upload-time = "2025-02-05T03:50:10.86Z" }, - { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload-time = "2025-02-05T03:50:08.348Z" }, + { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, + { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, + { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, + { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, + { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, + { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, + { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, + { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, + { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, + { url = "https://files.pythonhosted.org/packages/49/5e/ed1e6a7344005df11dfd58b0fdd59ce939a0ba9f7ed37754bf20670b74db/mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069", size = 10959511, upload-time = "2025-06-16T16:47:21.945Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/a7cbc2541e91fe04f43d9e4577264b260fecedb9bccb64ffb1a34b7e6c22/mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da", size = 10075555, upload-time = "2025-06-16T16:50:14.084Z" }, + { url = "https://files.pythonhosted.org/packages/93/f7/c62b1e31a32fbd1546cca5e0a2e5f181be5761265ad1f2e94f2a306fa906/mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c", size = 11874169, upload-time = "2025-06-16T16:49:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/c8/15/db580a28034657fb6cb87af2f8996435a5b19d429ea4dcd6e1c73d418e60/mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383", size = 12610060, upload-time = "2025-06-16T16:34:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/ec/78/c17f48f6843048fa92d1489d3095e99324f2a8c420f831a04ccc454e2e51/mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40", size = 12875199, upload-time = "2025-06-16T16:35:14.448Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d6/ed42167d0a42680381653fd251d877382351e1bd2c6dd8a818764be3beb1/mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b", size = 9487033, upload-time = "2025-06-16T16:49:57.907Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, ] [[package]] @@ -787,50 +810,50 @@ wheels = [ [[package]] name = "ormsgpack" -version = "1.9.1" +version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/a7/462cf8ff5e29241868b82d3a5ec124d690eb6a6a5c6fa5bb1367b839e027/ormsgpack-1.9.1.tar.gz", hash = "sha256:3da6e63d82565e590b98178545e64f0f8506137b92bd31a2d04fd7c82baf5794", size = 56887, upload-time = "2025-03-28T07:14:38.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/36/44eed5ef8ce93cded76a576780bab16425ce7876f10d3e2e6265e46c21ea/ormsgpack-1.10.0.tar.gz", hash = "sha256:7f7a27efd67ef22d7182ec3b7fa7e9d147c3ad9be2a24656b23c989077e08b16", size = 58629, upload-time = "2025-05-24T19:07:53.944Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/32/5f504c0695ff96aaaf0452bee522d79b5a3ee809f22fd77fdb0dd5756d86/ormsgpack-1.9.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f1f804fd9c0fd84213a6022c34172f82323b34afa7052a4af18797582cf56365", size = 382793, upload-time = "2025-03-28T07:13:32.067Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c6/64fe1270271b61495611f1d3068baedb57d76e0f93ce7156f3763fb79b32/ormsgpack-1.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eab5cec99c46276b37071d570aab98603f3d0309b3818da3247eb64bb95e5cfc", size = 213974, upload-time = "2025-03-28T07:13:33.92Z" }, - { url = "https://files.pythonhosted.org/packages/13/56/6666d6a9b82c7d2021fce6823ff823bc373a4e7280979c1b453317678fbc/ormsgpack-1.9.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c12c6bb30e6df6fc0213b77f0a5e143f371d618be2e8eb4d555340ce01c6900", size = 217200, upload-time = "2025-03-28T07:13:35.793Z" }, - { url = "https://files.pythonhosted.org/packages/5f/fb/b844ed1e69d8615163525a8403d7abd3548b3fbfa0f3a973808f36145a0f/ormsgpack-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994d4bbb7ee333264a3e55e30ccee063df6635d785f21a08bf52f67821454a51", size = 223648, upload-time = "2025-03-28T07:13:37.538Z" }, - { url = "https://files.pythonhosted.org/packages/f2/26/c40c3e300f9c61a5ed7a6921656dd0d2907a8174936e1e643677585e497c/ormsgpack-1.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a668a584cf4bb6e1a6ef5a35f3f0d0fdae80cfb7237344ad19a50cce8c79317b", size = 394197, upload-time = "2025-03-28T07:13:39.682Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4c/7a4ae187f18e7abf7ab0662b473264a60a5aa4e9bff266f541a8855df163/ormsgpack-1.9.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:aaf77699203822638014c604d100f132583844d4fd01eb639a2266970c02cfdf", size = 480550, upload-time = "2025-03-28T07:13:41.156Z" }, - { url = "https://files.pythonhosted.org/packages/b1/33/5c465dfd5571f816835bb9e371987bf081b529c64ef28a72d18b0b59902d/ormsgpack-1.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:003d7e1992b447898caf25a820b3037ec68a57864b3e2f34b64693b7d60a9984", size = 396955, upload-time = "2025-03-28T07:13:43.185Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fd/8f64f477b5c6d66e9c6343d7d3f32d7063ba20ab151dd36884e6504899ab/ormsgpack-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:67fefc77e4ba9469f79426769eb4c78acf21f22bef3ab1239a72dd728036ffc2", size = 125102, upload-time = "2025-03-28T07:13:44.878Z" }, - { url = "https://files.pythonhosted.org/packages/d8/3b/388e7915a28db6ab3daedfd4937bd7b063c50dd1543068daa31c0a3b70ed/ormsgpack-1.9.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:16eaf32c33ab4249e242181d59e2509b8e0330d6f65c1d8bf08c3dea38fd7c02", size = 382794, upload-time = "2025-03-28T07:13:46.72Z" }, - { url = "https://files.pythonhosted.org/packages/0f/b4/3f4afba058822bf69b274e0defe507056be0340e65363c3ebcd312b01b84/ormsgpack-1.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c70f2e5b2f9975536e8f7936a9721601dc54febe363d2d82f74c9b31d4fe1c65", size = 213974, upload-time = "2025-03-28T07:13:48.488Z" }, - { url = "https://files.pythonhosted.org/packages/bf/be/f0e21366d51b6e28fc3a55425be6a125545370d3479bf25be081e83ee236/ormsgpack-1.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:17c9e18b07d69e3db2e0f8af4731040175e11bdfde78ad8e28126e9e66ec5167", size = 217200, upload-time = "2025-03-28T07:13:50.449Z" }, - { url = "https://files.pythonhosted.org/packages/cc/90/67a23c1c880a6e5552acb45f9555b642528f89c8bcf75283a2ea64ef7175/ormsgpack-1.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73538d749096bb6470328601a2be8f7bdec28849ec6fd19595c232a5848d7124", size = 223649, upload-time = "2025-03-28T07:13:52.281Z" }, - { url = "https://files.pythonhosted.org/packages/80/ad/116c1f970b5b4453e4faa52645517a2e5eaf1ab385ba09a5c54253d07d0e/ormsgpack-1.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:827ff71de228cfd6d07b9d6b47911aa61b1e8dc995dec3caf8fdcdf4f874bcd0", size = 394200, upload-time = "2025-03-28T07:13:53.691Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a2/b224a5ef193628a15205e473179276b87e8290d321693e4934a05cbd6ccf/ormsgpack-1.9.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:7307f808b3df282c8e8ed92c6ebceeb3eea3d8eeec808438f3f212226b25e217", size = 480551, upload-time = "2025-03-28T07:13:55.442Z" }, - { url = "https://files.pythonhosted.org/packages/b5/f4/a0f528196af6ab46e6c3f3051cf7403016bdc7b7d3e673ea5b04b145be98/ormsgpack-1.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f30aad7fb083bed1c540a3c163c6a9f63a94e3c538860bf8f13386c29b560ad5", size = 396959, upload-time = "2025-03-28T07:13:56.907Z" }, - { url = "https://files.pythonhosted.org/packages/bc/6b/60c6f4787e3e93f5eb34fccb163753a8771465983a579e3405152f2422fd/ormsgpack-1.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:829a1b4c5bc3c38ece0c55cf91ebc09c3b987fceb24d3f680c2bcd03fd3789a4", size = 125100, upload-time = "2025-03-28T07:13:58.607Z" }, - { url = "https://files.pythonhosted.org/packages/dd/f1/155a598cc8030526ccaaf91ba4d61530f87900645559487edba58b0a90a2/ormsgpack-1.9.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1ede445fc3fdba219bb0e0d1f289df26a9c7602016b7daac6fafe8fe4e91548f", size = 383225, upload-time = "2025-03-28T07:14:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/23/1c/ef3097ba550fad55c79525f461febdd4e0d9cc18d065248044536f09488e/ormsgpack-1.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db50b9f918e25b289114312ed775794d0978b469831b992bdc65bfe20b91fe30", size = 214056, upload-time = "2025-03-28T07:14:02.861Z" }, - { url = "https://files.pythonhosted.org/packages/27/77/64d0da25896b2cbb99505ca518c109d7dd1964d7fde14c10943731738b60/ormsgpack-1.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c7d8fc58e4333308f58ec720b1ee6b12b2b3fe2d2d8f0766ab751cb351e8757", size = 217339, upload-time = "2025-03-28T07:14:04.278Z" }, - { url = "https://files.pythonhosted.org/packages/6c/10/c3a7fd0a0068b0bb52cccbfeb5656db895d69e895a3abbc210c4b3f98ff8/ormsgpack-1.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeee6d08c040db265cb8563444aba343ecb32cbdbe2414a489dcead9f70c6765", size = 223816, upload-time = "2025-03-28T07:14:05.969Z" }, - { url = "https://files.pythonhosted.org/packages/43/e7/aee1238dba652f2116c2523d36fd1c5f9775436032be5c233108fd2a1415/ormsgpack-1.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2fbb8181c198bdc413a4e889e5200f010724eea4b6d5a9a7eee2df039ac04aca", size = 394287, upload-time = "2025-03-28T07:14:07.635Z" }, - { url = "https://files.pythonhosted.org/packages/c7/09/1b452a92376f29d7a2da7c18fb01cf09978197a8eccbb8b204e72fd5a970/ormsgpack-1.9.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:16488f094ac0e2250cceea6caf72962614aa432ee11dd57ef45e1ad25ece3eff", size = 480709, upload-time = "2025-03-28T07:14:09.41Z" }, - { url = "https://files.pythonhosted.org/packages/de/13/7fa9fee5a73af8a73a42bf8c2e69489605714f65f5a41454400a05e84a3b/ormsgpack-1.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:422d960bfd6ad88be20794f50ec7953d8f7a0f2df60e19d0e8feb994e2ed64ee", size = 397247, upload-time = "2025-03-28T07:14:11.261Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2d/2e87cb28110db0d3bb750edd4d8719b5068852a2eef5e96b0bf376bb8a81/ormsgpack-1.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:e6e2f9eab527cf43fb4a4293e493370276b1c8716cf305689202d646c6a782ef", size = 125368, upload-time = "2025-03-28T07:14:12.665Z" }, - { url = "https://files.pythonhosted.org/packages/b8/54/0390d5d092831e4df29dbafe32402891fc14b3e6ffe5a644b16cbbc9d9bc/ormsgpack-1.9.1-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ac61c18d9dd085e8519b949f7e655f7fb07909fd09c53b4338dd33309012e289", size = 383226, upload-time = "2025-03-28T07:14:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/47/64/8b15d262d1caefead8fb22ec144f5ff7d9505fc31c22bc34598053d46fbe/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134840b8c6615da2c24ce77bd12a46098015c808197a9995c7a2d991e1904eec", size = 214057, upload-time = "2025-03-28T07:14:15.307Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/65823609266bad4d5ed29ea753d24a3bdb01c7edaf923da80967fc31f9c5/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38fd42618f626394b2c7713c5d4bcbc917254e9753d5d4cde460658b51b11a74", size = 217340, upload-time = "2025-03-28T07:14:16.69Z" }, - { url = "https://files.pythonhosted.org/packages/a0/51/e535c50f7f87b49110233647f55300d7975139ef5e51f1adb4c55f58c124/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d36397333ad07b9eba4c2e271fa78951bd81afc059c85a6e9f6c0eb2de07cda", size = 223815, upload-time = "2025-03-28T07:14:18.651Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ee/393e4a6de2a62124bf589602648f295a9fb3907a0e2fe80061b88899d072/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:603063089597917d04e4c1b1d53988a34f7dc2ff1a03adcfd1cf4ae966d5fba6", size = 394287, upload-time = "2025-03-28T07:14:20.569Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d8/e56d7c3cb73a0e533e3e2a21ae5838b2aa36a9dac1ca9c861af6bae5a369/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:94bbf2b185e0cb721ceaba20e64b7158e6caf0cecd140ca29b9f05a8d5e91e2f", size = 480707, upload-time = "2025-03-28T07:14:22.006Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e0/6a3c6a6dc98583a721c54b02f5195bde8f801aebdeda9b601fa2ab30ad39/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38f380b1e8c96a712eb302b9349347385161a8e29046868ae2bfdfcb23e2692", size = 397246, upload-time = "2025-03-28T07:14:23.868Z" }, - { url = "https://files.pythonhosted.org/packages/b0/60/0ee5d790f13507e1f75ac21fc82dc1ef29afe1f520bd0f249d65b2f4839b/ormsgpack-1.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:a4bc63fb30db94075611cedbbc3d261dd17cf2aa8ff75a0fd684cd45ca29cb1b", size = 125371, upload-time = "2025-03-28T07:14:25.176Z" }, - { url = "https://files.pythonhosted.org/packages/85/02/ac4a2263c9aad0d455f240e1bdd41b443e5452257cf13bc188177b0dfd1f/ormsgpack-1.9.1-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e95909248bece8e88a310a913838f17ff5a39190aa4e61de909c3cd27f59744b", size = 382789, upload-time = "2025-03-28T07:14:26.774Z" }, - { url = "https://files.pythonhosted.org/packages/81/6f/e50d070ae3a6aa7cb50849d0796ac6d72c0f8f01d5a42438c9567ab352e3/ormsgpack-1.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3939188810c5c641d6b207f29994142ae2b1c70534f7839bbd972d857ac2072", size = 213967, upload-time = "2025-03-28T07:14:28.473Z" }, - { url = "https://files.pythonhosted.org/packages/55/1d/379734bca4f2d71ce11c7096d85276280cf13d1bb7243bf809b171c25cda/ormsgpack-1.9.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b6476344a585aea00a2acc9fd07355bf2daac04062cfdd480fa83ec3e2403b", size = 217183, upload-time = "2025-03-28T07:14:29.822Z" }, - { url = "https://files.pythonhosted.org/packages/9a/f6/036a44ada8659b1729db5f20ba50dc1945a84a50cd4fa6b3a74d0f16fab9/ormsgpack-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d8b9d53da82b31662ce5a3834b65479cf794a34befb9fc50baa51518383250", size = 223647, upload-time = "2025-03-28T07:14:31.487Z" }, - { url = "https://files.pythonhosted.org/packages/b9/61/5c8671ab3b7cac21076169972bad9f4faa1fdda1f70e0ae78b365894b164/ormsgpack-1.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3933d4b0c0d404ee234dbc372836d6f2d2f4b6330c2a2fb9709ba4eaebfae7ba", size = 394232, upload-time = "2025-03-28T07:14:33.254Z" }, - { url = "https://files.pythonhosted.org/packages/7b/70/6e9ba8c8c405dee5dffd86edf1188ef1a116421598e18df89dac0c499aae/ormsgpack-1.9.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:f824e94a7969f0aee9a6847ec232cf731a03b8734951c2a774dd4762308ea2d2", size = 480582, upload-time = "2025-03-28T07:14:34.704Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e1/2113022dd9236cea13f0ba850a5f8a640c0c9e3b07bfbbaf01409190068b/ormsgpack-1.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c1f3f2295374020f9650e4aa7af6403ff016a0d92778b4a48bb3901fd801232d", size = 396952, upload-time = "2025-03-28T07:14:36.128Z" }, - { url = "https://files.pythonhosted.org/packages/01/d4/58ca5de3124ac975dae1a96a475c3cb9ed70c70dfba39fd4ceca53838ee6/ormsgpack-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:92eb1b4f7b168da47f547329b4b58d16d8f19508a97ce5266567385d42d81968", size = 125107, upload-time = "2025-03-28T07:14:37.621Z" }, + { url = "https://files.pythonhosted.org/packages/fc/74/c2dd5daf069e3798d09d5746000f9b210de04df83834e5cb47f0ace51892/ormsgpack-1.10.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8a52c7ce7659459f3dc8dec9fd6a6c76f855a0a7e2b61f26090982ac10b95216", size = 376280, upload-time = "2025-05-24T19:06:51.3Z" }, + { url = "https://files.pythonhosted.org/packages/78/7b/30ff4bffb709e8a242005a8c4d65714fd96308ad640d31cff1b85c0d8cc4/ormsgpack-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:060f67fe927582f4f63a1260726d019204b72f460cf20930e6c925a1d129f373", size = 204335, upload-time = "2025-05-24T19:06:53.442Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3f/c95b7d142819f801a0acdbd04280e8132e43b6e5a8920173e8eb92ea0e6a/ormsgpack-1.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7058ef6092f995561bf9f71d6c9a4da867b6cc69d2e94cb80184f579a3ceed5", size = 215373, upload-time = "2025-05-24T19:06:55.153Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1a/e30f4bcf386db2015d1686d1da6110c95110294d8ea04f86091dd5eb3361/ormsgpack-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f6f3509c1b0e51b15552d314b1d409321718122e90653122ce4b997f01453a", size = 216469, upload-time = "2025-05-24T19:06:56.555Z" }, + { url = "https://files.pythonhosted.org/packages/96/fc/7e44aeade22b91883586f45b7278c118fd210834c069774891447f444fc9/ormsgpack-1.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c1edafd5c72b863b1f875ec31c529f09c872a5ff6fe473b9dfaf188ccc3227", size = 384590, upload-time = "2025-05-24T19:06:58.286Z" }, + { url = "https://files.pythonhosted.org/packages/ec/78/f92c24e8446697caa83c122f10b6cf5e155eddf81ce63905c8223a260482/ormsgpack-1.10.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c780b44107a547a9e9327270f802fa4d6b0f6667c9c03c3338c0ce812259a0f7", size = 478891, upload-time = "2025-05-24T19:07:00.126Z" }, + { url = "https://files.pythonhosted.org/packages/5a/75/87449690253c64bea2b663c7c8f2dbc9ad39d73d0b38db74bdb0f3947b16/ormsgpack-1.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:137aab0d5cdb6df702da950a80405eb2b7038509585e32b4e16289604ac7cb84", size = 390121, upload-time = "2025-05-24T19:07:01.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/cc/c83257faf3a5169ec29dd87121317a25711da9412ee8c1e82f2e1a00c0be/ormsgpack-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e666cb63030538fa5cd74b1e40cb55b6fdb6e2981f024997a288bf138ebad07", size = 121196, upload-time = "2025-05-24T19:07:03.47Z" }, + { url = "https://files.pythonhosted.org/packages/30/27/7da748bc0d7d567950a378dee5a32477ed5d15462ab186918b5f25cac1ad/ormsgpack-1.10.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4bb7df307e17b36cbf7959cd642c47a7f2046ae19408c564e437f0ec323a7775", size = 376275, upload-time = "2025-05-24T19:07:05.128Z" }, + { url = "https://files.pythonhosted.org/packages/7b/65/c082cc8c74a914dbd05af0341c761c73c3d9960b7432bbf9b8e1e20811af/ormsgpack-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8817ae439c671779e1127ee62f0ac67afdeaeeacb5f0db45703168aa74a2e4af", size = 204335, upload-time = "2025-05-24T19:07:06.423Z" }, + { url = "https://files.pythonhosted.org/packages/46/62/17ef7e5d9766c79355b9c594cc9328c204f1677bc35da0595cc4e46449f0/ormsgpack-1.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f345f81e852035d80232e64374d3a104139d60f8f43c6c5eade35c4bac5590e", size = 215372, upload-time = "2025-05-24T19:07:08.149Z" }, + { url = "https://files.pythonhosted.org/packages/4e/92/7c91e8115fc37e88d1a35e13200fda3054ff5d2e5adf017345e58cea4834/ormsgpack-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21de648a1c7ef692bdd287fb08f047bd5371d7462504c0a7ae1553c39fee35e3", size = 216470, upload-time = "2025-05-24T19:07:09.903Z" }, + { url = "https://files.pythonhosted.org/packages/2c/86/ce053c52e2517b90e390792d83e926a7a523c1bce5cc63d0a7cd05ce6cf6/ormsgpack-1.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3a7d844ae9cbf2112c16086dd931b2acefce14cefd163c57db161170c2bfa22b", size = 384591, upload-time = "2025-05-24T19:07:11.24Z" }, + { url = "https://files.pythonhosted.org/packages/07/e8/2ad59f2ab222c6029e500bc966bfd2fe5cb099f8ab6b7ebeb50ddb1a6fe5/ormsgpack-1.10.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e4d80585403d86d7f800cf3d0aafac1189b403941e84e90dd5102bb2b92bf9d5", size = 478892, upload-time = "2025-05-24T19:07:13.147Z" }, + { url = "https://files.pythonhosted.org/packages/f4/73/f55e4b47b7b18fd8e7789680051bf830f1e39c03f1d9ed993cd0c3e97215/ormsgpack-1.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:da1de515a87e339e78a3ccf60e39f5fb740edac3e9e82d3c3d209e217a13ac08", size = 390122, upload-time = "2025-05-24T19:07:14.557Z" }, + { url = "https://files.pythonhosted.org/packages/f7/87/073251cdb93d4c6241748568b3ad1b2a76281fb2002eed16a3a4043d61cf/ormsgpack-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:57c4601812684024132cbb32c17a7d4bb46ffc7daf2fddf5b697391c2c4f142a", size = 121197, upload-time = "2025-05-24T19:07:15.981Z" }, + { url = "https://files.pythonhosted.org/packages/99/95/f3ab1a7638f6aa9362e87916bb96087fbbc5909db57e19f12ad127560e1e/ormsgpack-1.10.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4e159d50cd4064d7540e2bc6a0ab66eab70b0cc40c618b485324ee17037527c0", size = 376806, upload-time = "2025-05-24T19:07:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2b/42f559f13c0b0f647b09d749682851d47c1a7e48308c43612ae6833499c8/ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb47c85f3a866e29279d801115b554af0fefc409e2ed8aa90aabfa77efe5cc6", size = 204433, upload-time = "2025-05-24T19:07:18.569Z" }, + { url = "https://files.pythonhosted.org/packages/45/42/1ca0cb4d8c80340a89a4af9e6d8951fb8ba0d076a899d2084eadf536f677/ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c28249574934534c9bd5dce5485c52f21bcea0ee44d13ece3def6e3d2c3798b5", size = 215547, upload-time = "2025-05-24T19:07:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/0a/38/184a570d7c44c0260bc576d1daaac35b2bfd465a50a08189518505748b9a/ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1957dcadbb16e6a981cd3f9caef9faf4c2df1125e2a1b702ee8236a55837ce07", size = 216746, upload-time = "2025-05-24T19:07:21.83Z" }, + { url = "https://files.pythonhosted.org/packages/69/2f/1aaffd08f6b7fdc2a57336a80bdfb8df24e6a65ada5aa769afecfcbc6cc6/ormsgpack-1.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b29412558c740bf6bac156727aa85ac67f9952cd6f071318f29ee72e1a76044", size = 384783, upload-time = "2025-05-24T19:07:23.674Z" }, + { url = "https://files.pythonhosted.org/packages/a9/63/3e53d6f43bb35e00c98f2b8ab2006d5138089ad254bc405614fbf0213502/ormsgpack-1.10.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6933f350c2041ec189fe739f0ba7d6117c8772f5bc81f45b97697a84d03020dd", size = 479076, upload-time = "2025-05-24T19:07:25.047Z" }, + { url = "https://files.pythonhosted.org/packages/b8/19/fa1121b03b61402bb4d04e35d164e2320ef73dfb001b57748110319dd014/ormsgpack-1.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a86de06d368fcc2e58b79dece527dc8ca831e0e8b9cec5d6e633d2777ec93d0", size = 390447, upload-time = "2025-05-24T19:07:26.568Z" }, + { url = "https://files.pythonhosted.org/packages/b0/0d/73143ecb94ac4a5dcba223402139240a75dee0cc6ba8a543788a5646407a/ormsgpack-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:35fa9f81e5b9a0dab42e09a73f7339ecffdb978d6dbf9deb2ecf1e9fc7808722", size = 121401, upload-time = "2025-05-24T19:07:28.308Z" }, + { url = "https://files.pythonhosted.org/packages/61/f8/ec5f4e03268d0097545efaab2893aa63f171cf2959cb0ea678a5690e16a1/ormsgpack-1.10.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d816d45175a878993b7372bd5408e0f3ec5a40f48e2d5b9d8f1cc5d31b61f1f", size = 376806, upload-time = "2025-05-24T19:07:29.555Z" }, + { url = "https://files.pythonhosted.org/packages/c1/19/b3c53284aad1e90d4d7ed8c881a373d218e16675b8b38e3569d5b40cc9b8/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90345ccb058de0f35262893751c603b6376b05f02be2b6f6b7e05d9dd6d5643", size = 204433, upload-time = "2025-05-24T19:07:30.977Z" }, + { url = "https://files.pythonhosted.org/packages/09/0b/845c258f59df974a20a536c06cace593698491defdd3d026a8a5f9b6e745/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144b5e88f1999433e54db9d637bae6fe21e935888be4e3ac3daecd8260bd454e", size = 215549, upload-time = "2025-05-24T19:07:32.345Z" }, + { url = "https://files.pythonhosted.org/packages/61/56/57fce8fb34ca6c9543c026ebebf08344c64dbb7b6643d6ddd5355d37e724/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2190b352509d012915921cca76267db136cd026ddee42f1b0d9624613cc7058c", size = 216747, upload-time = "2025-05-24T19:07:34.075Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3f/655b5f6a2475c8d209f5348cfbaaf73ce26237b92d79ef2ad439407dd0fa/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:86fd9c1737eaba43d3bb2730add9c9e8b5fbed85282433705dd1b1e88ea7e6fb", size = 384785, upload-time = "2025-05-24T19:07:35.83Z" }, + { url = "https://files.pythonhosted.org/packages/4b/94/687a0ad8afd17e4bce1892145d6a1111e58987ddb176810d02a1f3f18686/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:33afe143a7b61ad21bb60109a86bb4e87fec70ef35db76b89c65b17e32da7935", size = 479076, upload-time = "2025-05-24T19:07:37.533Z" }, + { url = "https://files.pythonhosted.org/packages/c8/34/68925232e81e0e062a2f0ac678f62aa3b6f7009d6a759e19324dbbaebae7/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f23d45080846a7b90feabec0d330a9cc1863dc956728412e4f7986c80ab3a668", size = 390446, upload-time = "2025-05-24T19:07:39.469Z" }, + { url = "https://files.pythonhosted.org/packages/12/ad/f4e1a36a6d1714afb7ffb74b3ababdcb96529cf4e7a216f9f7c8eda837b6/ormsgpack-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:534d18acb805c75e5fba09598bf40abe1851c853247e61dda0c01f772234da69", size = 121399, upload-time = "2025-05-24T19:07:40.854Z" }, + { url = "https://files.pythonhosted.org/packages/75/8f/bb80469db9d5b10708cba6997463d140486ca7053a5d18f99b5739cfecf7/ormsgpack-1.10.0-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:efdb25cf6d54085f7ae557268d59fd2d956f1a09a340856e282d2960fe929f32", size = 376272, upload-time = "2025-05-24T19:07:42.16Z" }, + { url = "https://files.pythonhosted.org/packages/08/9c/48f714ed3d5a153f25e3b490496e6ba214aee265a82be1b61e39019ea146/ormsgpack-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddfcb30d4b1be2439836249d675f297947f4fb8efcd3eeb6fd83021d773cadc4", size = 204314, upload-time = "2025-05-24T19:07:43.444Z" }, + { url = "https://files.pythonhosted.org/packages/27/42/7f9edf6e5511120b5304c76c5d3a8b4719ff927555a6dba41b6f9d041b30/ormsgpack-1.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee0944b6ccfd880beb1ca29f9442a774683c366f17f4207f8b81c5e24cadb453", size = 215386, upload-time = "2025-05-24T19:07:45.232Z" }, + { url = "https://files.pythonhosted.org/packages/40/87/41e14485857fbe4ed5a530677fe60dd6910a254825c0b1cb5b04baaa4be0/ormsgpack-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35cdff6a0d3ba04e40a751129763c3b9b57a602c02944138e4b760ec99ae80a1", size = 216466, upload-time = "2025-05-24T19:07:46.548Z" }, + { url = "https://files.pythonhosted.org/packages/cb/68/769fa1c721d8aa6799c0ce98b1711ae57de3e6379b554ebf9a11be4c62ff/ormsgpack-1.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:599ccdabc19c618ef5de6e6f2e7f5d48c1f531a625fa6772313b8515bc710681", size = 384600, upload-time = "2025-05-24T19:07:47.945Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f9/b57fd387fe16753783a3cea0ed2471c727bbed4356d8a08e3f0340251870/ormsgpack-1.10.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:bf46f57da9364bd5eefd92365c1b78797f56c6f780581eecd60cd7b367f9b4d3", size = 478888, upload-time = "2025-05-24T19:07:49.801Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0f/464cdfa7f9ee817c2d94485880b6c3c4b9f22df9fcbf21c303bbfebcb3ed/ormsgpack-1.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b796f64fdf823dedb1e35436a4a6f889cf78b1aa42d3097c66e5adfd8c3bd72d", size = 390118, upload-time = "2025-05-24T19:07:51.193Z" }, + { url = "https://files.pythonhosted.org/packages/ad/03/b9146dff5458def4c0a2b1e35c1c24e4d5e8083899aa0718b6eccba39317/ormsgpack-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:106253ac9dc08520951e556b3c270220fcb8b4fef0d30b71eedac4befa4de749", size = 121199, upload-time = "2025-05-24T19:07:52.639Z" }, ] [[package]] @@ -843,12 +866,21 @@ wheels = [ ] [[package]] -name = "pluggy" -version = "1.5.0" +name = "pathspec" +version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] @@ -862,7 +894,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.4" +version = "2.11.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types", marker = "python_full_version >= '3.11'" }, @@ -870,9 +902,9 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, { name = "typing-inspection", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540, upload-time = "2025-04-29T20:38:55.02Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, ] [[package]] @@ -984,6 +1016,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/29/3cade8a924a61f60ccfa10842f75eb12787e1440e2b8660ceffeb26685e7/pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27", size = 2066661, upload-time = "2025-04-23T18:33:49.995Z" }, ] +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + [[package]] name = "pyjwt" version = "2.10.1" @@ -995,7 +1036,7 @@ wheels = [ [[package]] name = "pytest" -version = "7.4.4" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1003,35 +1044,37 @@ dependencies = [ { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, + { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116, upload-time = "2023-12-31T12:00:18.035Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload-time = "2023-12-31T12:00:13.963Z" }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] [[package]] name = "pytest-asyncio" -version = "0.21.2" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/53/57663d99acaac2fcdafdc697e52a9b1b7d6fcf36616281ff9768a44e7ff3/pytest_asyncio-0.21.2.tar.gz", hash = "sha256:d67738fc232b94b326b9d060750beb16e0074210b98dd8b58a5239fa2a154f45", size = 30656, upload-time = "2024-04-29T13:23:24.738Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload-time = "2025-05-26T04:54:40.484Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/ce/1e4b53c213dce25d6e8b163697fbce2d43799d76fa08eea6ad270451c370/pytest_asyncio-0.21.2-py3-none-any.whl", hash = "sha256:ab664c88bb7998f711d8039cacd4884da6430886ae8bbd4eded552ed2004f16b", size = 13368, upload-time = "2024-04-29T13:23:23.126Z" }, + { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload-time = "2025-05-26T04:54:39.035Z" }, ] [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload-time = "2024-03-21T22:14:04.964Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload-time = "2024-03-21T22:14:02.694Z" }, + { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, ] [[package]] @@ -1048,11 +1091,11 @@ sdist = { url = "https://files.pythonhosted.org/packages/36/47/ab65fc1d682befc31 [[package]] name = "python-dotenv" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, ] [[package]] @@ -1110,7 +1153,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi", marker = "python_full_version >= '3.11'" }, @@ -1118,9 +1161,9 @@ dependencies = [ { name = "idna", marker = "python_full_version >= '3.11'" }, { name = "urllib3", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, ] [[package]] @@ -1137,27 +1180,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.6.9" +version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/0d/6148a48dab5662ca1d5a93b7c0d13c03abd3cc7e2f35db08410e47cef15d/ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2", size = 3095355, upload-time = "2024-10-04T13:40:28.594Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/38/796a101608a90494440856ccfb52b1edae90de0b817e76bfade66b12d320/ruff-0.12.1.tar.gz", hash = "sha256:806bbc17f1104fd57451a98a58df35388ee3ab422e029e8f5cf30aa4af2c138c", size = 4413426, upload-time = "2025-06-26T20:34:14.784Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/8f/f7a0a0ef1818662efb32ed6df16078c95da7a0a3248d64c2410c1e27799f/ruff-0.6.9-py3-none-linux_armv6l.whl", hash = "sha256:064df58d84ccc0ac0fcd63bc3090b251d90e2a372558c0f057c3f75ed73e1ccd", size = 10440526, upload-time = "2024-10-04T13:39:21.747Z" }, - { url = "https://files.pythonhosted.org/packages/8b/69/b179a5faf936a9e2ab45bb412a668e4661eded964ccfa19d533f29463ef6/ruff-0.6.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:140d4b5c9f5fc7a7b074908a78ab8d384dd7f6510402267bc76c37195c02a7ec", size = 10034612, upload-time = "2024-10-04T13:39:26.301Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ef/fd1b4be979c579d191eeac37b5cfc0ec906de72c8bcd8595e2c81bb700c1/ruff-0.6.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53fd8ca5e82bdee8da7f506d7b03a261f24cd43d090ea9db9a1dc59d9313914c", size = 9706197, upload-time = "2024-10-04T13:39:29.297Z" }, - { url = "https://files.pythonhosted.org/packages/29/61/b376d775deb5851cb48d893c568b511a6d3625ef2c129ad5698b64fb523c/ruff-0.6.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645d7d8761f915e48a00d4ecc3686969761df69fb561dd914a773c1a8266e14e", size = 10751855, upload-time = "2024-10-04T13:39:33.175Z" }, - { url = "https://files.pythonhosted.org/packages/13/d7/def9e5f446d75b9a9c19b24231a3a658c075d79163b08582e56fa5dcfa38/ruff-0.6.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eae02b700763e3847595b9d2891488989cac00214da7f845f4bcf2989007d577", size = 10200889, upload-time = "2024-10-04T13:39:36.867Z" }, - { url = "https://files.pythonhosted.org/packages/6c/d6/7f34160818bcb6e84ce293a5966cba368d9112ff0289b273fbb689046047/ruff-0.6.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d5ccc9e58112441de8ad4b29dcb7a86dc25c5f770e3c06a9d57e0e5eba48829", size = 11038678, upload-time = "2024-10-04T13:39:40.428Z" }, - { url = "https://files.pythonhosted.org/packages/13/34/a40ff8ae62fb1b26fb8e6fa7e64bc0e0a834b47317880de22edd6bfb54fb/ruff-0.6.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:417b81aa1c9b60b2f8edc463c58363075412866ae4e2b9ab0f690dc1e87ac1b5", size = 11808682, upload-time = "2024-10-04T13:39:52.141Z" }, - { url = "https://files.pythonhosted.org/packages/2e/6d/25a4386ae4009fc798bd10ba48c942d1b0b3e459b5403028f1214b6dd161/ruff-0.6.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c866b631f5fbce896a74a6e4383407ba7507b815ccc52bcedabb6810fdb3ef7", size = 11330446, upload-time = "2024-10-04T13:39:55.783Z" }, - { url = "https://files.pythonhosted.org/packages/f7/f6/bdf891a9200d692c94ebcd06ae5a2fa5894e522f2c66c2a12dd5d8cb2654/ruff-0.6.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b118afbb3202f5911486ad52da86d1d52305b59e7ef2031cea3425142b97d6f", size = 12483048, upload-time = "2024-10-04T13:39:58.845Z" }, - { url = "https://files.pythonhosted.org/packages/a7/86/96f4252f41840e325b3fa6c48297e661abb9f564bd7dcc0572398c8daa42/ruff-0.6.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67267654edc23c97335586774790cde402fb6bbdb3c2314f1fc087dee320bfa", size = 10936855, upload-time = "2024-10-04T13:40:01.818Z" }, - { url = "https://files.pythonhosted.org/packages/45/87/801a52d26c8dbf73424238e9908b9ceac430d903c8ef35eab1b44fcfa2bd/ruff-0.6.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3ef0cc774b00fec123f635ce5c547dac263f6ee9fb9cc83437c5904183b55ceb", size = 10713007, upload-time = "2024-10-04T13:40:05.384Z" }, - { url = "https://files.pythonhosted.org/packages/be/27/6f7161d90320a389695e32b6ebdbfbedde28ccbf52451e4b723d7ce744ad/ruff-0.6.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:12edd2af0c60fa61ff31cefb90aef4288ac4d372b4962c2864aeea3a1a2460c0", size = 10274594, upload-time = "2024-10-04T13:40:08.801Z" }, - { url = "https://files.pythonhosted.org/packages/00/52/dc311775e7b5f5b19831563cb1572ecce63e62681bccc609867711fae317/ruff-0.6.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:55bb01caeaf3a60b2b2bba07308a02fca6ab56233302406ed5245180a05c5625", size = 10608024, upload-time = "2024-10-04T13:40:11.923Z" }, - { url = "https://files.pythonhosted.org/packages/98/b6/be0a1ddcbac65a30c985cf7224c4fce786ba2c51e7efeb5178fe410ed3cf/ruff-0.6.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:925d26471fa24b0ce5a6cdfab1bb526fb4159952385f386bdcc643813d472039", size = 10982085, upload-time = "2024-10-04T13:40:15.539Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a4/c84bc13d0b573cf7bb7d17b16d6d29f84267c92d79b2f478d4ce322e8e72/ruff-0.6.9-py3-none-win32.whl", hash = "sha256:eb61ec9bdb2506cffd492e05ac40e5bc6284873aceb605503d8494180d6fc84d", size = 8522088, upload-time = "2024-10-04T13:40:19.168Z" }, - { url = "https://files.pythonhosted.org/packages/74/be/fc352bd8ca40daae8740b54c1c3e905a7efe470d420a268cd62150248c91/ruff-0.6.9-py3-none-win_amd64.whl", hash = "sha256:785d31851c1ae91f45b3d8fe23b8ae4b5170089021fbb42402d811135f0b7117", size = 9359275, upload-time = "2024-10-04T13:40:22.852Z" }, - { url = "https://files.pythonhosted.org/packages/3e/14/fd026bc74ded05e2351681545a5f626e78ef831f8edce064d61acd2e6ec7/ruff-0.6.9-py3-none-win_arm64.whl", hash = "sha256:a9641e31476d601f83cd602608739a0840e348bda93fec9f1ee816f8b6798b93", size = 8679879, upload-time = "2024-10-04T13:40:25.797Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/3dba52c1d12ab5e78d75bd78ad52fb85a6a1f29cc447c2423037b82bed0d/ruff-0.12.1-py3-none-linux_armv6l.whl", hash = "sha256:6013a46d865111e2edb71ad692fbb8262e6c172587a57c0669332a449384a36b", size = 10305649, upload-time = "2025-06-26T20:33:39.242Z" }, + { url = "https://files.pythonhosted.org/packages/8c/65/dab1ba90269bc8c81ce1d499a6517e28fe6f87b2119ec449257d0983cceb/ruff-0.12.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b3f75a19e03a4b0757d1412edb7f27cffb0c700365e9d6b60bc1b68d35bc89e0", size = 11120201, upload-time = "2025-06-26T20:33:42.207Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3e/2d819ffda01defe857fa2dd4cba4d19109713df4034cc36f06bbf582d62a/ruff-0.12.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9a256522893cb7e92bb1e1153283927f842dea2e48619c803243dccc8437b8be", size = 10466769, upload-time = "2025-06-26T20:33:44.102Z" }, + { url = "https://files.pythonhosted.org/packages/63/37/bde4cf84dbd7821c8de56ec4ccc2816bce8125684f7b9e22fe4ad92364de/ruff-0.12.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:069052605fe74c765a5b4272eb89880e0ff7a31e6c0dbf8767203c1fbd31c7ff", size = 10660902, upload-time = "2025-06-26T20:33:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3a/390782a9ed1358c95e78ccc745eed1a9d657a537e5c4c4812fce06c8d1a0/ruff-0.12.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a684f125a4fec2d5a6501a466be3841113ba6847827be4573fddf8308b83477d", size = 10167002, upload-time = "2025-06-26T20:33:47.81Z" }, + { url = "https://files.pythonhosted.org/packages/6d/05/f2d4c965009634830e97ffe733201ec59e4addc5b1c0efa035645baa9e5f/ruff-0.12.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdecdef753bf1e95797593007569d8e1697a54fca843d78f6862f7dc279e23bd", size = 11751522, upload-time = "2025-06-26T20:33:49.857Z" }, + { url = "https://files.pythonhosted.org/packages/35/4e/4bfc519b5fcd462233f82fc20ef8b1e5ecce476c283b355af92c0935d5d9/ruff-0.12.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:70d52a058c0e7b88b602f575d23596e89bd7d8196437a4148381a3f73fcd5010", size = 12520264, upload-time = "2025-06-26T20:33:52.199Z" }, + { url = "https://files.pythonhosted.org/packages/85/b2/7756a6925da236b3a31f234b4167397c3e5f91edb861028a631546bad719/ruff-0.12.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84d0a69d1e8d716dfeab22d8d5e7c786b73f2106429a933cee51d7b09f861d4e", size = 12133882, upload-time = "2025-06-26T20:33:54.231Z" }, + { url = "https://files.pythonhosted.org/packages/dd/00/40da9c66d4a4d51291e619be6757fa65c91b92456ff4f01101593f3a1170/ruff-0.12.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cc32e863adcf9e71690248607ccdf25252eeeab5193768e6873b901fd441fed", size = 11608941, upload-time = "2025-06-26T20:33:56.202Z" }, + { url = "https://files.pythonhosted.org/packages/91/e7/f898391cc026a77fbe68dfea5940f8213622474cb848eb30215538a2dadf/ruff-0.12.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fd49a4619f90d5afc65cf42e07b6ae98bb454fd5029d03b306bd9e2273d44cc", size = 11602887, upload-time = "2025-06-26T20:33:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/f6/02/0891872fc6aab8678084f4cf8826f85c5d2d24aa9114092139a38123f94b/ruff-0.12.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ed5af6aaaea20710e77698e2055b9ff9b3494891e1b24d26c07055459bb717e9", size = 10521742, upload-time = "2025-06-26T20:34:00.465Z" }, + { url = "https://files.pythonhosted.org/packages/2a/98/d6534322c74a7d47b0f33b036b2498ccac99d8d8c40edadb552c038cecf1/ruff-0.12.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:801d626de15e6bf988fbe7ce59b303a914ff9c616d5866f8c79eb5012720ae13", size = 10149909, upload-time = "2025-06-26T20:34:02.603Z" }, + { url = "https://files.pythonhosted.org/packages/34/5c/9b7ba8c19a31e2b6bd5e31aa1e65b533208a30512f118805371dbbbdf6a9/ruff-0.12.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2be9d32a147f98a1972c1e4df9a6956d612ca5f5578536814372113d09a27a6c", size = 11136005, upload-time = "2025-06-26T20:34:04.723Z" }, + { url = "https://files.pythonhosted.org/packages/dc/34/9bbefa4d0ff2c000e4e533f591499f6b834346025e11da97f4ded21cb23e/ruff-0.12.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:49b7ce354eed2a322fbaea80168c902de9504e6e174fd501e9447cad0232f9e6", size = 11648579, upload-time = "2025-06-26T20:34:06.766Z" }, + { url = "https://files.pythonhosted.org/packages/6f/1c/20cdb593783f8f411839ce749ec9ae9e4298c2b2079b40295c3e6e2089e1/ruff-0.12.1-py3-none-win32.whl", hash = "sha256:d973fa626d4c8267848755bd0414211a456e99e125dcab147f24daa9e991a245", size = 10519495, upload-time = "2025-06-26T20:34:08.718Z" }, + { url = "https://files.pythonhosted.org/packages/cf/56/7158bd8d3cf16394928f47c637d39a7d532268cd45220bdb6cd622985760/ruff-0.12.1-py3-none-win_amd64.whl", hash = "sha256:9e1123b1c033f77bd2590e4c1fe7e8ea72ef990a85d2484351d408224d603013", size = 11547485, upload-time = "2025-06-26T20:34:11.008Z" }, + { url = "https://files.pythonhosted.org/packages/91/d0/6902c0d017259439d6fd2fd9393cea1cfe30169940118b007d5e0ea7e954/ruff-0.12.1-py3-none-win_arm64.whl", hash = "sha256:78ad09a022c64c13cc6077707f036bab0fac8cd7088772dcd1e5be21c5002efc", size = 10691209, upload-time = "2025-06-26T20:34:12.928Z" }, ] [[package]] @@ -1185,23 +1228,24 @@ wheels = [ [[package]] name = "starlette" -version = "0.46.2" +version = "0.47.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846, upload-time = "2025-04-13T13:56:17.942Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/69/662169fdb92fb96ec3eaee218cf540a629d629c86d7993d9651226a6789b/starlette-0.47.1.tar.gz", hash = "sha256:aef012dd2b6be325ffa16698f9dc533614fb1cebd593a906b90dc1025529a79b", size = 2583072, upload-time = "2025-06-21T04:03:17.337Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, + { url = "https://files.pythonhosted.org/packages/82/95/38ef0cd7fa11eaba6a99b3c4f5ac948d8bc6ff199aabd327a29cc000840c/starlette-0.47.1-py3-none-any.whl", hash = "sha256:5e11c9f5c7c3f24959edbf2dffdc01bba860228acf657129467d8a7468591527", size = 72747, upload-time = "2025-06-21T04:03:15.705Z" }, ] [[package]] name = "structlog" -version = "25.3.0" +version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/6a/b0b6d440e429d2267076c4819300d9929563b1da959cf1f68afbcd69fe45/structlog-25.3.0.tar.gz", hash = "sha256:8dab497e6f6ca962abad0c283c46744185e0c9ba900db52a423cb6db99f7abeb", size = 1367514, upload-time = "2025-04-25T16:00:39.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/6e672db4fec07349e7a8a8172c1a6ae235c58679ca29c3f86a61b5e59ff3/structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4", size = 1369138, upload-time = "2025-06-02T08:21:12.971Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/52/7a2c7a317b254af857464da3d60a0d3730c44f912f8c510c76a738a207fd/structlog-25.3.0-py3-none-any.whl", hash = "sha256:a341f5524004c158498c3127eecded091eb67d3a611e7a3093deca30db06e172", size = 68240, upload-time = "2025-04-25T16:00:37.295Z" }, + { url = "https://files.pythonhosted.org/packages/a0/4a/97ee6973e3a73c74c8120d59829c3861ea52210667ec3e7a16045c62b64d/structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c", size = 68720, upload-time = "2025-06-02T08:21:11.43Z" }, ] [[package]] @@ -1263,45 +1307,45 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.13.2" +version = "4.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, ] [[package]] name = "typing-inspection" -version = "0.4.0" +version = "0.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, ] [[package]] name = "uvicorn" -version = "0.34.2" +version = "0.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "python_full_version >= '3.11'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "h11", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, ] [[package]] @@ -1343,83 +1387,118 @@ wheels = [ [[package]] name = "watchfiles" -version = "1.0.5" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/e2/8ed598c42057de7aa5d97c472254af4906ff0a59a66699d426fc9ef795d7/watchfiles-1.0.5.tar.gz", hash = "sha256:b7529b5dcc114679d43827d8c35a07c493ad6f083633d573d81c660abc5979e9", size = 94537, upload-time = "2025-04-08T10:36:26.722Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/4d/d02e6ea147bb7fff5fd109c694a95109612f419abed46548a930e7f7afa3/watchfiles-1.0.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5c40fe7dd9e5f81e0847b1ea64e1f5dd79dd61afbedb57759df06767ac719b40", size = 405632, upload-time = "2025-04-08T10:34:41.832Z" }, - { url = "https://files.pythonhosted.org/packages/60/31/9ee50e29129d53a9a92ccf1d3992751dc56fc3c8f6ee721be1c7b9c81763/watchfiles-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0db396e6003d99bb2d7232c957b5f0b5634bbd1b24e381a5afcc880f7373fb", size = 395734, upload-time = "2025-04-08T10:34:44.236Z" }, - { url = "https://files.pythonhosted.org/packages/ad/8c/759176c97195306f028024f878e7f1c776bda66ccc5c68fa51e699cf8f1d/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b551d4fb482fc57d852b4541f911ba28957d051c8776e79c3b4a51eb5e2a1b11", size = 455008, upload-time = "2025-04-08T10:34:45.617Z" }, - { url = "https://files.pythonhosted.org/packages/55/1a/5e977250c795ee79a0229e3b7f5e3a1b664e4e450756a22da84d2f4979fe/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:830aa432ba5c491d52a15b51526c29e4a4b92bf4f92253787f9726fe01519487", size = 459029, upload-time = "2025-04-08T10:34:46.814Z" }, - { url = "https://files.pythonhosted.org/packages/e6/17/884cf039333605c1d6e296cf5be35fad0836953c3dfd2adb71b72f9dbcd0/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a16512051a822a416b0d477d5f8c0e67b67c1a20d9acecb0aafa3aa4d6e7d256", size = 488916, upload-time = "2025-04-08T10:34:48.571Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e0/bcb6e64b45837056c0a40f3a2db3ef51c2ced19fda38484fa7508e00632c/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe0cbc787770e52a96c6fda6726ace75be7f840cb327e1b08d7d54eadc3bc85", size = 523763, upload-time = "2025-04-08T10:34:50.268Z" }, - { url = "https://files.pythonhosted.org/packages/24/e9/f67e9199f3bb35c1837447ecf07e9830ec00ff5d35a61e08c2cd67217949/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d363152c5e16b29d66cbde8fa614f9e313e6f94a8204eaab268db52231fe5358", size = 502891, upload-time = "2025-04-08T10:34:51.419Z" }, - { url = "https://files.pythonhosted.org/packages/23/ed/a6cf815f215632f5c8065e9c41fe872025ffea35aa1f80499f86eae922db/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee32c9a9bee4d0b7bd7cbeb53cb185cf0b622ac761efaa2eba84006c3b3a614", size = 454921, upload-time = "2025-04-08T10:34:52.67Z" }, - { url = "https://files.pythonhosted.org/packages/92/4c/e14978599b80cde8486ab5a77a821e8a982ae8e2fcb22af7b0886a033ec8/watchfiles-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29c7fd632ccaf5517c16a5188e36f6612d6472ccf55382db6c7fe3fcccb7f59f", size = 631422, upload-time = "2025-04-08T10:34:53.985Z" }, - { url = "https://files.pythonhosted.org/packages/b2/1a/9263e34c3458f7614b657f974f4ee61fd72f58adce8b436e16450e054efd/watchfiles-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e637810586e6fe380c8bc1b3910accd7f1d3a9a7262c8a78d4c8fb3ba6a2b3d", size = 625675, upload-time = "2025-04-08T10:34:55.173Z" }, - { url = "https://files.pythonhosted.org/packages/96/1f/1803a18bd6ab04a0766386a19bcfe64641381a04939efdaa95f0e3b0eb58/watchfiles-1.0.5-cp310-cp310-win32.whl", hash = "sha256:cd47d063fbeabd4c6cae1d4bcaa38f0902f8dc5ed168072874ea11d0c7afc1ff", size = 277921, upload-time = "2025-04-08T10:34:56.318Z" }, - { url = "https://files.pythonhosted.org/packages/c2/3b/29a89de074a7d6e8b4dc67c26e03d73313e4ecf0d6e97e942a65fa7c195e/watchfiles-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:86c0df05b47a79d80351cd179893f2f9c1b1cae49d96e8b3290c7f4bd0ca0a92", size = 291526, upload-time = "2025-04-08T10:34:57.95Z" }, - { url = "https://files.pythonhosted.org/packages/39/f4/41b591f59021786ef517e1cdc3b510383551846703e03f204827854a96f8/watchfiles-1.0.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:237f9be419e977a0f8f6b2e7b0475ababe78ff1ab06822df95d914a945eac827", size = 405336, upload-time = "2025-04-08T10:34:59.359Z" }, - { url = "https://files.pythonhosted.org/packages/ae/06/93789c135be4d6d0e4f63e96eea56dc54050b243eacc28439a26482b5235/watchfiles-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0da39ff917af8b27a4bdc5a97ac577552a38aac0d260a859c1517ea3dc1a7c4", size = 395977, upload-time = "2025-04-08T10:35:00.522Z" }, - { url = "https://files.pythonhosted.org/packages/d2/db/1cd89bd83728ca37054512d4d35ab69b5f12b8aa2ac9be3b0276b3bf06cc/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cfcb3952350e95603f232a7a15f6c5f86c5375e46f0bd4ae70d43e3e063c13d", size = 455232, upload-time = "2025-04-08T10:35:01.698Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/d8a4d44ffe960517e487c9c04f77b06b8abf05eb680bed71c82b5f2cad62/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b2dddba7a4e6151384e252a5632efcaa9bc5d1c4b567f3cb621306b2ca9f63", size = 459151, upload-time = "2025-04-08T10:35:03.358Z" }, - { url = "https://files.pythonhosted.org/packages/6c/da/267a1546f26465dead1719caaba3ce660657f83c9d9c052ba98fb8856e13/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95cf944fcfc394c5f9de794ce581914900f82ff1f855326f25ebcf24d5397418", size = 489054, upload-time = "2025-04-08T10:35:04.561Z" }, - { url = "https://files.pythonhosted.org/packages/b1/31/33850dfd5c6efb6f27d2465cc4c6b27c5a6f5ed53c6fa63b7263cf5f60f6/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf6cd9f83d7c023b1aba15d13f705ca7b7d38675c121f3cc4a6e25bd0857ee9", size = 523955, upload-time = "2025-04-08T10:35:05.786Z" }, - { url = "https://files.pythonhosted.org/packages/09/84/b7d7b67856efb183a421f1416b44ca975cb2ea6c4544827955dfb01f7dc2/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:852de68acd6212cd6d33edf21e6f9e56e5d98c6add46f48244bd479d97c967c6", size = 502234, upload-time = "2025-04-08T10:35:07.187Z" }, - { url = "https://files.pythonhosted.org/packages/71/87/6dc5ec6882a2254cfdd8b0718b684504e737273903b65d7338efaba08b52/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5730f3aa35e646103b53389d5bc77edfbf578ab6dab2e005142b5b80a35ef25", size = 454750, upload-time = "2025-04-08T10:35:08.859Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6c/3786c50213451a0ad15170d091570d4a6554976cf0df19878002fc96075a/watchfiles-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:18b3bd29954bc4abeeb4e9d9cf0b30227f0f206c86657674f544cb032296acd5", size = 631591, upload-time = "2025-04-08T10:35:10.64Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b3/1427425ade4e359a0deacce01a47a26024b2ccdb53098f9d64d497f6684c/watchfiles-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ba5552a1b07c8edbf197055bc9d518b8f0d98a1c6a73a293bc0726dce068ed01", size = 625370, upload-time = "2025-04-08T10:35:12.412Z" }, - { url = "https://files.pythonhosted.org/packages/15/ba/f60e053b0b5b8145d682672024aa91370a29c5c921a88977eb565de34086/watchfiles-1.0.5-cp311-cp311-win32.whl", hash = "sha256:2f1fefb2e90e89959447bc0420fddd1e76f625784340d64a2f7d5983ef9ad246", size = 277791, upload-time = "2025-04-08T10:35:13.719Z" }, - { url = "https://files.pythonhosted.org/packages/50/ed/7603c4e164225c12c0d4e8700b64bb00e01a6c4eeea372292a3856be33a4/watchfiles-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:b6e76ceb1dd18c8e29c73f47d41866972e891fc4cc7ba014f487def72c1cf096", size = 291622, upload-time = "2025-04-08T10:35:15.071Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c2/99bb7c96b4450e36877fde33690ded286ff555b5a5c1d925855d556968a1/watchfiles-1.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:266710eb6fddc1f5e51843c70e3bebfb0f5e77cf4f27129278c70554104d19ed", size = 283699, upload-time = "2025-04-08T10:35:16.732Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8c/4f0b9bdb75a1bfbd9c78fad7d8854369283f74fe7cf03eb16be77054536d/watchfiles-1.0.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5eb568c2aa6018e26da9e6c86f3ec3fd958cee7f0311b35c2630fa4217d17f2", size = 401511, upload-time = "2025-04-08T10:35:17.956Z" }, - { url = "https://files.pythonhosted.org/packages/dc/4e/7e15825def77f8bd359b6d3f379f0c9dac4eb09dd4ddd58fd7d14127179c/watchfiles-1.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a04059f4923ce4e856b4b4e5e783a70f49d9663d22a4c3b3298165996d1377f", size = 392715, upload-time = "2025-04-08T10:35:19.202Z" }, - { url = "https://files.pythonhosted.org/packages/58/65/b72fb817518728e08de5840d5d38571466c1b4a3f724d190cec909ee6f3f/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e380c89983ce6e6fe2dd1e1921b9952fb4e6da882931abd1824c092ed495dec", size = 454138, upload-time = "2025-04-08T10:35:20.586Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a4/86833fd2ea2e50ae28989f5950b5c3f91022d67092bfec08f8300d8b347b/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe43139b2c0fdc4a14d4f8d5b5d967f7a2777fd3d38ecf5b1ec669b0d7e43c21", size = 458592, upload-time = "2025-04-08T10:35:21.87Z" }, - { url = "https://files.pythonhosted.org/packages/38/7e/42cb8df8be9a37e50dd3a818816501cf7a20d635d76d6bd65aae3dbbff68/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee0822ce1b8a14fe5a066f93edd20aada932acfe348bede8aa2149f1a4489512", size = 487532, upload-time = "2025-04-08T10:35:23.143Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fd/13d26721c85d7f3df6169d8b495fcac8ab0dc8f0945ebea8845de4681dab/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a0dbcb1c2d8f2ab6e0a81c6699b236932bd264d4cef1ac475858d16c403de74d", size = 522865, upload-time = "2025-04-08T10:35:24.702Z" }, - { url = "https://files.pythonhosted.org/packages/a1/0d/7f9ae243c04e96c5455d111e21b09087d0eeaf9a1369e13a01c7d3d82478/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2014a2b18ad3ca53b1f6c23f8cd94a18ce930c1837bd891262c182640eb40a6", size = 499887, upload-time = "2025-04-08T10:35:25.969Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0f/a257766998e26aca4b3acf2ae97dff04b57071e991a510857d3799247c67/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f6ae86d5cb647bf58f9f655fcf577f713915a5d69057a0371bc257e2553234", size = 454498, upload-time = "2025-04-08T10:35:27.353Z" }, - { url = "https://files.pythonhosted.org/packages/81/79/8bf142575a03e0af9c3d5f8bcae911ee6683ae93a625d349d4ecf4c8f7df/watchfiles-1.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1a7bac2bde1d661fb31f4d4e8e539e178774b76db3c2c17c4bb3e960a5de07a2", size = 630663, upload-time = "2025-04-08T10:35:28.685Z" }, - { url = "https://files.pythonhosted.org/packages/f1/80/abe2e79f610e45c63a70d271caea90c49bbf93eb00fa947fa9b803a1d51f/watchfiles-1.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ab626da2fc1ac277bbf752446470b367f84b50295264d2d313e28dc4405d663", size = 625410, upload-time = "2025-04-08T10:35:30.42Z" }, - { url = "https://files.pythonhosted.org/packages/91/6f/bc7fbecb84a41a9069c2c6eb6319f7f7df113adf113e358c57fc1aff7ff5/watchfiles-1.0.5-cp312-cp312-win32.whl", hash = "sha256:9f4571a783914feda92018ef3901dab8caf5b029325b5fe4558c074582815249", size = 277965, upload-time = "2025-04-08T10:35:32.023Z" }, - { url = "https://files.pythonhosted.org/packages/99/a5/bf1c297ea6649ec59e935ab311f63d8af5faa8f0b86993e3282b984263e3/watchfiles-1.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:360a398c3a19672cf93527f7e8d8b60d8275119c5d900f2e184d32483117a705", size = 291693, upload-time = "2025-04-08T10:35:33.225Z" }, - { url = "https://files.pythonhosted.org/packages/7f/7b/fd01087cc21db5c47e5beae507b87965db341cce8a86f9eb12bf5219d4e0/watchfiles-1.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:1a2902ede862969077b97523987c38db28abbe09fb19866e711485d9fbf0d417", size = 283287, upload-time = "2025-04-08T10:35:34.568Z" }, - { url = "https://files.pythonhosted.org/packages/c7/62/435766874b704f39b2fecd8395a29042db2b5ec4005bd34523415e9bd2e0/watchfiles-1.0.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0b289572c33a0deae62daa57e44a25b99b783e5f7aed81b314232b3d3c81a11d", size = 401531, upload-time = "2025-04-08T10:35:35.792Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a6/e52a02c05411b9cb02823e6797ef9bbba0bfaf1bb627da1634d44d8af833/watchfiles-1.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a056c2f692d65bf1e99c41045e3bdcaea3cb9e6b5a53dcaf60a5f3bd95fc9763", size = 392417, upload-time = "2025-04-08T10:35:37.048Z" }, - { url = "https://files.pythonhosted.org/packages/3f/53/c4af6819770455932144e0109d4854437769672d7ad897e76e8e1673435d/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9dca99744991fc9850d18015c4f0438865414e50069670f5f7eee08340d8b40", size = 453423, upload-time = "2025-04-08T10:35:38.357Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d1/8e88df58bbbf819b8bc5cfbacd3c79e01b40261cad0fc84d1e1ebd778a07/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:894342d61d355446d02cd3988a7326af344143eb33a2fd5d38482a92072d9563", size = 458185, upload-time = "2025-04-08T10:35:39.708Z" }, - { url = "https://files.pythonhosted.org/packages/ff/70/fffaa11962dd5429e47e478a18736d4e42bec42404f5ee3b92ef1b87ad60/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab44e1580924d1ffd7b3938e02716d5ad190441965138b4aa1d1f31ea0877f04", size = 486696, upload-time = "2025-04-08T10:35:41.469Z" }, - { url = "https://files.pythonhosted.org/packages/39/db/723c0328e8b3692d53eb273797d9a08be6ffb1d16f1c0ba2bdbdc2a3852c/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6f9367b132078b2ceb8d066ff6c93a970a18c3029cea37bfd7b2d3dd2e5db8f", size = 522327, upload-time = "2025-04-08T10:35:43.289Z" }, - { url = "https://files.pythonhosted.org/packages/cd/05/9fccc43c50c39a76b68343484b9da7b12d42d0859c37c61aec018c967a32/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2e55a9b162e06e3f862fb61e399fe9f05d908d019d87bf5b496a04ef18a970a", size = 499741, upload-time = "2025-04-08T10:35:44.574Z" }, - { url = "https://files.pythonhosted.org/packages/23/14/499e90c37fa518976782b10a18b18db9f55ea73ca14641615056f8194bb3/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0125f91f70e0732a9f8ee01e49515c35d38ba48db507a50c5bdcad9503af5827", size = 453995, upload-time = "2025-04-08T10:35:46.336Z" }, - { url = "https://files.pythonhosted.org/packages/61/d9/f75d6840059320df5adecd2c687fbc18960a7f97b55c300d20f207d48aef/watchfiles-1.0.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:13bb21f8ba3248386337c9fa51c528868e6c34a707f729ab041c846d52a0c69a", size = 629693, upload-time = "2025-04-08T10:35:48.161Z" }, - { url = "https://files.pythonhosted.org/packages/fc/17/180ca383f5061b61406477218c55d66ec118e6c0c51f02d8142895fcf0a9/watchfiles-1.0.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:839ebd0df4a18c5b3c1b890145b5a3f5f64063c2a0d02b13c76d78fe5de34936", size = 624677, upload-time = "2025-04-08T10:35:49.65Z" }, - { url = "https://files.pythonhosted.org/packages/bf/15/714d6ef307f803f236d69ee9d421763707899d6298d9f3183e55e366d9af/watchfiles-1.0.5-cp313-cp313-win32.whl", hash = "sha256:4a8ec1e4e16e2d5bafc9ba82f7aaecfeec990ca7cd27e84fb6f191804ed2fcfc", size = 277804, upload-time = "2025-04-08T10:35:51.093Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b4/c57b99518fadf431f3ef47a610839e46e5f8abf9814f969859d1c65c02c7/watchfiles-1.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:f436601594f15bf406518af922a89dcaab416568edb6f65c4e5bbbad1ea45c11", size = 291087, upload-time = "2025-04-08T10:35:52.458Z" }, - { url = "https://files.pythonhosted.org/packages/c5/95/94f3dd15557f5553261e407551c5e4d340e50161c55aa30812c79da6cb04/watchfiles-1.0.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2cfb371be97d4db374cba381b9f911dd35bb5f4c58faa7b8b7106c8853e5d225", size = 405686, upload-time = "2025-04-08T10:35:53.86Z" }, - { url = "https://files.pythonhosted.org/packages/f4/aa/b99e968153f8b70159ecca7b3daf46a6f46d97190bdaa3a449ad31b921d7/watchfiles-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3904d88955fda461ea2531fcf6ef73584ca921415d5cfa44457a225f4a42bc1", size = 396047, upload-time = "2025-04-08T10:35:55.232Z" }, - { url = "https://files.pythonhosted.org/packages/23/cb/90d3d760ad4bc7290e313fb9236c7d60598627a25a5a72764e48d9652064/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b7a21715fb12274a71d335cff6c71fe7f676b293d322722fe708a9ec81d91f5", size = 456081, upload-time = "2025-04-08T10:35:57.102Z" }, - { url = "https://files.pythonhosted.org/packages/3e/65/79c6cebe5bcb695cdac145946ad5a09b9f66762549e82fb2d064ea960c95/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dfd6ae1c385ab481766b3c61c44aca2b3cd775f6f7c0fa93d979ddec853d29d5", size = 459838, upload-time = "2025-04-08T10:35:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/3f/84/699f52632cdaa777f6df7f6f1cc02a23a75b41071b7e6765b9a412495f61/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b659576b950865fdad31fa491d31d37cf78b27113a7671d39f919828587b429b", size = 489753, upload-time = "2025-04-08T10:36:00.237Z" }, - { url = "https://files.pythonhosted.org/packages/25/68/3241f82ad414fd969de6bf3a93805682e5eb589aeab510322f2aa14462f8/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1909e0a9cd95251b15bff4261de5dd7550885bd172e3536824bf1cf6b121e200", size = 525015, upload-time = "2025-04-08T10:36:02.159Z" }, - { url = "https://files.pythonhosted.org/packages/85/c4/30d879e252f52b01660f545c193e6b81c48aac2e0eeec71263af3add905b/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:832ccc221927c860e7286c55c9b6ebcc0265d5e072f49c7f6456c7798d2b39aa", size = 503816, upload-time = "2025-04-08T10:36:03.869Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7d/fa34750f6f4b1a70d96fa6b685fe2948d01e3936328ea528f182943eb373/watchfiles-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fbb6102b3296926d0c62cfc9347f6237fb9400aecd0ba6bbda94cae15f2b3b", size = 456137, upload-time = "2025-04-08T10:36:05.226Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0c/a1569709aaeccb1dd74b0dd304d0de29e3ea1fdf11e08c78f489628f9ebb/watchfiles-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:15ac96dd567ad6c71c71f7b2c658cb22b7734901546cd50a475128ab557593ca", size = 632673, upload-time = "2025-04-08T10:36:06.752Z" }, - { url = "https://files.pythonhosted.org/packages/90/b6/645eaaca11f3ac625cf3b6e008e543acf0bf2581f68b5e205a13b05618b6/watchfiles-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b6227351e11c57ae997d222e13f5b6f1f0700d84b8c52304e8675d33a808382", size = 626659, upload-time = "2025-04-08T10:36:08.18Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c4/e741d9b92b0a2c74b976ff78bbc9a1276b4d904c590878e8fe0ec9fecca5/watchfiles-1.0.5-cp39-cp39-win32.whl", hash = "sha256:974866e0db748ebf1eccab17862bc0f0303807ed9cda465d1324625b81293a18", size = 278471, upload-time = "2025-04-08T10:36:10.546Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/36b0cb6add99105f78931994b30bc1dd24118c0e659ab6a3ffe0dd8734d4/watchfiles-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:9848b21ae152fe79c10dd0197304ada8f7b586d3ebc3f27f43c506e5a52a863c", size = 292027, upload-time = "2025-04-08T10:36:11.901Z" }, - { url = "https://files.pythonhosted.org/packages/1a/03/81f9fcc3963b3fc415cd4b0b2b39ee8cc136c42fb10a36acf38745e9d283/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f59b870db1f1ae5a9ac28245707d955c8721dd6565e7f411024fa374b5362d1d", size = 405947, upload-time = "2025-04-08T10:36:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/54/97/8c4213a852feb64807ec1d380f42d4fc8bfaef896bdbd94318f8fd7f3e4e/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9475b0093767e1475095f2aeb1d219fb9664081d403d1dff81342df8cd707034", size = 397276, upload-time = "2025-04-08T10:36:15.131Z" }, - { url = "https://files.pythonhosted.org/packages/78/12/d4464d19860cb9672efa45eec1b08f8472c478ed67dcd30647c51ada7aef/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc533aa50664ebd6c628b2f30591956519462f5d27f951ed03d6c82b2dfd9965", size = 455550, upload-time = "2025-04-08T10:36:16.635Z" }, - { url = "https://files.pythonhosted.org/packages/90/fb/b07bcdf1034d8edeaef4c22f3e9e3157d37c5071b5f9492ffdfa4ad4bed7/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed1cd825158dcaae36acce7b2db33dcbfd12b30c34317a88b8ed80f0541cc57", size = 455542, upload-time = "2025-04-08T10:36:18.655Z" }, - { url = "https://files.pythonhosted.org/packages/5b/84/7b69282c0df2bf2dff4e50be2c54669cddf219a5a5fb077891c00c00e5c8/watchfiles-1.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:554389562c29c2c182e3908b149095051f81d28c2fec79ad6c8997d7d63e0009", size = 405783, upload-time = "2025-04-08T10:36:20.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ae/03fca0545d99b7ea21df49bead7b51e7dca9ce3b45bb6d34530aa18c16a2/watchfiles-1.0.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a74add8d7727e6404d5dc4dcd7fac65d4d82f95928bbee0cf5414c900e86773e", size = 397133, upload-time = "2025-04-08T10:36:22.439Z" }, - { url = "https://files.pythonhosted.org/packages/1a/07/c2b6390003e933b2e187a3f7070c00bd87da8a58d6f2393e039b06a88c2e/watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb1489f25b051a89fae574505cc26360c8e95e227a9500182a7fe0afcc500ce0", size = 456198, upload-time = "2025-04-08T10:36:23.884Z" }, - { url = "https://files.pythonhosted.org/packages/46/d3/ecc62cbd7054f0812f3a7ca7c1c9f7ba99ba45efcfc8297a9fcd2c87b31c/watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0901429650652d3f0da90bad42bdafc1f9143ff3605633c455c999a2d786cac", size = 456511, upload-time = "2025-04-08T10:36:25.42Z" }, + { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757, upload-time = "2025-06-15T19:04:51.058Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511, upload-time = "2025-06-15T19:04:52.79Z" }, + { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739, upload-time = "2025-06-15T19:04:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106, upload-time = "2025-06-15T19:04:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264, upload-time = "2025-06-15T19:04:57.009Z" }, + { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612, upload-time = "2025-06-15T19:04:58.409Z" }, + { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242, upload-time = "2025-06-15T19:04:59.786Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148, upload-time = "2025-06-15T19:05:01.103Z" }, + { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574, upload-time = "2025-06-15T19:05:02.582Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378, upload-time = "2025-06-15T19:05:03.719Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829, upload-time = "2025-06-15T19:05:04.822Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192, upload-time = "2025-06-15T19:05:06.348Z" }, + { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" }, + { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" }, + { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" }, + { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" }, + { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" }, + { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" }, + { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" }, + { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, + { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, + { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, + { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, + { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, + { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, + { url = "https://files.pythonhosted.org/packages/47/8a/a45db804b9f0740f8408626ab2bca89c3136432e57c4673b50180bf85dd9/watchfiles-1.1.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:865c8e95713744cf5ae261f3067861e9da5f1370ba91fc536431e29b418676fa", size = 406400, upload-time = "2025-06-15T19:06:30.233Z" }, + { url = "https://files.pythonhosted.org/packages/64/06/a08684f628fb41addd451845aceedc2407dc3d843b4b060a7c4350ddee0c/watchfiles-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42f92befc848bb7a19658f21f3e7bae80d7d005d13891c62c2cd4d4d0abb3433", size = 397920, upload-time = "2025-06-15T19:06:31.315Z" }, + { url = "https://files.pythonhosted.org/packages/79/e6/e10d5675af653b1b07d4156906858041149ca222edaf8995877f2605ba9e/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0cc8365ab29487eb4f9979fd41b22549853389e22d5de3f134a6796e1b05a4", size = 451196, upload-time = "2025-06-15T19:06:32.435Z" }, + { url = "https://files.pythonhosted.org/packages/f6/8a/facd6988100cd0f39e89f6c550af80edb28e3a529e1ee662e750663e6b36/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90ebb429e933645f3da534c89b29b665e285048973b4d2b6946526888c3eb2c7", size = 458218, upload-time = "2025-06-15T19:06:33.503Z" }, + { url = "https://files.pythonhosted.org/packages/90/26/34cbcbc4d0f2f8f9cc243007e65d741ae039f7a11ef8ec6e9cd25bee08d1/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c588c45da9b08ab3da81d08d7987dae6d2a3badd63acdb3e206a42dbfa7cb76f", size = 484851, upload-time = "2025-06-15T19:06:34.541Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1f/f59faa9fc4b0e36dbcdd28a18c430416443b309d295d8b82e18192d120ad/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c55b0f9f68590115c25272b06e63f0824f03d4fc7d6deed43d8ad5660cabdbf", size = 599520, upload-time = "2025-06-15T19:06:35.785Z" }, + { url = "https://files.pythonhosted.org/packages/83/72/3637abecb3bf590529f5154ca000924003e5f4bbb9619744feeaf6f0b70b/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd17a1e489f02ce9117b0de3c0b1fab1c3e2eedc82311b299ee6b6faf6c23a29", size = 477956, upload-time = "2025-06-15T19:06:36.965Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f3/d14ffd9acc0c1bd4790378995e320981423263a5d70bd3929e2e0dc87fff/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da71945c9ace018d8634822f16cbc2a78323ef6c876b1d34bbf5d5222fd6a72e", size = 453196, upload-time = "2025-06-15T19:06:38.024Z" }, + { url = "https://files.pythonhosted.org/packages/7f/38/78ad77bd99e20c0fdc82262be571ef114fc0beef9b43db52adb939768c38/watchfiles-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:51556d5004887045dba3acdd1fdf61dddea2be0a7e18048b5e853dcd37149b86", size = 627479, upload-time = "2025-06-15T19:06:39.442Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/549d50a22fcc83f1017c6427b1c76c053233f91b526f4ad7a45971e70c0b/watchfiles-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04e4ed5d1cd3eae68c89bcc1a485a109f39f2fd8de05f705e98af6b5f1861f1f", size = 624414, upload-time = "2025-06-15T19:06:40.859Z" }, + { url = "https://files.pythonhosted.org/packages/72/de/57d6e40dc9140af71c12f3a9fc2d3efc5529d93981cd4d265d484d7c9148/watchfiles-1.1.0-cp39-cp39-win32.whl", hash = "sha256:c600e85f2ffd9f1035222b1a312aff85fd11ea39baff1d705b9b047aad2ce267", size = 280020, upload-time = "2025-06-15T19:06:41.89Z" }, + { url = "https://files.pythonhosted.org/packages/88/bb/7d287fc2a762396b128a0fca2dbae29386e0a242b81d1046daf389641db3/watchfiles-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3aba215958d88182e8d2acba0fdaf687745180974946609119953c0e112397dc", size = 292758, upload-time = "2025-06-15T19:06:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748, upload-time = "2025-06-15T19:06:44.2Z" }, + { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801, upload-time = "2025-06-15T19:06:45.774Z" }, + { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528, upload-time = "2025-06-15T19:06:46.791Z" }, + { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095, upload-time = "2025-06-15T19:06:48.211Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" }, + { url = "https://files.pythonhosted.org/packages/48/93/5c96bdb65e7f88f7da40645f34c0a3c317a2931ed82161e93c91e8eddd27/watchfiles-1.1.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7b3443f4ec3ba5aa00b0e9fa90cf31d98321cbff8b925a7c7b84161619870bc9", size = 406640, upload-time = "2025-06-15T19:06:54.868Z" }, + { url = "https://files.pythonhosted.org/packages/e3/25/09204836e93e1b99cce88802ce87264a1d20610c7a8f6de24def27ad95b1/watchfiles-1.1.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7049e52167fc75fc3cc418fc13d39a8e520cbb60ca08b47f6cedb85e181d2f2a", size = 398543, upload-time = "2025-06-15T19:06:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/5e/dc/6f324a6f32c5ab73b54311b5f393a79df34c1584b8d2404cf7e6d780aa5d/watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54062ef956807ba806559b3c3d52105ae1827a0d4ab47b621b31132b6b7e2866", size = 451787, upload-time = "2025-06-15T19:06:56.998Z" }, + { url = "https://files.pythonhosted.org/packages/45/5d/1d02ef4caa4ec02389e72d5594cdf9c67f1800a7c380baa55063c30c6598/watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a7bd57a1bb02f9d5c398c0c1675384e7ab1dd39da0ca50b7f09af45fa435277", size = 454272, upload-time = "2025-06-15T19:06:58.055Z" }, ] [[package]] diff --git a/libs/langgraph/langgraph/_internal/_fields.py b/libs/langgraph/langgraph/_internal/_fields.py index 33a8f2951..5b9c8dca6 100644 --- a/libs/langgraph/langgraph/_internal/_fields.py +++ b/libs/langgraph/langgraph/_internal/_fields.py @@ -128,8 +128,8 @@ def get_enhanced_type_hints( # Pydantic models try: - if hasattr(type, "__fields__") and name in type.__fields__: - field = type.__fields__[name] + if hasattr(type, "model_fields") and name in type.model_fields: + field = type.model_fields[name] if hasattr(field, "description") and field.description is not None: description = field.description @@ -163,7 +163,7 @@ def get_update_as_tuples(input: Any, keys: Sequence[str]) -> list[tuple[str, Any """Get Pydantic state update as a list of (key, value) tuples.""" if isinstance(input, BaseModel): keep = input.model_fields_set - defaults = {k: v.default for k, v in input.model_fields.items()} + defaults = {k: v.default for k, v in type(input).model_fields.items()} else: keep = None defaults = {} diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 6ad7dce00..8a094c6c4 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -23,8 +23,8 @@ from typing import ( ) from langchain_core.runnables import Runnable, RunnableConfig -from pydantic import BaseModel -from typing_extensions import Self, Unpack +from pydantic import BaseModel, TypeAdapter +from typing_extensions import Self, Unpack, is_typeddict from langgraph._internal._fields import ( get_cached_annotated_keys, @@ -831,18 +831,20 @@ class CompiledStateGraph( self.builder = builder self.schema_to_mapper = schema_to_mapper - def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]: - return _get_schema( + def get_input_jsonschema( + self, config: RunnableConfig | None = None + ) -> dict[str, Any]: + return _get_json_schema( typ=self.builder.input_schema, schemas=self.builder.schemas, channels=self.builder.channels, name=self.get_name("Input"), ) - def get_output_schema( + def get_output_jsonschema( self, config: RunnableConfig | None = None - ) -> type[BaseModel]: - return _get_schema( + ) -> dict[str, Any]: + return _get_json_schema( typ=self.builder.output_schema, schemas=self.builder.schemas, channels=self.builder.channels, @@ -1309,21 +1311,23 @@ def _is_field_managed_value(name: str, typ: type[Any]) -> ManagedValueSpec | Non return None -def _get_schema( +def _get_json_schema( typ: type, schemas: dict, channels: dict, name: str, -) -> type[BaseModel]: +) -> dict[str, Any]: if isclass(typ) and issubclass(typ, BaseModel): - return typ + return typ.model_json_schema() + elif is_typeddict(typ): + return TypeAdapter(typ).json_schema() else: keys = list(schemas[typ].keys()) if len(keys) == 1 and keys[0] == "__root__": return create_model( name, root=(channels[keys[0]].UpdateType, None), - ) + ).model_json_schema() else: return create_model( name, @@ -1341,4 +1345,4 @@ def _get_schema( for k in schemas[typ] if k in channels and isinstance(channels[k], BaseChannel) }, - ) + ).model_json_schema() diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index eded49e24..539f0aedd 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -775,10 +775,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou self, *, include: Sequence[str] | None = None ) -> dict[str, Any]: schema = self.config_schema(include=include) - if hasattr(schema, "model_json_schema"): - return schema.model_json_schema() - else: - return schema.schema() + return schema.model_json_schema() @property def InputType(self) -> Any: @@ -805,10 +802,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou self, config: RunnableConfig | None = None ) -> dict[str, Any]: schema = self.get_input_schema(config) - if hasattr(schema, "model_json_schema"): - return schema.model_json_schema() - else: - return schema.schema() + return schema.model_json_schema() @property def OutputType(self) -> Any: @@ -837,10 +831,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou self, config: RunnableConfig | None = None ) -> dict[str, Any]: schema = self.get_output_schema(config) - if hasattr(schema, "model_json_schema"): - return schema.model_json_schema() - else: - return schema.schema() + return schema.model_json_schema() @property def stream_channels_list(self) -> Sequence[str]: diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index b04d64a36..1d5518052 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -13,9 +13,9 @@ license = "MIT" license-files = ['LICENSE'] dependencies = [ "langchain-core>=0.1", - "langgraph-checkpoint>=2.1.0", - "langgraph-sdk>=0.1.42", - "langgraph-prebuilt>=0.5.0", + "langgraph-checkpoint>=2.1.0,<3.0.0", + "langgraph-sdk>=0.1.42,<0.2.0", + "langgraph-prebuilt>=0.5.0,<0.6.0", "xxhash>=3.5.0", "pydantic>=2.7.4", ] diff --git a/libs/langgraph/tests/__snapshots__/test_large_cases.ambr b/libs/langgraph/tests/__snapshots__/test_large_cases.ambr index 2a6c8dd25..6d4d8c2e6 100644 --- a/libs/langgraph/tests/__snapshots__/test_large_cases.ambr +++ b/libs/langgraph/tests/__snapshots__/test_large_cases.ambr @@ -1,9 +1,9 @@ # serializer version: 1 # name: test_conditional_state_graph[memory] - '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "enum": ["AgentAction"], "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "enum": ["AgentFinish"], "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"default": null, "title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "default": null, "title": "Agent Outcome"}, "intermediate_steps": {"default": null, "items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "title": "LangGraphInput", "type": "object"}' + '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"additionalProperties": true, "title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "title": "Agent Outcome"}, "intermediate_steps": {"items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "title": "AgentState", "type": "object"}' # --- # name: test_conditional_state_graph[memory].1 - '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "enum": ["AgentAction"], "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "enum": ["AgentFinish"], "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"default": null, "title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "default": null, "title": "Agent Outcome"}, "intermediate_steps": {"default": null, "items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "title": "LangGraphOutput", "type": "object"}' + '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"additionalProperties": true, "title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "title": "Agent Outcome"}, "intermediate_steps": {"items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "title": "AgentState", "type": "object"}' # --- # name: test_conditional_state_graph[memory].2 ''' @@ -88,10 +88,10 @@ ''' # --- # name: test_message_graph[memory] - '{"$defs": {"AIMessage": {"additionalProperties": true, "description": "Message from an AI.\\n\\nAIMessage is returned from a chat model as a response to a prompt.\\n\\nThis message represents the output of the model and consists of both\\nthe raw output as returned by the model together standardized fields\\n(e.g., tool calls, usage metadata) added by the LangChain framework.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "ai", "default": "ai", "enum": ["ai"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}}, "required": ["content"], "title": "AIMessage", "type": "object"}, "AIMessageChunk": {"additionalProperties": true, "description": "Message chunk from an AI.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "AIMessageChunk", "default": "AIMessageChunk", "enum": ["AIMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}, "tool_call_chunks": {"default": [], "items": {"$ref": "#/$defs/ToolCallChunk"}, "title": "Tool Call Chunks", "type": "array"}}, "required": ["content"], "title": "AIMessageChunk", "type": "object"}, "ChatMessage": {"additionalProperties": true, "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "chat", "default": "chat", "enum": ["chat"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessage", "type": "object"}, "ChatMessageChunk": {"additionalProperties": true, "description": "Chat Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "ChatMessageChunk", "default": "ChatMessageChunk", "enum": ["ChatMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessageChunk", "type": "object"}, "FunctionMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nFunctionMessage are an older version of the ToolMessage schema, and\\ndo not contain the tool_call_id field.\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "function", "default": "function", "enum": ["function"], "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessage", "type": "object"}, "FunctionMessageChunk": {"additionalProperties": true, "description": "Function Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "FunctionMessageChunk", "default": "FunctionMessageChunk", "enum": ["FunctionMessageChunk"], "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessageChunk", "type": "object"}, "HumanMessage": {"additionalProperties": true, "description": "Message from a human.\\n\\nHumanMessages are messages that are passed in from a human to the model.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Instantiate a chat model and invoke it with the messages\\n model = ...\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "human", "default": "human", "enum": ["human"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessage", "type": "object"}, "HumanMessageChunk": {"additionalProperties": true, "description": "Human Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "HumanMessageChunk", "default": "HumanMessageChunk", "enum": ["HumanMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessageChunk", "type": "object"}, "InputTokenDetails": {"description": "Breakdown of input token counts.\\n\\nDoes *not* need to sum to full input token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n }\\n\\n.. versionadded:: 0.3.9\\n\\nMay also hold extra provider-specific keys.", "properties": {"audio": {"title": "Audio", "type": "integer"}, "cache_creation": {"title": "Cache Creation", "type": "integer"}, "cache_read": {"title": "Cache Read", "type": "integer"}}, "title": "InputTokenDetails", "type": "object"}, "InvalidToolCall": {"description": "Allowance for errors made by LLM.\\n\\nHere we add an `error` key to surface errors made during generation\\n(e.g., invalid JSON arguments.)", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "error": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Error"}, "type": {"const": "invalid_tool_call", "enum": ["invalid_tool_call"], "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "error"], "title": "InvalidToolCall", "type": "object"}, "OutputTokenDetails": {"description": "Breakdown of output token counts.\\n\\nDoes *not* need to sum to full output token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n\\n.. versionadded:: 0.3.9", "properties": {"audio": {"title": "Audio", "type": "integer"}, "reasoning": {"title": "Reasoning", "type": "integer"}}, "title": "OutputTokenDetails", "type": "object"}, "SystemMessage": {"additionalProperties": true, "description": "Message for priming AI behavior.\\n\\nThe system message is usually passed in as the first of a sequence\\nof input messages.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Define a chat model and invoke it with the messages\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "system", "default": "system", "enum": ["system"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessage", "type": "object"}, "SystemMessageChunk": {"additionalProperties": true, "description": "System Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "SystemMessageChunk", "default": "SystemMessageChunk", "enum": ["SystemMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessageChunk", "type": "object"}, "ToolCall": {"description": "Represents a request to call a tool.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"name\\": \\"foo\\",\\n \\"args\\": {\\"a\\": 1},\\n \\"id\\": \\"123\\"\\n }\\n\\n This represents a request to call the tool named \\"foo\\" with arguments {\\"a\\": 1}\\n and an identifier of \\"123\\".", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "type": {"const": "tool_call", "enum": ["tool_call"], "title": "Type", "type": "string"}}, "required": ["name", "args", "id"], "title": "ToolCall", "type": "object"}, "ToolCallChunk": {"description": "A chunk of a tool call (e.g., as part of a stream).\\n\\nWhen merging ToolCallChunks (e.g., via AIMessageChunk.__add__),\\nall string attributes are concatenated. Chunks are only merged if their\\nvalues of `index` are equal and not None.\\n\\nExample:\\n\\n.. code-block:: python\\n\\n left_chunks = [ToolCallChunk(name=\\"foo\\", args=\'{\\"a\\":\', index=0)]\\n right_chunks = [ToolCallChunk(name=None, args=\'1}\', index=0)]\\n\\n (\\n AIMessageChunk(content=\\"\\", tool_call_chunks=left_chunks)\\n + AIMessageChunk(content=\\"\\", tool_call_chunks=right_chunks)\\n ).tool_call_chunks == [ToolCallChunk(name=\'foo\', args=\'{\\"a\\":1}\', index=0)]", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "index": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Index"}, "type": {"const": "tool_call_chunk", "enum": ["tool_call_chunk"], "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "index"], "title": "ToolCallChunk", "type": "object"}, "ToolMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nToolMessages contain the result of a tool invocation. Typically, the result\\nis encoded inside the `content` field.\\n\\nExample: A ToolMessage representing a result of 42 from a tool call with id\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n ToolMessage(content=\'42\', tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\')\\n\\n\\nExample: A ToolMessage where only part of the tool output is sent to the model\\n and the full output is passed in to artifact.\\n\\n .. versionadded:: 0.2.17\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n tool_output = {\\n \\"stdout\\": \\"From the graph we can see that the correlation between x and y is ...\\",\\n \\"stderr\\": None,\\n \\"artifacts\\": {\\"type\\": \\"image\\", \\"base64_data\\": \\"/9j/4gIcSU...\\"},\\n }\\n\\n ToolMessage(\\n content=tool_output[\\"stdout\\"],\\n artifact=tool_output,\\n tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\',\\n )\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "tool", "default": "tool", "enum": ["tool"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessage", "type": "object"}, "ToolMessageChunk": {"additionalProperties": true, "description": "Tool Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "ToolMessageChunk", "default": "ToolMessageChunk", "enum": ["ToolMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessageChunk", "type": "object"}, "UsageMetadata": {"description": "Usage metadata for a message, such as token counts.\\n\\nThis is a standard representation of token usage that is consistent across models.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"input_tokens\\": 350,\\n \\"output_tokens\\": 240,\\n \\"total_tokens\\": 590,\\n \\"input_token_details\\": {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n },\\n \\"output_token_details\\": {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n }\\n\\n.. versionchanged:: 0.3.9\\n\\n Added ``input_token_details`` and ``output_token_details``.", "properties": {"input_tokens": {"title": "Input Tokens", "type": "integer"}, "output_tokens": {"title": "Output Tokens", "type": "integer"}, "total_tokens": {"title": "Total Tokens", "type": "integer"}, "input_token_details": {"$ref": "#/$defs/InputTokenDetails"}, "output_token_details": {"$ref": "#/$defs/OutputTokenDetails"}}, "required": ["input_tokens", "output_tokens", "total_tokens"], "title": "UsageMetadata", "type": "object"}}, "default": null, "items": {"oneOf": [{"$ref": "#/$defs/AIMessage"}, {"$ref": "#/$defs/HumanMessage"}, {"$ref": "#/$defs/ChatMessage"}, {"$ref": "#/$defs/SystemMessage"}, {"$ref": "#/$defs/FunctionMessage"}, {"$ref": "#/$defs/ToolMessage"}, {"$ref": "#/$defs/AIMessageChunk"}, {"$ref": "#/$defs/HumanMessageChunk"}, {"$ref": "#/$defs/ChatMessageChunk"}, {"$ref": "#/$defs/SystemMessageChunk"}, {"$ref": "#/$defs/FunctionMessageChunk"}, {"$ref": "#/$defs/ToolMessageChunk"}]}, "title": "LangGraphInput", "type": "array"}' + '{"$defs": {"AIMessage": {"additionalProperties": true, "description": "Message from an AI.\\n\\nAIMessage is returned from a chat model as a response to a prompt.\\n\\nThis message represents the output of the model and consists of both\\nthe raw output as returned by the model together standardized fields\\n(e.g., tool calls, usage metadata) added by the LangChain framework.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "ai", "default": "ai", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}}, "required": ["content"], "title": "AIMessage", "type": "object"}, "AIMessageChunk": {"additionalProperties": true, "description": "Message chunk from an AI.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "AIMessageChunk", "default": "AIMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}, "tool_call_chunks": {"default": [], "items": {"$ref": "#/$defs/ToolCallChunk"}, "title": "Tool Call Chunks", "type": "array"}}, "required": ["content"], "title": "AIMessageChunk", "type": "object"}, "ChatMessage": {"additionalProperties": true, "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "chat", "default": "chat", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessage", "type": "object"}, "ChatMessageChunk": {"additionalProperties": true, "description": "Chat Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "ChatMessageChunk", "default": "ChatMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessageChunk", "type": "object"}, "FunctionMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nFunctionMessage are an older version of the ToolMessage schema, and\\ndo not contain the tool_call_id field.\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "function", "default": "function", "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessage", "type": "object"}, "FunctionMessageChunk": {"additionalProperties": true, "description": "Function Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "FunctionMessageChunk", "default": "FunctionMessageChunk", "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessageChunk", "type": "object"}, "HumanMessage": {"additionalProperties": true, "description": "Message from a human.\\n\\nHumanMessages are messages that are passed in from a human to the model.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Instantiate a chat model and invoke it with the messages\\n model = ...\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "human", "default": "human", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessage", "type": "object"}, "HumanMessageChunk": {"additionalProperties": true, "description": "Human Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "HumanMessageChunk", "default": "HumanMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessageChunk", "type": "object"}, "InputTokenDetails": {"description": "Breakdown of input token counts.\\n\\nDoes *not* need to sum to full input token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n }\\n\\n.. versionadded:: 0.3.9\\n\\nMay also hold extra provider-specific keys.", "properties": {"audio": {"title": "Audio", "type": "integer"}, "cache_creation": {"title": "Cache Creation", "type": "integer"}, "cache_read": {"title": "Cache Read", "type": "integer"}}, "title": "InputTokenDetails", "type": "object"}, "InvalidToolCall": {"description": "Allowance for errors made by LLM.\\n\\nHere we add an `error` key to surface errors made during generation\\n(e.g., invalid JSON arguments.)", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "error": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Error"}, "type": {"const": "invalid_tool_call", "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "error"], "title": "InvalidToolCall", "type": "object"}, "OutputTokenDetails": {"description": "Breakdown of output token counts.\\n\\nDoes *not* need to sum to full output token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n\\n.. versionadded:: 0.3.9", "properties": {"audio": {"title": "Audio", "type": "integer"}, "reasoning": {"title": "Reasoning", "type": "integer"}}, "title": "OutputTokenDetails", "type": "object"}, "SystemMessage": {"additionalProperties": true, "description": "Message for priming AI behavior.\\n\\nThe system message is usually passed in as the first of a sequence\\nof input messages.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Define a chat model and invoke it with the messages\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "system", "default": "system", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessage", "type": "object"}, "SystemMessageChunk": {"additionalProperties": true, "description": "System Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "SystemMessageChunk", "default": "SystemMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessageChunk", "type": "object"}, "ToolCall": {"description": "Represents a request to call a tool.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"name\\": \\"foo\\",\\n \\"args\\": {\\"a\\": 1},\\n \\"id\\": \\"123\\"\\n }\\n\\n This represents a request to call the tool named \\"foo\\" with arguments {\\"a\\": 1}\\n and an identifier of \\"123\\".", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"additionalProperties": true, "title": "Args", "type": "object"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "type": {"const": "tool_call", "title": "Type", "type": "string"}}, "required": ["name", "args", "id"], "title": "ToolCall", "type": "object"}, "ToolCallChunk": {"description": "A chunk of a tool call (e.g., as part of a stream).\\n\\nWhen merging ToolCallChunks (e.g., via AIMessageChunk.__add__),\\nall string attributes are concatenated. Chunks are only merged if their\\nvalues of `index` are equal and not None.\\n\\nExample:\\n\\n.. code-block:: python\\n\\n left_chunks = [ToolCallChunk(name=\\"foo\\", args=\'{\\"a\\":\', index=0)]\\n right_chunks = [ToolCallChunk(name=None, args=\'1}\', index=0)]\\n\\n (\\n AIMessageChunk(content=\\"\\", tool_call_chunks=left_chunks)\\n + AIMessageChunk(content=\\"\\", tool_call_chunks=right_chunks)\\n ).tool_call_chunks == [ToolCallChunk(name=\'foo\', args=\'{\\"a\\":1}\', index=0)]", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "index": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Index"}, "type": {"const": "tool_call_chunk", "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "index"], "title": "ToolCallChunk", "type": "object"}, "ToolMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nToolMessages contain the result of a tool invocation. Typically, the result\\nis encoded inside the `content` field.\\n\\nExample: A ToolMessage representing a result of 42 from a tool call with id\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n ToolMessage(content=\'42\', tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\')\\n\\n\\nExample: A ToolMessage where only part of the tool output is sent to the model\\n and the full output is passed in to artifact.\\n\\n .. versionadded:: 0.2.17\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n tool_output = {\\n \\"stdout\\": \\"From the graph we can see that the correlation between x and y is ...\\",\\n \\"stderr\\": None,\\n \\"artifacts\\": {\\"type\\": \\"image\\", \\"base64_data\\": \\"/9j/4gIcSU...\\"},\\n }\\n\\n ToolMessage(\\n content=tool_output[\\"stdout\\"],\\n artifact=tool_output,\\n tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\',\\n )\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "tool", "default": "tool", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessage", "type": "object"}, "ToolMessageChunk": {"additionalProperties": true, "description": "Tool Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "ToolMessageChunk", "default": "ToolMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessageChunk", "type": "object"}, "UsageMetadata": {"description": "Usage metadata for a message, such as token counts.\\n\\nThis is a standard representation of token usage that is consistent across models.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"input_tokens\\": 350,\\n \\"output_tokens\\": 240,\\n \\"total_tokens\\": 590,\\n \\"input_token_details\\": {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n },\\n \\"output_token_details\\": {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n }\\n\\n.. versionchanged:: 0.3.9\\n\\n Added ``input_token_details`` and ``output_token_details``.", "properties": {"input_tokens": {"title": "Input Tokens", "type": "integer"}, "output_tokens": {"title": "Output Tokens", "type": "integer"}, "total_tokens": {"title": "Total Tokens", "type": "integer"}, "input_token_details": {"$ref": "#/$defs/InputTokenDetails"}, "output_token_details": {"$ref": "#/$defs/OutputTokenDetails"}}, "required": ["input_tokens", "output_tokens", "total_tokens"], "title": "UsageMetadata", "type": "object"}}, "default": null, "items": {"oneOf": [{"$ref": "#/$defs/AIMessage"}, {"$ref": "#/$defs/HumanMessage"}, {"$ref": "#/$defs/ChatMessage"}, {"$ref": "#/$defs/SystemMessage"}, {"$ref": "#/$defs/FunctionMessage"}, {"$ref": "#/$defs/ToolMessage"}, {"$ref": "#/$defs/AIMessageChunk"}, {"$ref": "#/$defs/HumanMessageChunk"}, {"$ref": "#/$defs/ChatMessageChunk"}, {"$ref": "#/$defs/SystemMessageChunk"}, {"$ref": "#/$defs/FunctionMessageChunk"}, {"$ref": "#/$defs/ToolMessageChunk"}]}, "title": "LangGraphInput", "type": "array"}' # --- # name: test_message_graph[memory].1 - '{"$defs": {"AIMessage": {"additionalProperties": true, "description": "Message from an AI.\\n\\nAIMessage is returned from a chat model as a response to a prompt.\\n\\nThis message represents the output of the model and consists of both\\nthe raw output as returned by the model together standardized fields\\n(e.g., tool calls, usage metadata) added by the LangChain framework.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "ai", "default": "ai", "enum": ["ai"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}}, "required": ["content"], "title": "AIMessage", "type": "object"}, "AIMessageChunk": {"additionalProperties": true, "description": "Message chunk from an AI.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "AIMessageChunk", "default": "AIMessageChunk", "enum": ["AIMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}, "tool_call_chunks": {"default": [], "items": {"$ref": "#/$defs/ToolCallChunk"}, "title": "Tool Call Chunks", "type": "array"}}, "required": ["content"], "title": "AIMessageChunk", "type": "object"}, "ChatMessage": {"additionalProperties": true, "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "chat", "default": "chat", "enum": ["chat"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessage", "type": "object"}, "ChatMessageChunk": {"additionalProperties": true, "description": "Chat Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "ChatMessageChunk", "default": "ChatMessageChunk", "enum": ["ChatMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessageChunk", "type": "object"}, "FunctionMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nFunctionMessage are an older version of the ToolMessage schema, and\\ndo not contain the tool_call_id field.\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "function", "default": "function", "enum": ["function"], "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessage", "type": "object"}, "FunctionMessageChunk": {"additionalProperties": true, "description": "Function Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "FunctionMessageChunk", "default": "FunctionMessageChunk", "enum": ["FunctionMessageChunk"], "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessageChunk", "type": "object"}, "HumanMessage": {"additionalProperties": true, "description": "Message from a human.\\n\\nHumanMessages are messages that are passed in from a human to the model.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Instantiate a chat model and invoke it with the messages\\n model = ...\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "human", "default": "human", "enum": ["human"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessage", "type": "object"}, "HumanMessageChunk": {"additionalProperties": true, "description": "Human Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "HumanMessageChunk", "default": "HumanMessageChunk", "enum": ["HumanMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessageChunk", "type": "object"}, "InputTokenDetails": {"description": "Breakdown of input token counts.\\n\\nDoes *not* need to sum to full input token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n }\\n\\n.. versionadded:: 0.3.9\\n\\nMay also hold extra provider-specific keys.", "properties": {"audio": {"title": "Audio", "type": "integer"}, "cache_creation": {"title": "Cache Creation", "type": "integer"}, "cache_read": {"title": "Cache Read", "type": "integer"}}, "title": "InputTokenDetails", "type": "object"}, "InvalidToolCall": {"description": "Allowance for errors made by LLM.\\n\\nHere we add an `error` key to surface errors made during generation\\n(e.g., invalid JSON arguments.)", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "error": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Error"}, "type": {"const": "invalid_tool_call", "enum": ["invalid_tool_call"], "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "error"], "title": "InvalidToolCall", "type": "object"}, "OutputTokenDetails": {"description": "Breakdown of output token counts.\\n\\nDoes *not* need to sum to full output token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n\\n.. versionadded:: 0.3.9", "properties": {"audio": {"title": "Audio", "type": "integer"}, "reasoning": {"title": "Reasoning", "type": "integer"}}, "title": "OutputTokenDetails", "type": "object"}, "SystemMessage": {"additionalProperties": true, "description": "Message for priming AI behavior.\\n\\nThe system message is usually passed in as the first of a sequence\\nof input messages.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Define a chat model and invoke it with the messages\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "system", "default": "system", "enum": ["system"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessage", "type": "object"}, "SystemMessageChunk": {"additionalProperties": true, "description": "System Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "SystemMessageChunk", "default": "SystemMessageChunk", "enum": ["SystemMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessageChunk", "type": "object"}, "ToolCall": {"description": "Represents a request to call a tool.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"name\\": \\"foo\\",\\n \\"args\\": {\\"a\\": 1},\\n \\"id\\": \\"123\\"\\n }\\n\\n This represents a request to call the tool named \\"foo\\" with arguments {\\"a\\": 1}\\n and an identifier of \\"123\\".", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "type": {"const": "tool_call", "enum": ["tool_call"], "title": "Type", "type": "string"}}, "required": ["name", "args", "id"], "title": "ToolCall", "type": "object"}, "ToolCallChunk": {"description": "A chunk of a tool call (e.g., as part of a stream).\\n\\nWhen merging ToolCallChunks (e.g., via AIMessageChunk.__add__),\\nall string attributes are concatenated. Chunks are only merged if their\\nvalues of `index` are equal and not None.\\n\\nExample:\\n\\n.. code-block:: python\\n\\n left_chunks = [ToolCallChunk(name=\\"foo\\", args=\'{\\"a\\":\', index=0)]\\n right_chunks = [ToolCallChunk(name=None, args=\'1}\', index=0)]\\n\\n (\\n AIMessageChunk(content=\\"\\", tool_call_chunks=left_chunks)\\n + AIMessageChunk(content=\\"\\", tool_call_chunks=right_chunks)\\n ).tool_call_chunks == [ToolCallChunk(name=\'foo\', args=\'{\\"a\\":1}\', index=0)]", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "index": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Index"}, "type": {"const": "tool_call_chunk", "enum": ["tool_call_chunk"], "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "index"], "title": "ToolCallChunk", "type": "object"}, "ToolMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nToolMessages contain the result of a tool invocation. Typically, the result\\nis encoded inside the `content` field.\\n\\nExample: A ToolMessage representing a result of 42 from a tool call with id\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n ToolMessage(content=\'42\', tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\')\\n\\n\\nExample: A ToolMessage where only part of the tool output is sent to the model\\n and the full output is passed in to artifact.\\n\\n .. versionadded:: 0.2.17\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n tool_output = {\\n \\"stdout\\": \\"From the graph we can see that the correlation between x and y is ...\\",\\n \\"stderr\\": None,\\n \\"artifacts\\": {\\"type\\": \\"image\\", \\"base64_data\\": \\"/9j/4gIcSU...\\"},\\n }\\n\\n ToolMessage(\\n content=tool_output[\\"stdout\\"],\\n artifact=tool_output,\\n tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\',\\n )\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "tool", "default": "tool", "enum": ["tool"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessage", "type": "object"}, "ToolMessageChunk": {"additionalProperties": true, "description": "Tool Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"const": "ToolMessageChunk", "default": "ToolMessageChunk", "enum": ["ToolMessageChunk"], "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessageChunk", "type": "object"}, "UsageMetadata": {"description": "Usage metadata for a message, such as token counts.\\n\\nThis is a standard representation of token usage that is consistent across models.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"input_tokens\\": 350,\\n \\"output_tokens\\": 240,\\n \\"total_tokens\\": 590,\\n \\"input_token_details\\": {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n },\\n \\"output_token_details\\": {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n }\\n\\n.. versionchanged:: 0.3.9\\n\\n Added ``input_token_details`` and ``output_token_details``.", "properties": {"input_tokens": {"title": "Input Tokens", "type": "integer"}, "output_tokens": {"title": "Output Tokens", "type": "integer"}, "total_tokens": {"title": "Total Tokens", "type": "integer"}, "input_token_details": {"$ref": "#/$defs/InputTokenDetails"}, "output_token_details": {"$ref": "#/$defs/OutputTokenDetails"}}, "required": ["input_tokens", "output_tokens", "total_tokens"], "title": "UsageMetadata", "type": "object"}}, "default": null, "items": {"oneOf": [{"$ref": "#/$defs/AIMessage"}, {"$ref": "#/$defs/HumanMessage"}, {"$ref": "#/$defs/ChatMessage"}, {"$ref": "#/$defs/SystemMessage"}, {"$ref": "#/$defs/FunctionMessage"}, {"$ref": "#/$defs/ToolMessage"}, {"$ref": "#/$defs/AIMessageChunk"}, {"$ref": "#/$defs/HumanMessageChunk"}, {"$ref": "#/$defs/ChatMessageChunk"}, {"$ref": "#/$defs/SystemMessageChunk"}, {"$ref": "#/$defs/FunctionMessageChunk"}, {"$ref": "#/$defs/ToolMessageChunk"}]}, "title": "LangGraphOutput", "type": "array"}' + '{"$defs": {"AIMessage": {"additionalProperties": true, "description": "Message from an AI.\\n\\nAIMessage is returned from a chat model as a response to a prompt.\\n\\nThis message represents the output of the model and consists of both\\nthe raw output as returned by the model together standardized fields\\n(e.g., tool calls, usage metadata) added by the LangChain framework.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "ai", "default": "ai", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}}, "required": ["content"], "title": "AIMessage", "type": "object"}, "AIMessageChunk": {"additionalProperties": true, "description": "Message chunk from an AI.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "AIMessageChunk", "default": "AIMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}, "tool_calls": {"default": [], "items": {"$ref": "#/$defs/ToolCall"}, "title": "Tool Calls", "type": "array"}, "invalid_tool_calls": {"default": [], "items": {"$ref": "#/$defs/InvalidToolCall"}, "title": "Invalid Tool Calls", "type": "array"}, "usage_metadata": {"anyOf": [{"$ref": "#/$defs/UsageMetadata"}, {"type": "null"}], "default": null}, "tool_call_chunks": {"default": [], "items": {"$ref": "#/$defs/ToolCallChunk"}, "title": "Tool Call Chunks", "type": "array"}}, "required": ["content"], "title": "AIMessageChunk", "type": "object"}, "ChatMessage": {"additionalProperties": true, "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "chat", "default": "chat", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessage", "type": "object"}, "ChatMessageChunk": {"additionalProperties": true, "description": "Chat Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "ChatMessageChunk", "default": "ChatMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"], "title": "ChatMessageChunk", "type": "object"}, "FunctionMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nFunctionMessage are an older version of the ToolMessage schema, and\\ndo not contain the tool_call_id field.\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "function", "default": "function", "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessage", "type": "object"}, "FunctionMessageChunk": {"additionalProperties": true, "description": "Function Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "FunctionMessageChunk", "default": "FunctionMessageChunk", "title": "Type", "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "name"], "title": "FunctionMessageChunk", "type": "object"}, "HumanMessage": {"additionalProperties": true, "description": "Message from a human.\\n\\nHumanMessages are messages that are passed in from a human to the model.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Instantiate a chat model and invoke it with the messages\\n model = ...\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "human", "default": "human", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessage", "type": "object"}, "HumanMessageChunk": {"additionalProperties": true, "description": "Human Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "HumanMessageChunk", "default": "HumanMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "example": {"default": false, "title": "Example", "type": "boolean"}}, "required": ["content"], "title": "HumanMessageChunk", "type": "object"}, "InputTokenDetails": {"description": "Breakdown of input token counts.\\n\\nDoes *not* need to sum to full input token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n }\\n\\n.. versionadded:: 0.3.9\\n\\nMay also hold extra provider-specific keys.", "properties": {"audio": {"title": "Audio", "type": "integer"}, "cache_creation": {"title": "Cache Creation", "type": "integer"}, "cache_read": {"title": "Cache Read", "type": "integer"}}, "title": "InputTokenDetails", "type": "object"}, "InvalidToolCall": {"description": "Allowance for errors made by LLM.\\n\\nHere we add an `error` key to surface errors made during generation\\n(e.g., invalid JSON arguments.)", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "error": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Error"}, "type": {"const": "invalid_tool_call", "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "error"], "title": "InvalidToolCall", "type": "object"}, "OutputTokenDetails": {"description": "Breakdown of output token counts.\\n\\nDoes *not* need to sum to full output token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n\\n.. versionadded:: 0.3.9", "properties": {"audio": {"title": "Audio", "type": "integer"}, "reasoning": {"title": "Reasoning", "type": "integer"}}, "title": "OutputTokenDetails", "type": "object"}, "SystemMessage": {"additionalProperties": true, "description": "Message for priming AI behavior.\\n\\nThe system message is usually passed in as the first of a sequence\\nof input messages.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import HumanMessage, SystemMessage\\n\\n messages = [\\n SystemMessage(\\n content=\\"You are a helpful assistant! Your name is Bob.\\"\\n ),\\n HumanMessage(\\n content=\\"What is your name?\\"\\n )\\n ]\\n\\n # Define a chat model and invoke it with the messages\\n print(model.invoke(messages))", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "system", "default": "system", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessage", "type": "object"}, "SystemMessageChunk": {"additionalProperties": true, "description": "System Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "SystemMessageChunk", "default": "SystemMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content"], "title": "SystemMessageChunk", "type": "object"}, "ToolCall": {"description": "Represents a request to call a tool.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"name\\": \\"foo\\",\\n \\"args\\": {\\"a\\": 1},\\n \\"id\\": \\"123\\"\\n }\\n\\n This represents a request to call the tool named \\"foo\\" with arguments {\\"a\\": 1}\\n and an identifier of \\"123\\".", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"additionalProperties": true, "title": "Args", "type": "object"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "type": {"const": "tool_call", "title": "Type", "type": "string"}}, "required": ["name", "args", "id"], "title": "ToolCall", "type": "object"}, "ToolCallChunk": {"description": "A chunk of a tool call (e.g., as part of a stream).\\n\\nWhen merging ToolCallChunks (e.g., via AIMessageChunk.__add__),\\nall string attributes are concatenated. Chunks are only merged if their\\nvalues of `index` are equal and not None.\\n\\nExample:\\n\\n.. code-block:: python\\n\\n left_chunks = [ToolCallChunk(name=\\"foo\\", args=\'{\\"a\\":\', index=0)]\\n right_chunks = [ToolCallChunk(name=None, args=\'1}\', index=0)]\\n\\n (\\n AIMessageChunk(content=\\"\\", tool_call_chunks=left_chunks)\\n + AIMessageChunk(content=\\"\\", tool_call_chunks=right_chunks)\\n ).tool_call_chunks == [ToolCallChunk(name=\'foo\', args=\'{\\"a\\":1}\', index=0)]", "properties": {"name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "args": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Args"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Id"}, "index": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Index"}, "type": {"const": "tool_call_chunk", "title": "Type", "type": "string"}}, "required": ["name", "args", "id", "index"], "title": "ToolCallChunk", "type": "object"}, "ToolMessage": {"additionalProperties": true, "description": "Message for passing the result of executing a tool back to a model.\\n\\nToolMessages contain the result of a tool invocation. Typically, the result\\nis encoded inside the `content` field.\\n\\nExample: A ToolMessage representing a result of 42 from a tool call with id\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n ToolMessage(content=\'42\', tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\')\\n\\n\\nExample: A ToolMessage where only part of the tool output is sent to the model\\n and the full output is passed in to artifact.\\n\\n .. versionadded:: 0.2.17\\n\\n .. code-block:: python\\n\\n from langchain_core.messages import ToolMessage\\n\\n tool_output = {\\n \\"stdout\\": \\"From the graph we can see that the correlation between x and y is ...\\",\\n \\"stderr\\": None,\\n \\"artifacts\\": {\\"type\\": \\"image\\", \\"base64_data\\": \\"/9j/4gIcSU...\\"},\\n }\\n\\n ToolMessage(\\n content=tool_output[\\"stdout\\"],\\n artifact=tool_output,\\n tool_call_id=\'call_Jja7J89XsjrOLA5r!MEOW!SL\',\\n )\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "tool", "default": "tool", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessage", "type": "object"}, "ToolMessageChunk": {"additionalProperties": true, "description": "Tool Message chunk.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"const": "ToolMessageChunk", "default": "ToolMessageChunk", "title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}, "artifact": {"default": null, "title": "Artifact"}, "status": {"default": "success", "enum": ["success", "error"], "title": "Status", "type": "string"}}, "required": ["content", "tool_call_id"], "title": "ToolMessageChunk", "type": "object"}, "UsageMetadata": {"description": "Usage metadata for a message, such as token counts.\\n\\nThis is a standard representation of token usage that is consistent across models.\\n\\nExample:\\n\\n .. code-block:: python\\n\\n {\\n \\"input_tokens\\": 350,\\n \\"output_tokens\\": 240,\\n \\"total_tokens\\": 590,\\n \\"input_token_details\\": {\\n \\"audio\\": 10,\\n \\"cache_creation\\": 200,\\n \\"cache_read\\": 100,\\n },\\n \\"output_token_details\\": {\\n \\"audio\\": 10,\\n \\"reasoning\\": 200,\\n }\\n }\\n\\n.. versionchanged:: 0.3.9\\n\\n Added ``input_token_details`` and ``output_token_details``.", "properties": {"input_tokens": {"title": "Input Tokens", "type": "integer"}, "output_tokens": {"title": "Output Tokens", "type": "integer"}, "total_tokens": {"title": "Total Tokens", "type": "integer"}, "input_token_details": {"$ref": "#/$defs/InputTokenDetails"}, "output_token_details": {"$ref": "#/$defs/OutputTokenDetails"}}, "required": ["input_tokens", "output_tokens", "total_tokens"], "title": "UsageMetadata", "type": "object"}}, "default": null, "items": {"oneOf": [{"$ref": "#/$defs/AIMessage"}, {"$ref": "#/$defs/HumanMessage"}, {"$ref": "#/$defs/ChatMessage"}, {"$ref": "#/$defs/SystemMessage"}, {"$ref": "#/$defs/FunctionMessage"}, {"$ref": "#/$defs/ToolMessage"}, {"$ref": "#/$defs/AIMessageChunk"}, {"$ref": "#/$defs/HumanMessageChunk"}, {"$ref": "#/$defs/ChatMessageChunk"}, {"$ref": "#/$defs/SystemMessageChunk"}, {"$ref": "#/$defs/FunctionMessageChunk"}, {"$ref": "#/$defs/ToolMessageChunk"}]}, "title": "LangGraphOutput", "type": "array"}' # --- # name: test_message_graph[memory].2 ''' @@ -175,10 +175,10 @@ ''' # --- # name: test_prebuilt_tool_chat - '{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}}, "required": ["messages"], "title": "LangGraphInput", "type": "object"}' + '{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "description": "The state of the agent.", "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}, "is_last_step": {"title": "Is Last Step", "type": "boolean"}, "remaining_steps": {"title": "Remaining Steps", "type": "integer"}}, "required": ["messages", "is_last_step", "remaining_steps"], "title": "AgentState", "type": "object"}' # --- # name: test_prebuilt_tool_chat.1 - '{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}}, "required": ["messages"], "title": "LangGraphOutput", "type": "object"}' + '{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "description": "The state of the agent.", "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}, "is_last_step": {"title": "Is Last Step", "type": "boolean"}, "remaining_steps": {"title": "Remaining Steps", "type": "integer"}}, "required": ["messages", "is_last_step", "remaining_steps"], "title": "AgentState", "type": "object"}' # --- # name: test_prebuilt_tool_chat.2 ''' diff --git a/libs/langgraph/tests/__snapshots__/test_pregel.ambr b/libs/langgraph/tests/__snapshots__/test_pregel.ambr index b8e226348..c1c17b29d 100644 --- a/libs/langgraph/tests/__snapshots__/test_pregel.ambr +++ b/libs/langgraph/tests/__snapshots__/test_pregel.ambr @@ -1,9 +1,9 @@ # serializer version: 1 # name: test_conditional_entrypoint_graph_state - '{"properties": {"input": {"default": null, "title": "Input", "type": "string"}, "output": {"default": null, "title": "Output", "type": "string"}, "steps": {"default": null, "items": {"type": "string"}, "title": "Steps", "type": "array"}}, "title": "LangGraphInput", "type": "object"}' + '{"properties": {"input": {"title": "Input", "type": "string"}, "output": {"title": "Output", "type": "string"}, "steps": {"items": {"type": "string"}, "title": "Steps", "type": "array"}}, "title": "AgentState", "type": "object"}' # --- # name: test_conditional_entrypoint_graph_state.1 - '{"properties": {"input": {"default": null, "title": "Input", "type": "string"}, "output": {"default": null, "title": "Output", "type": "string"}, "steps": {"default": null, "items": {"type": "string"}, "title": "Steps", "type": "array"}}, "title": "LangGraphOutput", "type": "object"}' + '{"properties": {"input": {"title": "Input", "type": "string"}, "output": {"title": "Output", "type": "string"}, "steps": {"items": {"type": "string"}, "title": "Steps", "type": "array"}}, "title": "AgentState", "type": "object"}' # --- # name: test_conditional_entrypoint_graph_state.2 ''' @@ -89,10 +89,10 @@ ''' # --- # name: test_conditional_entrypoint_to_multiple_state_graph - '{"properties": {"locations": {"items": {"type": "string"}, "title": "Locations", "type": "array"}, "results": {"items": {"type": "string"}, "title": "Results", "type": "array"}}, "required": ["locations", "results"], "title": "LangGraphInput", "type": "object"}' + '{"properties": {"locations": {"items": {"type": "string"}, "title": "Locations", "type": "array"}, "results": {"items": {"type": "string"}, "title": "Results", "type": "array"}}, "required": ["locations", "results"], "title": "OverallState", "type": "object"}' # --- # name: test_conditional_entrypoint_to_multiple_state_graph.1 - '{"properties": {"locations": {"items": {"type": "string"}, "title": "Locations", "type": "array"}, "results": {"items": {"type": "string"}, "title": "Results", "type": "array"}}, "required": ["locations", "results"], "title": "LangGraphOutput", "type": "object"}' + '{"properties": {"locations": {"items": {"type": "string"}, "title": "Locations", "type": "array"}, "results": {"items": {"type": "string"}, "title": "Results", "type": "array"}}, "required": ["locations", "results"], "title": "OverallState", "type": "object"}' # --- # name: test_conditional_entrypoint_to_multiple_state_graph.2 ''' @@ -828,10 +828,10 @@ '{"$defs": {"Config": {"properties": {"tools": {"items": {"type": "string"}, "title": "Tools", "type": "array"}}, "title": "Config", "type": "object"}}, "properties": {"configurable": {"$ref": "#/$defs/Config", "default": null}}, "title": "LangGraphConfig", "type": "object"}' # --- # name: test_state_graph_w_config_inherited_state_keys.1 - '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "enum": ["AgentAction"], "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "enum": ["AgentFinish"], "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "default": null, "title": "Agent Outcome"}, "intermediate_steps": {"default": null, "items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "required": ["input"], "title": "LangGraphInput", "type": "object"}' + '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"additionalProperties": true, "title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "title": "Agent Outcome"}, "intermediate_steps": {"items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "required": ["input", "agent_outcome"], "title": "AgentState", "type": "object"}' # --- # name: test_state_graph_w_config_inherited_state_keys.2 - '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "enum": ["AgentAction"], "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "enum": ["AgentFinish"], "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "default": null, "title": "Agent Outcome"}, "intermediate_steps": {"default": null, "items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "required": ["input"], "title": "LangGraphOutput", "type": "object"}' + '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"additionalProperties": true, "title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "title": "Agent Outcome"}, "intermediate_steps": {"items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "required": ["input", "agent_outcome"], "title": "AgentState", "type": "object"}' # --- # name: test_xray_bool ''' diff --git a/libs/langgraph/tests/test_large_cases.py b/libs/langgraph/tests/test_large_cases.py index e3108c105..81de033ba 100644 --- a/libs/langgraph/tests/test_large_cases.py +++ b/libs/langgraph/tests/test_large_cases.py @@ -569,8 +569,8 @@ def test_conditional_state_graph( app = workflow.compile() if isinstance(sync_checkpointer, InMemorySaver): - assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot - assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot + assert json.dumps(app.get_input_jsonschema()) == snapshot + assert json.dumps(app.get_output_jsonschema()) == snapshot assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot assert app.get_graph().draw_mermaid(with_styles=False) == snapshot @@ -1302,8 +1302,8 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: app = create_react_agent(model, tools) - assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot - assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot + assert json.dumps(app.get_input_jsonschema()) == snapshot + assert json.dumps(app.get_output_jsonschema()) == snapshot assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot assert app.get_graph().draw_mermaid(with_styles=False) == snapshot @@ -2483,8 +2483,8 @@ def test_message_graph( app = workflow.compile() if isinstance(sync_checkpointer, InMemorySaver): - assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot - assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot + assert json.dumps(app.get_input_jsonschema()) == snapshot + assert json.dumps(app.get_output_jsonschema()) == snapshot assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot assert app.get_graph().draw_mermaid(with_styles=False) == snapshot diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index 2fabec768..c82bec7ba 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -1722,8 +1722,8 @@ def test_conditional_entrypoint_to_multiple_state_graph( app = workflow.compile() - assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot - assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot + assert json.dumps(app.get_input_jsonschema()) == snapshot + assert json.dumps(app.get_output_jsonschema()) == snapshot assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot assert app.get_graph().draw_mermaid(with_styles=False) == snapshot @@ -1847,8 +1847,8 @@ def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion) app = builder.compile() assert json.dumps(app.config_schema().model_json_schema()) == snapshot - assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot - assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot + assert json.dumps(app.get_input_jsonschema()) == snapshot + assert json.dumps(app.get_output_jsonschema()) == snapshot assert builder.channels.keys() == {"input", "agent_outcome", "intermediate_steps"} @@ -1911,8 +1911,8 @@ def test_conditional_entrypoint_graph_state(snapshot: SnapshotAssertion) -> None app = workflow.compile() - assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot - assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot + assert json.dumps(app.get_input_jsonschema()) == snapshot + assert json.dumps(app.get_output_jsonschema()) == snapshot assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot assert app.get_graph().draw_mermaid(with_styles=False) == snapshot @@ -2506,8 +2506,8 @@ def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class_pydantic2( if isinstance(sync_checkpointer, InMemorySaver): assert app.get_graph().draw_mermaid(with_styles=False) == snapshot - assert app.get_input_schema().model_json_schema() == snapshot - assert app.get_output_schema().model_json_schema() == snapshot + assert app.get_input_jsonschema() == snapshot + assert app.get_output_jsonschema() == snapshot with pytest.raises(ValidationError): app.invoke({"query": {}}) @@ -6548,7 +6548,7 @@ def test_entrypoint_output_schema_with_return_and_save() -> None: def foo2(inputs, *, previous: Any) -> entrypoint.final: return entrypoint.final(value="foo", save=1) - assert foo2.get_output_schema().model_json_schema() == { + assert foo2.get_output_jsonschema() == { "title": "LangGraphOutput", } @@ -6556,7 +6556,7 @@ def test_entrypoint_output_schema_with_return_and_save() -> None: def foo(inputs, *, previous: Any) -> entrypoint.final[str, int]: return entrypoint.final(value="foo", save=1) - assert foo.get_output_schema().model_json_schema() == { + assert foo.get_output_jsonschema() == { "title": "LangGraphOutput", "type": "string", } @@ -6583,7 +6583,7 @@ def test_entrypoint_with_return_and_save( previous = previous or [] return entrypoint.final(value=len(previous), save=previous + [msg]) - assert foo.get_output_schema().model_json_schema() == { + assert foo.get_output_jsonschema() == { "title": "LangGraphOutput", "type": "integer", } diff --git a/libs/langgraph/tests/test_pregel_async.py b/libs/langgraph/tests/test_pregel_async.py index 6569a1d8d..dc7a9c3d0 100644 --- a/libs/langgraph/tests/test_pregel_async.py +++ b/libs/langgraph/tests/test_pregel_async.py @@ -4499,8 +4499,8 @@ async def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class_pydant if isinstance(async_checkpointer, InMemorySaver): assert app.get_graph().draw_mermaid(with_styles=False) == snapshot - assert app.get_input_schema().model_json_schema() == snapshot - assert app.get_output_schema().model_json_schema() == snapshot + assert app.get_input_jsonschema() == snapshot + assert app.get_output_jsonschema() == snapshot with pytest.raises(ValidationError): await app.ainvoke({"query": {}}) diff --git a/libs/langgraph/tests/test_state.py b/libs/langgraph/tests/test_state.py index 0ad70140d..82a3997de 100644 --- a/libs/langgraph/tests/test_state.py +++ b/libs/langgraph/tests/test_state.py @@ -163,13 +163,12 @@ def test_state_schema_optional_values(total_: bool): expected_required = set() expected_optional = {"val2", "val1"} else: - expected_required = {"val1"} - - expected_optional = {"val2"} + expected_required = {"val1", "val2"} + expected_optional = set() # The others should always have precedence based on the required annotation - expected_required |= {"val0a", "val3", "val5"} - expected_optional |= {"val0b", "val4", "val6"} + expected_required |= {"val0a", "val0b", "val3", "val5"} + expected_optional |= {"val4", "val6"} assert set(json_schema.get("required", set())) == expected_required assert ( @@ -182,11 +181,11 @@ def test_state_schema_optional_values(total_: bool): expected_required = set() expected_optional = {"out_val2", "out_val1"} else: - expected_required = {"out_val1"} - expected_optional = {"out_val2"} + expected_required = {"out_val1", "out_val2"} + expected_optional = set() - expected_required |= {"val0a", "out_val3", "out_val5"} - expected_optional |= {"val0b", "out_val4", "out_val6"} + expected_required |= {"val0a", "val0b", "out_val3", "out_val5"} + expected_optional |= {"out_val4", "out_val6"} assert set(output_schema.get("required", set())) == expected_required assert ( diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index a4f5a8600..af9948b00 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -2161,103 +2161,126 @@ wheels = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.11.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917, upload-time = "2024-09-17T15:59:54.273Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928, upload-time = "2024-09-17T15:59:51.827Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, ] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.33.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156, upload-time = "2024-09-16T16:06:44.786Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/8b/d3ae387f66277bd8104096d6ec0a145f4baa2966ebb2cad746c0920c9526/pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b", size = 1867835, upload-time = "2024-09-16T16:03:57.223Z" }, - { url = "https://files.pythonhosted.org/packages/46/76/f68272e4c3a7df8777798282c5e47d508274917f29992d84e1898f8908c7/pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166", size = 1776689, upload-time = "2024-09-16T16:03:59.266Z" }, - { url = "https://files.pythonhosted.org/packages/cc/69/5f945b4416f42ea3f3bc9d2aaec66c76084a6ff4ff27555bf9415ab43189/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb", size = 1800748, upload-time = "2024-09-16T16:04:01.011Z" }, - { url = "https://files.pythonhosted.org/packages/50/ab/891a7b0054bcc297fb02d44d05c50e68154e31788f2d9d41d0b72c89fdf7/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916", size = 1806469, upload-time = "2024-09-16T16:04:02.323Z" }, - { url = "https://files.pythonhosted.org/packages/31/7c/6e3fa122075d78f277a8431c4c608f061881b76c2b7faca01d317ee39b5d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07", size = 2002246, upload-time = "2024-09-16T16:04:03.688Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6f/22d5692b7ab63fc4acbc74de6ff61d185804a83160adba5e6cc6068e1128/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232", size = 2659404, upload-time = "2024-09-16T16:04:05.299Z" }, - { url = "https://files.pythonhosted.org/packages/11/ac/1e647dc1121c028b691028fa61a4e7477e6aeb5132628fde41dd34c1671f/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2", size = 2053940, upload-time = "2024-09-16T16:04:06.604Z" }, - { url = "https://files.pythonhosted.org/packages/91/75/984740c17f12c3ce18b5a2fcc4bdceb785cce7df1511a4ce89bca17c7e2d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f", size = 1921437, upload-time = "2024-09-16T16:04:08.071Z" }, - { url = "https://files.pythonhosted.org/packages/a0/74/13c5f606b64d93f0721e7768cd3e8b2102164866c207b8cd6f90bb15d24f/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3", size = 1966129, upload-time = "2024-09-16T16:04:10.363Z" }, - { url = "https://files.pythonhosted.org/packages/18/03/9c4aa5919457c7b57a016c1ab513b1a926ed9b2bb7915bf8e506bf65c34b/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071", size = 2110908, upload-time = "2024-09-16T16:04:12.412Z" }, - { url = "https://files.pythonhosted.org/packages/92/2c/053d33f029c5dc65e5cf44ff03ceeefb7cce908f8f3cca9265e7f9b540c8/pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119", size = 1735278, upload-time = "2024-09-16T16:04:13.732Z" }, - { url = "https://files.pythonhosted.org/packages/de/81/7dfe464eca78d76d31dd661b04b5f2036ec72ea8848dd87ab7375e185c23/pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f", size = 1917453, upload-time = "2024-09-16T16:04:15.996Z" }, - { url = "https://files.pythonhosted.org/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", size = 1865160, upload-time = "2024-09-16T16:04:18.628Z" }, - { url = "https://files.pythonhosted.org/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", size = 1776777, upload-time = "2024-09-16T16:04:20.038Z" }, - { url = "https://files.pythonhosted.org/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", size = 1799244, upload-time = "2024-09-16T16:04:21.799Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8f/89c1405176903e567c5f99ec53387449e62f1121894aa9fc2c4fdc51a59b/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607", size = 1805307, upload-time = "2024-09-16T16:04:23.324Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a5/1a194447d0da1ef492e3470680c66048fef56fc1f1a25cafbea4bc1d1c48/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd", size = 2000663, upload-time = "2024-09-16T16:04:25.203Z" }, - { url = "https://files.pythonhosted.org/packages/13/a5/1df8541651de4455e7d587cf556201b4f7997191e110bca3b589218745a5/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea", size = 2655941, upload-time = "2024-09-16T16:04:27.211Z" }, - { url = "https://files.pythonhosted.org/packages/44/31/a3899b5ce02c4316865e390107f145089876dff7e1dfc770a231d836aed8/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e", size = 2052105, upload-time = "2024-09-16T16:04:28.611Z" }, - { url = "https://files.pythonhosted.org/packages/1b/aa/98e190f8745d5ec831f6d5449344c48c0627ac5fed4e5340a44b74878f8e/pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b", size = 1919967, upload-time = "2024-09-16T16:04:30.045Z" }, - { url = "https://files.pythonhosted.org/packages/ae/35/b6e00b6abb2acfee3e8f85558c02a0822e9a8b2f2d812ea8b9079b118ba0/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0", size = 1964291, upload-time = "2024-09-16T16:04:32.376Z" }, - { url = "https://files.pythonhosted.org/packages/13/46/7bee6d32b69191cd649bbbd2361af79c472d72cb29bb2024f0b6e350ba06/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64", size = 2109666, upload-time = "2024-09-16T16:04:33.923Z" }, - { url = "https://files.pythonhosted.org/packages/39/ef/7b34f1b122a81b68ed0a7d0e564da9ccdc9a2924c8d6c6b5b11fa3a56970/pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f", size = 1732940, upload-time = "2024-09-16T16:04:35.467Z" }, - { url = "https://files.pythonhosted.org/packages/2f/76/37b7e76c645843ff46c1d73e046207311ef298d3f7b2f7d8f6ac60113071/pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3", size = 1916804, upload-time = "2024-09-16T16:04:37.06Z" }, - { url = "https://files.pythonhosted.org/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", size = 1856459, upload-time = "2024-09-16T16:04:38.438Z" }, - { url = "https://files.pythonhosted.org/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", size = 1770007, upload-time = "2024-09-16T16:04:40.229Z" }, - { url = "https://files.pythonhosted.org/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", size = 1790245, upload-time = "2024-09-16T16:04:41.794Z" }, - { url = "https://files.pythonhosted.org/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", size = 1801260, upload-time = "2024-09-16T16:04:43.991Z" }, - { url = "https://files.pythonhosted.org/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", size = 1996872, upload-time = "2024-09-16T16:04:45.593Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", size = 2661617, upload-time = "2024-09-16T16:04:47.3Z" }, - { url = "https://files.pythonhosted.org/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", size = 2071831, upload-time = "2024-09-16T16:04:48.893Z" }, - { url = "https://files.pythonhosted.org/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", size = 1917453, upload-time = "2024-09-16T16:04:51.099Z" }, - { url = "https://files.pythonhosted.org/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", size = 1968793, upload-time = "2024-09-16T16:04:52.604Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872, upload-time = "2024-09-16T16:04:54.41Z" }, - { url = "https://files.pythonhosted.org/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535, upload-time = "2024-09-16T16:04:55.828Z" }, - { url = "https://files.pythonhosted.org/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992, upload-time = "2024-09-16T16:04:57.395Z" }, - { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143, upload-time = "2024-09-16T16:04:59.062Z" }, - { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063, upload-time = "2024-09-16T16:05:00.522Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013, upload-time = "2024-09-16T16:05:02.619Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077, upload-time = "2024-09-16T16:05:04.154Z" }, - { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782, upload-time = "2024-09-16T16:05:06.931Z" }, - { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375, upload-time = "2024-09-16T16:05:08.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635, upload-time = "2024-09-16T16:05:10.456Z" }, - { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994, upload-time = "2024-09-16T16:05:12.051Z" }, - { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877, upload-time = "2024-09-16T16:05:14.021Z" }, - { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814, upload-time = "2024-09-16T16:05:15.684Z" }, - { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360, upload-time = "2024-09-16T16:05:17.258Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411, upload-time = "2024-09-16T16:05:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/7a/04/2580b2deaae37b3e30fc30c54298be938b973990b23612d6b61c7bdd01c7/pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a", size = 1868200, upload-time = "2024-09-16T16:05:48.043Z" }, - { url = "https://files.pythonhosted.org/packages/39/6e/e311bd0751505350f0cdcee3077841eb1f9253c5a1ddbad048cd9fbf7c6e/pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36", size = 1749316, upload-time = "2024-09-16T16:05:50.176Z" }, - { url = "https://files.pythonhosted.org/packages/d0/b4/95b5eb47c6dc8692508c3ca04a1f8d6f0884c9dacb34cf3357595cbe73be/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b", size = 1800880, upload-time = "2024-09-16T16:05:52.58Z" }, - { url = "https://files.pythonhosted.org/packages/da/79/41c4f817acd7f42d94cd1e16526c062a7b089f66faed4bd30852314d9a66/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323", size = 1807077, upload-time = "2024-09-16T16:05:54.313Z" }, - { url = "https://files.pythonhosted.org/packages/fb/53/d13d1eb0a97d5c06cf7a225935d471e9c241afd389a333f40c703f214973/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3", size = 2002859, upload-time = "2024-09-16T16:05:56.051Z" }, - { url = "https://files.pythonhosted.org/packages/53/7d/6b8a1eff453774b46cac8c849e99455b27167971a003212f668e94bc4c9c/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df", size = 2661437, upload-time = "2024-09-16T16:05:57.96Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ea/8820f57f0b46e6148ee42d8216b15e8fe3b360944284bbc705bf34fac888/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c", size = 2054404, upload-time = "2024-09-16T16:05:59.63Z" }, - { url = "https://files.pythonhosted.org/packages/0f/36/d4ae869e473c3c7868e1cd1e2a1b9e13bce5cd1a7d287f6ac755a0b1575e/pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55", size = 1921680, upload-time = "2024-09-16T16:06:01.554Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/eed5c65b80c4ac4494117e2101973b45fc655774ef647d17dde40a70f7d2/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040", size = 1966093, upload-time = "2024-09-16T16:06:03.259Z" }, - { url = "https://files.pythonhosted.org/packages/e8/c8/1d42ce51d65e571ab53d466cae83434325a126811df7ce4861d9d97bee4b/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605", size = 2111437, upload-time = "2024-09-16T16:06:05.016Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c9/7fea9d13383c2ec6865919e09cffe44ab77e911eb281b53a4deaafd4c8e8/pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6", size = 1735049, upload-time = "2024-09-16T16:06:06.709Z" }, - { url = "https://files.pythonhosted.org/packages/98/95/dd7045c4caa2b73d0bf3b989d66b23cfbb7a0ef14ce99db15677a000a953/pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29", size = 1920180, upload-time = "2024-09-16T16:06:08.528Z" }, - { url = "https://files.pythonhosted.org/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135, upload-time = "2024-09-16T16:06:10.45Z" }, - { url = "https://files.pythonhosted.org/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583, upload-time = "2024-09-16T16:06:12.298Z" }, - { url = "https://files.pythonhosted.org/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637, upload-time = "2024-09-16T16:06:14.092Z" }, - { url = "https://files.pythonhosted.org/packages/29/18/812222b6d18c2d13eebbb0f7cdc170a408d9ced65794fdb86147c77e1982/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068", size = 1941963, upload-time = "2024-09-16T16:06:16.757Z" }, - { url = "https://files.pythonhosted.org/packages/0f/36/c1f3642ac3f05e6bb4aec3ffc399fa3f84895d259cf5f0ce3054b7735c29/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801", size = 1915332, upload-time = "2024-09-16T16:06:18.677Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", size = 1957926, upload-time = "2024-09-16T16:06:20.591Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", size = 2100342, upload-time = "2024-09-16T16:06:22.888Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", size = 1920344, upload-time = "2024-09-16T16:06:24.849Z" }, - { url = "https://files.pythonhosted.org/packages/32/fd/ac9cdfaaa7cf2d32590b807d900612b39acb25e5527c3c7e482f0553025b/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21", size = 1857850, upload-time = "2024-09-16T16:06:26.828Z" }, - { url = "https://files.pythonhosted.org/packages/08/fe/038f4b2bcae325ea643c8ad353191187a4c92a9c3b913b139289a6f2ef04/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb", size = 1740265, upload-time = "2024-09-16T16:06:28.872Z" }, - { url = "https://files.pythonhosted.org/packages/51/14/b215c9c3cbd1edaaea23014d4b3304260823f712d3fdee52549b19b25d62/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59", size = 1793912, upload-time = "2024-09-16T16:06:30.925Z" }, - { url = "https://files.pythonhosted.org/packages/62/de/2c3ad79b63ba564878cbce325be725929ba50089cd5156f89ea5155cb9b3/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577", size = 1942870, upload-time = "2024-09-16T16:06:33.298Z" }, - { url = "https://files.pythonhosted.org/packages/cb/55/c222af19e4644c741b3f3fe4fd8bbb6b4cdca87d8a49258b61cf7826b19e/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744", size = 1915610, upload-time = "2024-09-16T16:06:35.5Z" }, - { url = "https://files.pythonhosted.org/packages/c4/7a/9a8760692a6f76bb54bcd43f245ff3d8b603db695899bbc624099c00af80/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef", size = 1958403, upload-time = "2024-09-16T16:06:37.722Z" }, - { url = "https://files.pythonhosted.org/packages/4c/91/9b03166feb914bb5698e2f6499e07c2617e2eebf69f9374d0358d7eb2009/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8", size = 2101154, upload-time = "2024-09-16T16:06:40.376Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d9/1d7ecb98318da4cb96986daaf0e20d66f1651d0aeb9e2d4435b916ce031d/pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e", size = 1920855, upload-time = "2024-09-16T16:06:42.707Z" }, + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/53/ea/bbe9095cdd771987d13c82d104a9c8559ae9aec1e29f139e286fd2e9256e/pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d", size = 2028677, upload-time = "2025-04-23T18:32:27.227Z" }, + { url = "https://files.pythonhosted.org/packages/49/1d/4ac5ed228078737d457a609013e8f7edc64adc37b91d619ea965758369e5/pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954", size = 1864735, upload-time = "2025-04-23T18:32:29.019Z" }, + { url = "https://files.pythonhosted.org/packages/23/9a/2e70d6388d7cda488ae38f57bc2f7b03ee442fbcf0d75d848304ac7e405b/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb", size = 1898467, upload-time = "2025-04-23T18:32:31.119Z" }, + { url = "https://files.pythonhosted.org/packages/ff/2e/1568934feb43370c1ffb78a77f0baaa5a8b6897513e7a91051af707ffdc4/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7", size = 1983041, upload-time = "2025-04-23T18:32:33.655Z" }, + { url = "https://files.pythonhosted.org/packages/01/1a/1a1118f38ab64eac2f6269eb8c120ab915be30e387bb561e3af904b12499/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4", size = 2136503, upload-time = "2025-04-23T18:32:35.519Z" }, + { url = "https://files.pythonhosted.org/packages/5c/da/44754d1d7ae0f22d6d3ce6c6b1486fc07ac2c524ed8f6eca636e2e1ee49b/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b", size = 2736079, upload-time = "2025-04-23T18:32:37.659Z" }, + { url = "https://files.pythonhosted.org/packages/4d/98/f43cd89172220ec5aa86654967b22d862146bc4d736b1350b4c41e7c9c03/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3", size = 2006508, upload-time = "2025-04-23T18:32:39.637Z" }, + { url = "https://files.pythonhosted.org/packages/2b/cc/f77e8e242171d2158309f830f7d5d07e0531b756106f36bc18712dc439df/pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a", size = 2113693, upload-time = "2025-04-23T18:32:41.818Z" }, + { url = "https://files.pythonhosted.org/packages/54/7a/7be6a7bd43e0a47c147ba7fbf124fe8aaf1200bc587da925509641113b2d/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782", size = 2074224, upload-time = "2025-04-23T18:32:44.033Z" }, + { url = "https://files.pythonhosted.org/packages/2a/07/31cf8fadffbb03be1cb520850e00a8490c0927ec456e8293cafda0726184/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9", size = 2245403, upload-time = "2025-04-23T18:32:45.836Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8d/bbaf4c6721b668d44f01861f297eb01c9b35f612f6b8e14173cb204e6240/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e", size = 2242331, upload-time = "2025-04-23T18:32:47.618Z" }, + { url = "https://files.pythonhosted.org/packages/bb/93/3cc157026bca8f5006250e74515119fcaa6d6858aceee8f67ab6dc548c16/pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9", size = 1910571, upload-time = "2025-04-23T18:32:49.401Z" }, + { url = "https://files.pythonhosted.org/packages/5b/90/7edc3b2a0d9f0dda8806c04e511a67b0b7a41d2187e2003673a996fb4310/pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3", size = 1956504, upload-time = "2025-04-23T18:32:51.287Z" }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, + { url = "https://files.pythonhosted.org/packages/08/98/dbf3fdfabaf81cda5622154fda78ea9965ac467e3239078e0dcd6df159e7/pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101", size = 2024034, upload-time = "2025-04-23T18:33:32.843Z" }, + { url = "https://files.pythonhosted.org/packages/8d/99/7810aa9256e7f2ccd492590f86b79d370df1e9292f1f80b000b6a75bd2fb/pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64", size = 1858578, upload-time = "2025-04-23T18:33:34.912Z" }, + { url = "https://files.pythonhosted.org/packages/d8/60/bc06fa9027c7006cc6dd21e48dbf39076dc39d9abbaf718a1604973a9670/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d", size = 1892858, upload-time = "2025-04-23T18:33:36.933Z" }, + { url = "https://files.pythonhosted.org/packages/f2/40/9d03997d9518816c68b4dfccb88969756b9146031b61cd37f781c74c9b6a/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535", size = 2068498, upload-time = "2025-04-23T18:33:38.997Z" }, + { url = "https://files.pythonhosted.org/packages/d8/62/d490198d05d2d86672dc269f52579cad7261ced64c2df213d5c16e0aecb1/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d", size = 2108428, upload-time = "2025-04-23T18:33:41.18Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ec/4cd215534fd10b8549015f12ea650a1a973da20ce46430b68fc3185573e8/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6", size = 2069854, upload-time = "2025-04-23T18:33:43.446Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1a/abbd63d47e1d9b0d632fee6bb15785d0889c8a6e0a6c3b5a8e28ac1ec5d2/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca", size = 2237859, upload-time = "2025-04-23T18:33:45.56Z" }, + { url = "https://files.pythonhosted.org/packages/80/1c/fa883643429908b1c90598fd2642af8839efd1d835b65af1f75fba4d94fe/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039", size = 2239059, upload-time = "2025-04-23T18:33:47.735Z" }, + { url = "https://files.pythonhosted.org/packages/d4/29/3cade8a924a61f60ccfa10842f75eb12787e1440e2b8660ceffeb26685e7/pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27", size = 2066661, upload-time = "2025-04-23T18:33:49.995Z" }, ] [[package]] @@ -3057,6 +3080,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, ] +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, +] + [[package]] name = "tzdata" version = "2025.2" diff --git a/libs/prebuilt/pyproject.toml b/libs/prebuilt/pyproject.toml index c294cd7fa..a4d389343 100644 --- a/libs/prebuilt/pyproject.toml +++ b/libs/prebuilt/pyproject.toml @@ -12,7 +12,7 @@ readme = "README.md" license = "MIT" license-files = ['LICENSE'] dependencies = [ - "langgraph-checkpoint>=2.1.0", + "langgraph-checkpoint>=2.1.0,<3.0.0", "langchain-core>=0.3.67", ] diff --git a/libs/sdk-js/package.json b/libs/sdk-js/package.json index 24862107e..bc2b2fa5d 100644 --- a/libs/sdk-js/package.json +++ b/libs/sdk-js/package.json @@ -1,6 +1,6 @@ { "name": "@langchain/langgraph-sdk", - "version": "0.0.88", + "version": "0.0.89", "description": "Client library for interacting with the LangGraph API", "type": "module", "packageManager": "yarn@1.22.19", diff --git a/libs/sdk-js/src/types.messages.ts b/libs/sdk-js/src/types.messages.ts index 49123cf1a..26b929535 100644 --- a/libs/sdk-js/src/types.messages.ts +++ b/libs/sdk-js/src/types.messages.ts @@ -13,16 +13,22 @@ type MessageContent = string | MessageContentComplex[]; */ type MessageAdditionalKwargs = Record; -export type HumanMessage = { - type: "human"; - id?: string | undefined; +type BaseMessage = { + additional_kwargs?: MessageAdditionalKwargs | undefined; content: MessageContent; + id?: string | undefined; + name?: string | undefined; + response_metadata?: Record | undefined; }; -export type AIMessage = { +export type HumanMessage = BaseMessage & { + type: "human"; + example?: boolean | undefined; +}; + +export type AIMessage = BaseMessage & { type: "ai"; - id?: string | undefined; - content: MessageContent; + example?: boolean | undefined; tool_calls?: | { name: string; @@ -57,19 +63,12 @@ export type AIMessage = { | undefined; } | undefined; - additional_kwargs?: MessageAdditionalKwargs | undefined; - response_metadata?: Record | undefined; }; -export type ToolMessage = { +export type ToolMessage = BaseMessage & { type: "tool"; - name?: string | undefined; - id?: string | undefined; - content: MessageContent; status?: "error" | "success" | undefined; tool_call_id: string; - additional_kwargs?: MessageAdditionalKwargs | undefined; - response_metadata?: Record | undefined; /** * Artifact of the Tool execution which is not meant to be sent to the model. * @@ -81,22 +80,16 @@ export type ToolMessage = { artifact?: any; }; -export type SystemMessage = { +export type SystemMessage = BaseMessage & { type: "system"; - id?: string | undefined; - content: MessageContent; }; -export type FunctionMessage = { +export type FunctionMessage = BaseMessage & { type: "function"; - id?: string | undefined; - content: MessageContent; }; -export type RemoveMessage = { +export type RemoveMessage = BaseMessage & { type: "remove"; - id: string; - content: MessageContent; }; export type Message = From 7a8f29847b65561cf828fc15922e806d0de1e345 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Thu, 3 Jul 2025 19:33:39 -0400 Subject: [PATCH 04/22] fix conflicts in state.py (#5340) * fix conflicts * lockfile fixes --- libs/langgraph/langgraph/graph/state.py | 13 - libs/langgraph/uv.lock | 472 ++++++++++++------------ 2 files changed, 242 insertions(+), 243 deletions(-) diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index b0311d8ab..8a094c6c4 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -831,10 +831,6 @@ class CompiledStateGraph( self.builder = builder self.schema_to_mapper = schema_to_mapper - def get_input_jsonschema( - self, config: RunnableConfig | None = None - ) -> dict[str, Any]: - return _get_json_schema( def get_input_jsonschema( self, config: RunnableConfig | None = None ) -> dict[str, Any]: @@ -845,11 +841,8 @@ class CompiledStateGraph( name=self.get_name("Input"), ) - def get_output_jsonschema( def get_output_jsonschema( self, config: RunnableConfig | None = None - ) -> dict[str, Any]: - return _get_json_schema( ) -> dict[str, Any]: return _get_json_schema( typ=self.builder.output_schema, @@ -1318,19 +1311,14 @@ def _is_field_managed_value(name: str, typ: type[Any]) -> ManagedValueSpec | Non return None -def _get_json_schema( def _get_json_schema( typ: type, schemas: dict, channels: dict, name: str, -) -> dict[str, Any]: ) -> dict[str, Any]: if isclass(typ) and issubclass(typ, BaseModel): return typ.model_json_schema() - elif is_typeddict(typ): - return TypeAdapter(typ).json_schema() - return typ.model_json_schema() elif is_typeddict(typ): return TypeAdapter(typ).json_schema() else: @@ -1340,7 +1328,6 @@ def _get_json_schema( name, root=(channels[keys[0]].UpdateType, None), ).model_json_schema() - ).model_json_schema() else: return create_model( name, diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index 4404a7d67..21c33a15e 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -2,8 +2,7 @@ version = 1 revision = 2 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version >= '3.11'", "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] @@ -352,8 +351,7 @@ name = "click" version = "8.2.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] dependencies = [ @@ -396,76 +394,76 @@ wheels = [ [[package]] name = "coverage" -version = "7.9.1" +version = "7.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/e0/98670a80884f64578f0c22cd70c5e81a6e07b08167721c7487b4d70a7ca0/coverage-7.9.1.tar.gz", hash = "sha256:6cf43c78c4282708a28e466316935ec7489a9c487518a77fa68f716c67909cec", size = 813650, upload-time = "2025-06-13T13:02:28.627Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/78/1c1c5ec58f16817c09cbacb39783c3655d54a221b6552f47ff5ac9297603/coverage-7.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc94d7c5e8423920787c33d811c0be67b7be83c705f001f7180c7b186dcf10ca", size = 212028, upload-time = "2025-06-13T13:00:29.293Z" }, - { url = "https://files.pythonhosted.org/packages/98/db/e91b9076f3a888e3b4ad7972ea3842297a52cc52e73fd1e529856e473510/coverage-7.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16aa0830d0c08a2c40c264cef801db8bc4fc0e1892782e45bcacbd5889270509", size = 212420, upload-time = "2025-06-13T13:00:34.027Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d0/2b3733412954576b0aea0a16c3b6b8fbe95eb975d8bfa10b07359ead4252/coverage-7.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf95981b126f23db63e9dbe4cf65bd71f9a6305696fa5e2262693bc4e2183f5b", size = 241529, upload-time = "2025-06-13T13:00:35.786Z" }, - { url = "https://files.pythonhosted.org/packages/b3/00/5e2e5ae2e750a872226a68e984d4d3f3563cb01d1afb449a17aa819bc2c4/coverage-7.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f05031cf21699785cd47cb7485f67df619e7bcdae38e0fde40d23d3d0210d3c3", size = 239403, upload-time = "2025-06-13T13:00:37.399Z" }, - { url = "https://files.pythonhosted.org/packages/37/3b/a2c27736035156b0a7c20683afe7df498480c0dfdf503b8c878a21b6d7fb/coverage-7.9.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4fbcab8764dc072cb651a4bcda4d11fb5658a1d8d68842a862a6610bd8cfa3", size = 240548, upload-time = "2025-06-13T13:00:39.647Z" }, - { url = "https://files.pythonhosted.org/packages/98/f5/13d5fc074c3c0e0dc80422d9535814abf190f1254d7c3451590dc4f8b18c/coverage-7.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16649a7330ec307942ed27d06ee7e7a38417144620bb3d6e9a18ded8a2d3e5", size = 240459, upload-time = "2025-06-13T13:00:40.934Z" }, - { url = "https://files.pythonhosted.org/packages/36/24/24b9676ea06102df824c4a56ffd13dc9da7904478db519efa877d16527d5/coverage-7.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cea0a27a89e6432705fffc178064503508e3c0184b4f061700e771a09de58187", size = 239128, upload-time = "2025-06-13T13:00:42.343Z" }, - { url = "https://files.pythonhosted.org/packages/be/05/242b7a7d491b369ac5fee7908a6e5ba42b3030450f3ad62c645b40c23e0e/coverage-7.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e980b53a959fa53b6f05343afbd1e6f44a23ed6c23c4b4c56c6662bbb40c82ce", size = 239402, upload-time = "2025-06-13T13:00:43.634Z" }, - { url = "https://files.pythonhosted.org/packages/73/e0/4de7f87192fa65c9c8fbaeb75507e124f82396b71de1797da5602898be32/coverage-7.9.1-cp310-cp310-win32.whl", hash = "sha256:70760b4c5560be6ca70d11f8988ee6542b003f982b32f83d5ac0b72476607b70", size = 214518, upload-time = "2025-06-13T13:00:45.622Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ab/5e4e2fe458907d2a65fab62c773671cfc5ac704f1e7a9ddd91996f66e3c2/coverage-7.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a66e8f628b71f78c0e0342003d53b53101ba4e00ea8dabb799d9dba0abbbcebe", size = 215436, upload-time = "2025-06-13T13:00:47.245Z" }, - { url = "https://files.pythonhosted.org/packages/60/34/fa69372a07d0903a78ac103422ad34db72281c9fc625eba94ac1185da66f/coverage-7.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95c765060e65c692da2d2f51a9499c5e9f5cf5453aeaf1420e3fc847cc060582", size = 212146, upload-time = "2025-06-13T13:00:48.496Z" }, - { url = "https://files.pythonhosted.org/packages/27/f0/da1894915d2767f093f081c42afeba18e760f12fdd7a2f4acbe00564d767/coverage-7.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba383dc6afd5ec5b7a0d0c23d38895db0e15bcba7fb0fa8901f245267ac30d86", size = 212536, upload-time = "2025-06-13T13:00:51.535Z" }, - { url = "https://files.pythonhosted.org/packages/10/d5/3fc33b06e41e390f88eef111226a24e4504d216ab8e5d1a7089aa5a3c87a/coverage-7.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37ae0383f13cbdcf1e5e7014489b0d71cc0106458878ccde52e8a12ced4298ed", size = 245092, upload-time = "2025-06-13T13:00:52.883Z" }, - { url = "https://files.pythonhosted.org/packages/0a/39/7aa901c14977aba637b78e95800edf77f29f5a380d29768c5b66f258305b/coverage-7.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69aa417a030bf11ec46149636314c24c8d60fadb12fc0ee8f10fda0d918c879d", size = 242806, upload-time = "2025-06-13T13:00:54.571Z" }, - { url = "https://files.pythonhosted.org/packages/43/fc/30e5cfeaf560b1fc1989227adedc11019ce4bb7cce59d65db34fe0c2d963/coverage-7.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a4be2a28656afe279b34d4f91c3e26eccf2f85500d4a4ff0b1f8b54bf807338", size = 244610, upload-time = "2025-06-13T13:00:56.932Z" }, - { url = "https://files.pythonhosted.org/packages/bf/15/cca62b13f39650bc87b2b92bb03bce7f0e79dd0bf2c7529e9fc7393e4d60/coverage-7.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:382e7ddd5289f140259b610e5f5c58f713d025cb2f66d0eb17e68d0a94278875", size = 244257, upload-time = "2025-06-13T13:00:58.545Z" }, - { url = "https://files.pythonhosted.org/packages/cd/1a/c0f2abe92c29e1464dbd0ff9d56cb6c88ae2b9e21becdb38bea31fcb2f6c/coverage-7.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e5532482344186c543c37bfad0ee6069e8ae4fc38d073b8bc836fc8f03c9e250", size = 242309, upload-time = "2025-06-13T13:00:59.836Z" }, - { url = "https://files.pythonhosted.org/packages/57/8d/c6fd70848bd9bf88fa90df2af5636589a8126d2170f3aade21ed53f2b67a/coverage-7.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a39d18b3f50cc121d0ce3838d32d58bd1d15dab89c910358ebefc3665712256c", size = 242898, upload-time = "2025-06-13T13:01:02.506Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9e/6ca46c7bff4675f09a66fe2797cd1ad6a24f14c9c7c3b3ebe0470a6e30b8/coverage-7.9.1-cp311-cp311-win32.whl", hash = "sha256:dd24bd8d77c98557880def750782df77ab2b6885a18483dc8588792247174b32", size = 214561, upload-time = "2025-06-13T13:01:04.012Z" }, - { url = "https://files.pythonhosted.org/packages/a1/30/166978c6302010742dabcdc425fa0f938fa5a800908e39aff37a7a876a13/coverage-7.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b55ad10a35a21b8015eabddc9ba31eb590f54adc9cd39bcf09ff5349fd52125", size = 215493, upload-time = "2025-06-13T13:01:05.702Z" }, - { url = "https://files.pythonhosted.org/packages/60/07/a6d2342cd80a5be9f0eeab115bc5ebb3917b4a64c2953534273cf9bc7ae6/coverage-7.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ad935f0016be24c0e97fc8c40c465f9c4b85cbbe6eac48934c0dc4d2568321e", size = 213869, upload-time = "2025-06-13T13:01:09.345Z" }, - { url = "https://files.pythonhosted.org/packages/68/d9/7f66eb0a8f2fce222de7bdc2046ec41cb31fe33fb55a330037833fb88afc/coverage-7.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8de12b4b87c20de895f10567639c0797b621b22897b0af3ce4b4e204a743626", size = 212336, upload-time = "2025-06-13T13:01:10.909Z" }, - { url = "https://files.pythonhosted.org/packages/20/20/e07cb920ef3addf20f052ee3d54906e57407b6aeee3227a9c91eea38a665/coverage-7.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5add197315a054e92cee1b5f686a2bcba60c4c3e66ee3de77ace6c867bdee7cb", size = 212571, upload-time = "2025-06-13T13:01:12.518Z" }, - { url = "https://files.pythonhosted.org/packages/78/f8/96f155de7e9e248ca9c8ff1a40a521d944ba48bec65352da9be2463745bf/coverage-7.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600a1d4106fe66f41e5d0136dfbc68fe7200a5cbe85610ddf094f8f22e1b0300", size = 246377, upload-time = "2025-06-13T13:01:14.87Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cf/1d783bd05b7bca5c10ded5f946068909372e94615a4416afadfe3f63492d/coverage-7.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a876e4c3e5a2a1715a6608906aa5a2e0475b9c0f68343c2ada98110512ab1d8", size = 243394, upload-time = "2025-06-13T13:01:16.23Z" }, - { url = "https://files.pythonhosted.org/packages/02/dd/e7b20afd35b0a1abea09fb3998e1abc9f9bd953bee548f235aebd2b11401/coverage-7.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f34346dd63010453922c8e628a52ea2d2ccd73cb2487f7700ac531b247c8a5", size = 245586, upload-time = "2025-06-13T13:01:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/4e/38/b30b0006fea9d617d1cb8e43b1bc9a96af11eff42b87eb8c716cf4d37469/coverage-7.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:888f8eee13f2377ce86d44f338968eedec3291876b0b8a7289247ba52cb984cd", size = 245396, upload-time = "2025-06-13T13:01:19.164Z" }, - { url = "https://files.pythonhosted.org/packages/31/e4/4d8ec1dc826e16791f3daf1b50943e8e7e1eb70e8efa7abb03936ff48418/coverage-7.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9969ef1e69b8c8e1e70d591f91bbc37fc9a3621e447525d1602801a24ceda898", size = 243577, upload-time = "2025-06-13T13:01:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/25/f4/b0e96c5c38e6e40ef465c4bc7f138863e2909c00e54a331da335faf0d81a/coverage-7.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60c458224331ee3f1a5b472773e4a085cc27a86a0b48205409d364272d67140d", size = 244809, upload-time = "2025-06-13T13:01:24.143Z" }, - { url = "https://files.pythonhosted.org/packages/8a/65/27e0a1fa5e2e5079bdca4521be2f5dabf516f94e29a0defed35ac2382eb2/coverage-7.9.1-cp312-cp312-win32.whl", hash = "sha256:5f646a99a8c2b3ff4c6a6e081f78fad0dde275cd59f8f49dc4eab2e394332e74", size = 214724, upload-time = "2025-06-13T13:01:25.435Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a8/d5b128633fd1a5e0401a4160d02fa15986209a9e47717174f99dc2f7166d/coverage-7.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:30f445f85c353090b83e552dcbbdad3ec84c7967e108c3ae54556ca69955563e", size = 215535, upload-time = "2025-06-13T13:01:27.861Z" }, - { url = "https://files.pythonhosted.org/packages/a3/37/84bba9d2afabc3611f3e4325ee2c6a47cd449b580d4a606b240ce5a6f9bf/coverage-7.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:af41da5dca398d3474129c58cb2b106a5d93bbb196be0d307ac82311ca234342", size = 213904, upload-time = "2025-06-13T13:01:29.202Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a7/a027970c991ca90f24e968999f7d509332daf6b8c3533d68633930aaebac/coverage-7.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:31324f18d5969feef7344a932c32428a2d1a3e50b15a6404e97cba1cc9b2c631", size = 212358, upload-time = "2025-06-13T13:01:30.909Z" }, - { url = "https://files.pythonhosted.org/packages/f2/48/6aaed3651ae83b231556750280682528fea8ac7f1232834573472d83e459/coverage-7.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c804506d624e8a20fb3108764c52e0eef664e29d21692afa375e0dd98dc384f", size = 212620, upload-time = "2025-06-13T13:01:32.256Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/f4b613f3b44d8b9f144847c89151992b2b6b79cbc506dee89ad0c35f209d/coverage-7.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef64c27bc40189f36fcc50c3fb8f16ccda73b6a0b80d9bd6e6ce4cffcd810bbd", size = 245788, upload-time = "2025-06-13T13:01:33.948Z" }, - { url = "https://files.pythonhosted.org/packages/04/d2/de4fdc03af5e4e035ef420ed26a703c6ad3d7a07aff2e959eb84e3b19ca8/coverage-7.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4fe2348cc6ec372e25adec0219ee2334a68d2f5222e0cba9c0d613394e12d86", size = 243001, upload-time = "2025-06-13T13:01:35.285Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e8/eed18aa5583b0423ab7f04e34659e51101135c41cd1dcb33ac1d7013a6d6/coverage-7.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34ed2186fe52fcc24d4561041979a0dec69adae7bce2ae8d1c49eace13e55c43", size = 244985, upload-time = "2025-06-13T13:01:36.712Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/ae9e5cce8885728c934eaa58ebfa8281d488ef2afa81c3dbc8ee9e6d80db/coverage-7.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:25308bd3d00d5eedd5ae7d4357161f4df743e3c0240fa773ee1b0f75e6c7c0f1", size = 245152, upload-time = "2025-06-13T13:01:39.303Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c8/272c01ae792bb3af9b30fac14d71d63371db227980682836ec388e2c57c0/coverage-7.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73e9439310f65d55a5a1e0564b48e34f5369bee943d72c88378f2d576f5a5751", size = 243123, upload-time = "2025-06-13T13:01:40.727Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d0/2819a1e3086143c094ab446e3bdf07138527a7b88cb235c488e78150ba7a/coverage-7.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37ab6be0859141b53aa89412a82454b482c81cf750de4f29223d52268a86de67", size = 244506, upload-time = "2025-06-13T13:01:42.184Z" }, - { url = "https://files.pythonhosted.org/packages/8b/4e/9f6117b89152df7b6112f65c7a4ed1f2f5ec8e60c4be8f351d91e7acc848/coverage-7.9.1-cp313-cp313-win32.whl", hash = "sha256:64bdd969456e2d02a8b08aa047a92d269c7ac1f47e0c977675d550c9a0863643", size = 214766, upload-time = "2025-06-13T13:01:44.482Z" }, - { url = "https://files.pythonhosted.org/packages/27/0f/4b59f7c93b52c2c4ce7387c5a4e135e49891bb3b7408dcc98fe44033bbe0/coverage-7.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:be9e3f68ca9edb897c2184ad0eee815c635565dbe7a0e7e814dc1f7cbab92c0a", size = 215568, upload-time = "2025-06-13T13:01:45.772Z" }, - { url = "https://files.pythonhosted.org/packages/09/1e/9679826336f8c67b9c39a359352882b24a8a7aee48d4c9cad08d38d7510f/coverage-7.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:1c503289ffef1d5105d91bbb4d62cbe4b14bec4d13ca225f9c73cde9bb46207d", size = 213939, upload-time = "2025-06-13T13:01:47.087Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5b/5c6b4e7a407359a2e3b27bf9c8a7b658127975def62077d441b93a30dbe8/coverage-7.9.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0b3496922cb5f4215bf5caaef4cf12364a26b0be82e9ed6d050f3352cf2d7ef0", size = 213079, upload-time = "2025-06-13T13:01:48.554Z" }, - { url = "https://files.pythonhosted.org/packages/a2/22/1e2e07279fd2fd97ae26c01cc2186e2258850e9ec125ae87184225662e89/coverage-7.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9565c3ab1c93310569ec0d86b017f128f027cab0b622b7af288696d7ed43a16d", size = 213299, upload-time = "2025-06-13T13:01:49.997Z" }, - { url = "https://files.pythonhosted.org/packages/14/c0/4c5125a4b69d66b8c85986d3321520f628756cf524af810baab0790c7647/coverage-7.9.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2241ad5dbf79ae1d9c08fe52b36d03ca122fb9ac6bca0f34439e99f8327ac89f", size = 256535, upload-time = "2025-06-13T13:01:51.314Z" }, - { url = "https://files.pythonhosted.org/packages/81/8b/e36a04889dda9960be4263e95e777e7b46f1bb4fc32202612c130a20c4da/coverage-7.9.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb5838701ca68b10ebc0937dbd0eb81974bac54447c55cd58dea5bca8451029", size = 252756, upload-time = "2025-06-13T13:01:54.403Z" }, - { url = "https://files.pythonhosted.org/packages/98/82/be04eff8083a09a4622ecd0e1f31a2c563dbea3ed848069e7b0445043a70/coverage-7.9.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a25f814591a8c0c5372c11ac8967f669b97444c47fd794926e175c4047ece", size = 254912, upload-time = "2025-06-13T13:01:56.769Z" }, - { url = "https://files.pythonhosted.org/packages/0f/25/c26610a2c7f018508a5ab958e5b3202d900422cf7cdca7670b6b8ca4e8df/coverage-7.9.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2d04b16a6062516df97969f1ae7efd0de9c31eb6ebdceaa0d213b21c0ca1a683", size = 256144, upload-time = "2025-06-13T13:01:58.19Z" }, - { url = "https://files.pythonhosted.org/packages/c5/8b/fb9425c4684066c79e863f1e6e7ecebb49e3a64d9f7f7860ef1688c56f4a/coverage-7.9.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7931b9e249edefb07cd6ae10c702788546341d5fe44db5b6108a25da4dca513f", size = 254257, upload-time = "2025-06-13T13:01:59.645Z" }, - { url = "https://files.pythonhosted.org/packages/93/df/27b882f54157fc1131e0e215b0da3b8d608d9b8ef79a045280118a8f98fe/coverage-7.9.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52e92b01041151bf607ee858e5a56c62d4b70f4dac85b8c8cb7fb8a351ab2c10", size = 255094, upload-time = "2025-06-13T13:02:01.37Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/cad1c3dbed8b3ee9e16fa832afe365b4e3eeab1fb6edb65ebbf745eabc92/coverage-7.9.1-cp313-cp313t-win32.whl", hash = "sha256:684e2110ed84fd1ca5f40e89aa44adf1729dc85444004111aa01866507adf363", size = 215437, upload-time = "2025-06-13T13:02:02.905Z" }, - { url = "https://files.pythonhosted.org/packages/99/4d/fad293bf081c0e43331ca745ff63673badc20afea2104b431cdd8c278b4c/coverage-7.9.1-cp313-cp313t-win_amd64.whl", hash = "sha256:437c576979e4db840539674e68c84b3cda82bc824dd138d56bead1435f1cb5d7", size = 216605, upload-time = "2025-06-13T13:02:05.638Z" }, - { url = "https://files.pythonhosted.org/packages/1f/56/4ee027d5965fc7fc126d7ec1187529cc30cc7d740846e1ecb5e92d31b224/coverage-7.9.1-cp313-cp313t-win_arm64.whl", hash = "sha256:18a0912944d70aaf5f399e350445738a1a20b50fbea788f640751c2ed9208b6c", size = 214392, upload-time = "2025-06-13T13:02:07.642Z" }, - { url = "https://files.pythonhosted.org/packages/a5/d6/c41dd9b02bf16ec001aaf1cbef665537606899a3db1094e78f5ae17540ca/coverage-7.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f424507f57878e424d9a95dc4ead3fbdd72fd201e404e861e465f28ea469951", size = 212029, upload-time = "2025-06-13T13:02:09.058Z" }, - { url = "https://files.pythonhosted.org/packages/f8/c0/40420d81d731f84c3916dcdf0506b3e6c6570817bff2576b83f780914ae6/coverage-7.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:535fde4001b2783ac80865d90e7cc7798b6b126f4cd8a8c54acfe76804e54e58", size = 212407, upload-time = "2025-06-13T13:02:11.151Z" }, - { url = "https://files.pythonhosted.org/packages/9b/87/f0db7d62d0e09f14d6d2f6ae8c7274a2f09edf74895a34b412a0601e375a/coverage-7.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02532fd3290bb8fa6bec876520842428e2a6ed6c27014eca81b031c2d30e3f71", size = 241160, upload-time = "2025-06-13T13:02:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/a9/b7/3337c064f058a5d7696c4867159651a5b5fb01a5202bcf37362f0c51400e/coverage-7.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56f5eb308b17bca3bbff810f55ee26d51926d9f89ba92707ee41d3c061257e55", size = 239027, upload-time = "2025-06-13T13:02:14.294Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/5898a283f66d1bd413c32c2e0e05408196fd4f37e206e2b06c6e0c626e0e/coverage-7.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfa447506c1a52271f1b0de3f42ea0fa14676052549095e378d5bff1c505ff7b", size = 240145, upload-time = "2025-06-13T13:02:15.745Z" }, - { url = "https://files.pythonhosted.org/packages/e0/33/d96e3350078a3c423c549cb5b2ba970de24c5257954d3e4066e2b2152d30/coverage-7.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9ca8e220006966b4a7b68e8984a6aee645a0384b0769e829ba60281fe61ec4f7", size = 239871, upload-time = "2025-06-13T13:02:17.344Z" }, - { url = "https://files.pythonhosted.org/packages/1d/6e/6fb946072455f71a820cac144d49d11747a0f1a21038060a68d2d0200499/coverage-7.9.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49f1d0788ba5b7ba65933f3a18864117c6506619f5ca80326b478f72acf3f385", size = 238122, upload-time = "2025-06-13T13:02:18.849Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5c/bc43f25c8586840ce25a796a8111acf6a2b5f0909ba89a10d41ccff3920d/coverage-7.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68cd53aec6f45b8e4724c0950ce86eacb775c6be01ce6e3669fe4f3a21e768ed", size = 239058, upload-time = "2025-06-13T13:02:21.423Z" }, - { url = "https://files.pythonhosted.org/packages/11/d8/ce2007418dd7fd00ff8c8b898bb150bb4bac2d6a86df05d7b88a07ff595f/coverage-7.9.1-cp39-cp39-win32.whl", hash = "sha256:95335095b6c7b1cc14c3f3f17d5452ce677e8490d101698562b2ffcacc304c8d", size = 214532, upload-time = "2025-06-13T13:02:22.857Z" }, - { url = "https://files.pythonhosted.org/packages/20/21/334e76fa246e92e6d69cab217f7c8a70ae0cc8f01438bd0544103f29528e/coverage-7.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:e1b5191d1648acc439b24721caab2fd0c86679d8549ed2c84d5a7ec1bedcc244", size = 215439, upload-time = "2025-06-13T13:02:24.268Z" }, - { url = "https://files.pythonhosted.org/packages/3e/e5/c723545c3fd3204ebde3b4cc4b927dce709d3b6dc577754bb57f63ca4a4a/coverage-7.9.1-pp39.pp310.pp311-none-any.whl", hash = "sha256:db0f04118d1db74db6c9e1cb1898532c7dcc220f1d2718f058601f7c3f499514", size = 204009, upload-time = "2025-06-13T13:02:25.787Z" }, - { url = "https://files.pythonhosted.org/packages/08/b8/7ddd1e8ba9701dea08ce22029917140e6f66a859427406579fd8d0ca7274/coverage-7.9.1-py3-none-any.whl", hash = "sha256:66b974b145aa189516b6bf2d8423e888b742517d37872f6ee4c5be0073bd9a3c", size = 204000, upload-time = "2025-06-13T13:02:27.173Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/5c2114fd776c207bd55068ae8dc1bef63ecd1b767b3389984a8e58f2b926/coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912", size = 212039, upload-time = "2025-07-03T10:52:38.955Z" }, + { url = "https://files.pythonhosted.org/packages/cf/ad/dc51f40492dc2d5fcd31bb44577bc0cc8920757d6bc5d3e4293146524ef9/coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f", size = 212428, upload-time = "2025-07-03T10:52:41.36Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a3/55cb3ff1b36f00df04439c3993d8529193cdf165a2467bf1402539070f16/coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f", size = 241534, upload-time = "2025-07-03T10:52:42.956Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c9/a8410b91b6be4f6e9c2e9f0dce93749b6b40b751d7065b4410bf89cb654b/coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf", size = 239408, upload-time = "2025-07-03T10:52:44.199Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c4/6f3e56d467c612b9070ae71d5d3b114c0b899b5788e1ca3c93068ccb7018/coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547", size = 240552, upload-time = "2025-07-03T10:52:45.477Z" }, + { url = "https://files.pythonhosted.org/packages/fd/20/04eda789d15af1ce79bce5cc5fd64057c3a0ac08fd0576377a3096c24663/coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45", size = 240464, upload-time = "2025-07-03T10:52:46.809Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5a/217b32c94cc1a0b90f253514815332d08ec0812194a1ce9cca97dda1cd20/coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2", size = 239134, upload-time = "2025-07-03T10:52:48.149Z" }, + { url = "https://files.pythonhosted.org/packages/34/73/1d019c48f413465eb5d3b6898b6279e87141c80049f7dbf73fd020138549/coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e", size = 239405, upload-time = "2025-07-03T10:52:49.687Z" }, + { url = "https://files.pythonhosted.org/packages/49/6c/a2beca7aa2595dad0c0d3f350382c381c92400efe5261e2631f734a0e3fe/coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e", size = 214519, upload-time = "2025-07-03T10:52:51.036Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c8/91e5e4a21f9a51e2c7cdd86e587ae01a4fcff06fc3fa8cde4d6f7cf68df4/coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c", size = 215400, upload-time = "2025-07-03T10:52:52.313Z" }, + { url = "https://files.pythonhosted.org/packages/39/40/916786453bcfafa4c788abee4ccd6f592b5b5eca0cd61a32a4e5a7ef6e02/coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba", size = 212152, upload-time = "2025-07-03T10:52:53.562Z" }, + { url = "https://files.pythonhosted.org/packages/9f/66/cc13bae303284b546a030762957322bbbff1ee6b6cb8dc70a40f8a78512f/coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa", size = 212540, upload-time = "2025-07-03T10:52:55.196Z" }, + { url = "https://files.pythonhosted.org/packages/0f/3c/d56a764b2e5a3d43257c36af4a62c379df44636817bb5f89265de4bf8bd7/coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a", size = 245097, upload-time = "2025-07-03T10:52:56.509Z" }, + { url = "https://files.pythonhosted.org/packages/b1/46/bd064ea8b3c94eb4ca5d90e34d15b806cba091ffb2b8e89a0d7066c45791/coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc", size = 242812, upload-time = "2025-07-03T10:52:57.842Z" }, + { url = "https://files.pythonhosted.org/packages/43/02/d91992c2b29bc7afb729463bc918ebe5f361be7f1daae93375a5759d1e28/coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2", size = 244617, upload-time = "2025-07-03T10:52:59.239Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4f/8fadff6bf56595a16d2d6e33415841b0163ac660873ed9a4e9046194f779/coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c", size = 244263, upload-time = "2025-07-03T10:53:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d2/e0be7446a2bba11739edb9f9ba4eff30b30d8257370e237418eb44a14d11/coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd", size = 242314, upload-time = "2025-07-03T10:53:01.932Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7d/dcbac9345000121b8b57a3094c2dfcf1ccc52d8a14a40c1d4bc89f936f80/coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74", size = 242904, upload-time = "2025-07-03T10:53:03.478Z" }, + { url = "https://files.pythonhosted.org/packages/41/58/11e8db0a0c0510cf31bbbdc8caf5d74a358b696302a45948d7c768dfd1cf/coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6", size = 214553, upload-time = "2025-07-03T10:53:05.174Z" }, + { url = "https://files.pythonhosted.org/packages/3a/7d/751794ec8907a15e257136e48dc1021b1f671220ecccfd6c4eaf30802714/coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7", size = 215441, upload-time = "2025-07-03T10:53:06.472Z" }, + { url = "https://files.pythonhosted.org/packages/62/5b/34abcedf7b946c1c9e15b44f326cb5b0da852885312b30e916f674913428/coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62", size = 213873, upload-time = "2025-07-03T10:53:07.699Z" }, + { url = "https://files.pythonhosted.org/packages/53/d7/7deefc6fd4f0f1d4c58051f4004e366afc9e7ab60217ac393f247a1de70a/coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0", size = 212344, upload-time = "2025-07-03T10:53:09.3Z" }, + { url = "https://files.pythonhosted.org/packages/95/0c/ee03c95d32be4d519e6a02e601267769ce2e9a91fc8faa1b540e3626c680/coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3", size = 212580, upload-time = "2025-07-03T10:53:11.52Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9f/826fa4b544b27620086211b87a52ca67592622e1f3af9e0a62c87aea153a/coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1", size = 246383, upload-time = "2025-07-03T10:53:13.134Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b3/4477aafe2a546427b58b9c540665feff874f4db651f4d3cb21b308b3a6d2/coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615", size = 243400, upload-time = "2025-07-03T10:53:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c2/efffa43778490c226d9d434827702f2dfbc8041d79101a795f11cbb2cf1e/coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b", size = 245591, upload-time = "2025-07-03T10:53:15.872Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e7/a59888e882c9a5f0192d8627a30ae57910d5d449c80229b55e7643c078c4/coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9", size = 245402, upload-time = "2025-07-03T10:53:17.124Z" }, + { url = "https://files.pythonhosted.org/packages/92/a5/72fcd653ae3d214927edc100ce67440ed8a0a1e3576b8d5e6d066ed239db/coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f", size = 243583, upload-time = "2025-07-03T10:53:18.781Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f5/84e70e4df28f4a131d580d7d510aa1ffd95037293da66fd20d446090a13b/coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d", size = 244815, upload-time = "2025-07-03T10:53:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/39/e7/d73d7cbdbd09fdcf4642655ae843ad403d9cbda55d725721965f3580a314/coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355", size = 214719, upload-time = "2025-07-03T10:53:21.521Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d6/7486dcc3474e2e6ad26a2af2db7e7c162ccd889c4c68fa14ea8ec189c9e9/coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0", size = 215509, upload-time = "2025-07-03T10:53:22.853Z" }, + { url = "https://files.pythonhosted.org/packages/b7/34/0439f1ae2593b0346164d907cdf96a529b40b7721a45fdcf8b03c95fcd90/coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b", size = 213910, upload-time = "2025-07-03T10:53:24.472Z" }, + { url = "https://files.pythonhosted.org/packages/94/9d/7a8edf7acbcaa5e5c489a646226bed9591ee1c5e6a84733c0140e9ce1ae1/coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038", size = 212367, upload-time = "2025-07-03T10:53:25.811Z" }, + { url = "https://files.pythonhosted.org/packages/e8/9e/5cd6f130150712301f7e40fb5865c1bc27b97689ec57297e568d972eec3c/coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d", size = 212632, upload-time = "2025-07-03T10:53:27.075Z" }, + { url = "https://files.pythonhosted.org/packages/a8/de/6287a2c2036f9fd991c61cefa8c64e57390e30c894ad3aa52fac4c1e14a8/coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3", size = 245793, upload-time = "2025-07-03T10:53:28.408Z" }, + { url = "https://files.pythonhosted.org/packages/06/cc/9b5a9961d8160e3cb0b558c71f8051fe08aa2dd4b502ee937225da564ed1/coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14", size = 243006, upload-time = "2025-07-03T10:53:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6", size = 244990, upload-time = "2025-07-03T10:53:31.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/83/801cdc10f137b2d02b005a761661649ffa60eb173dcdaeb77f571e4dc192/coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b", size = 245157, upload-time = "2025-07-03T10:53:32.717Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a4/41911ed7e9d3ceb0ffb019e7635468df7499f5cc3edca5f7dfc078e9c5ec/coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d", size = 243128, upload-time = "2025-07-03T10:53:34.009Z" }, + { url = "https://files.pythonhosted.org/packages/10/41/344543b71d31ac9cb00a664d5d0c9ef134a0fe87cb7d8430003b20fa0b7d/coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868", size = 244511, upload-time = "2025-07-03T10:53:35.434Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/3b68c77e4812105e2a060f6946ba9e6f898ddcdc0d2bfc8b4b152a9ae522/coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a", size = 214765, upload-time = "2025-07-03T10:53:36.787Z" }, + { url = "https://files.pythonhosted.org/packages/06/a2/7fac400f6a346bb1a4004eb2a76fbff0e242cd48926a2ce37a22a6a1d917/coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b", size = 215536, upload-time = "2025-07-03T10:53:38.188Z" }, + { url = "https://files.pythonhosted.org/packages/08/47/2c6c215452b4f90d87017e61ea0fd9e0486bb734cb515e3de56e2c32075f/coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694", size = 213943, upload-time = "2025-07-03T10:53:39.492Z" }, + { url = "https://files.pythonhosted.org/packages/a3/46/e211e942b22d6af5e0f323faa8a9bc7c447a1cf1923b64c47523f36ed488/coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5", size = 213088, upload-time = "2025-07-03T10:53:40.874Z" }, + { url = "https://files.pythonhosted.org/packages/d2/2f/762551f97e124442eccd907bf8b0de54348635b8866a73567eb4e6417acf/coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b", size = 213298, upload-time = "2025-07-03T10:53:42.218Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b7/76d2d132b7baf7360ed69be0bcab968f151fa31abe6d067f0384439d9edb/coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3", size = 256541, upload-time = "2025-07-03T10:53:43.823Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/392b219837d7ad47d8e5974ce5f8dc3deb9f99a53b3bd4d123602f960c81/coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8", size = 252761, upload-time = "2025-07-03T10:53:45.19Z" }, + { url = "https://files.pythonhosted.org/packages/d5/77/4256d3577fe1b0daa8d3836a1ebe68eaa07dd2cbaf20cf5ab1115d6949d4/coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46", size = 254917, upload-time = "2025-07-03T10:53:46.931Z" }, + { url = "https://files.pythonhosted.org/packages/53/99/fc1a008eef1805e1ddb123cf17af864743354479ea5129a8f838c433cc2c/coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584", size = 256147, upload-time = "2025-07-03T10:53:48.289Z" }, + { url = "https://files.pythonhosted.org/packages/92/c0/f63bf667e18b7f88c2bdb3160870e277c4874ced87e21426128d70aa741f/coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e", size = 254261, upload-time = "2025-07-03T10:53:49.99Z" }, + { url = "https://files.pythonhosted.org/packages/8c/32/37dd1c42ce3016ff8ec9e4b607650d2e34845c0585d3518b2a93b4830c1a/coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac", size = 255099, upload-time = "2025-07-03T10:53:51.354Z" }, + { url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" }, + { url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" }, + { url = "https://files.pythonhosted.org/packages/62/ab/b4b06662ccaa00ca7bbee967b7035a33a58b41efb92d8c89a6c523a2ccd5/coverage-7.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddc39510ac922a5c4c27849b739f875d3e1d9e590d1e7b64c98dadf037a16cce", size = 212037, upload-time = "2025-07-03T10:53:58.055Z" }, + { url = "https://files.pythonhosted.org/packages/bb/5e/04619995657acc898d15bfad42b510344b3a74d4d5bc34f2e279d46c781c/coverage-7.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a535c0c7364acd55229749c2b3e5eebf141865de3a8f697076a3291985f02d30", size = 212412, upload-time = "2025-07-03T10:53:59.451Z" }, + { url = "https://files.pythonhosted.org/packages/14/e7/1465710224dc6d31c534e7714cbd907210622a044adc81c810e72eea873f/coverage-7.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df0f9ef28e0f20c767ccdccfc5ae5f83a6f4a2fbdfbcbcc8487a8a78771168c8", size = 241164, upload-time = "2025-07-03T10:54:00.852Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f2/44c6fbd2794afeb9ab6c0a14d3c088ab1dae3dff3df2624609981237bbb4/coverage-7.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f3da12e0ccbcb348969221d29441ac714bbddc4d74e13923d3d5a7a0bebef7a", size = 239032, upload-time = "2025-07-03T10:54:02.25Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d2/7a79845429c0aa2e6788bc45c26a2e3052fa91082c9ea1dea56fb531952c/coverage-7.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a17eaf46f56ae0f870f14a3cbc2e4632fe3771eab7f687eda1ee59b73d09fe4", size = 240148, upload-time = "2025-07-03T10:54:03.618Z" }, + { url = "https://files.pythonhosted.org/packages/9c/7d/2731d1b4c9c672d82d30d218224dfc62939cf3800bc8aba0258fefb191f5/coverage-7.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:669135a9d25df55d1ed56a11bf555f37c922cf08d80799d4f65d77d7d6123fcf", size = 239875, upload-time = "2025-07-03T10:54:05.022Z" }, + { url = "https://files.pythonhosted.org/packages/1b/83/685958715429a9da09cf172c15750ca5c795dd7259466f2645403696557b/coverage-7.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9d3a700304d01a627df9db4322dc082a0ce1e8fc74ac238e2af39ced4c083193", size = 238127, upload-time = "2025-07-03T10:54:06.366Z" }, + { url = "https://files.pythonhosted.org/packages/34/ff/161a4313308b3783126790adfae1970adbe4886fda8788792e435249910a/coverage-7.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:71ae8b53855644a0b1579d4041304ddc9995c7b21c8a1f16753c4d8903b4dfed", size = 239064, upload-time = "2025-07-03T10:54:07.878Z" }, + { url = "https://files.pythonhosted.org/packages/17/14/fe33f41b2e80811021de059621f44c01ebe4d6b08bdb82d54a514488e933/coverage-7.9.2-cp39-cp39-win32.whl", hash = "sha256:dd7a57b33b5cf27acb491e890720af45db05589a80c1ffc798462a765be6d4d7", size = 214522, upload-time = "2025-07-03T10:54:09.331Z" }, + { url = "https://files.pythonhosted.org/packages/6e/30/63d850ec31b5c6f6a7b4e853016375b846258300320eda29376e2786ceeb/coverage-7.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f65bb452e579d5540c8b37ec105dd54d8b9307b07bcaa186818c104ffda22441", size = 215419, upload-time = "2025-07-03T10:54:10.681Z" }, + { url = "https://files.pythonhosted.org/packages/d7/85/f8bbefac27d286386961c25515431482a425967e23d3698b75a250872924/coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050", size = 204013, upload-time = "2025-07-03T10:54:12.084Z" }, + { url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, ] [package.optional-dependencies] @@ -672,7 +670,7 @@ name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10'" }, + { name = "zipp", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ @@ -769,8 +767,7 @@ name = "ipython" version = "9.4.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version >= '3.11'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, @@ -1177,7 +1174,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.67" +version = "0.3.68" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1188,9 +1185,9 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/40/875af0194024d0006874f061958fa417d3500bbfdc9a57e1bd1c2f4e6ed2/langchain_core-0.3.67.tar.gz", hash = "sha256:2c14aa44a0e78e014e96d7f2f8916ac109d0a0ba87ed67ee25bf7296bed7e7ba", size = 561952, upload-time = "2025-06-30T17:09:35.142Z" } +sdist = { url = "https://files.pythonhosted.org/packages/23/20/f5b18a17bfbe3416177e702ab2fd230b7d168abb17be31fb48f43f0bb772/langchain_core-0.3.68.tar.gz", hash = "sha256:312e1932ac9aa2eaf111b70fdc171776fa571d1a86c1f873dcac88a094b19c6f", size = 563041, upload-time = "2025-07-03T17:02:28.704Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/2b/a0d283089c6d08c12d47dca39a55029ff714e939ec04f4560420426ab613/langchain_core-0.3.67-py3-none-any.whl", hash = "sha256:b699f1f24b24fa2747c05e2daa280aa64478a51e01a4e82c7f8e20b6167dfa99", size = 440237, upload-time = "2025-06-30T17:09:33.323Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c89be0a272993bfcb762b2a356b9f55de507784c2755ad63caec25d183bf/langchain_core-0.3.68-py3-none-any.whl", hash = "sha256:5e5c1fbef419590537c91b8c2d86af896fbcbaf0d5ed7fdcdd77f7d8f3467ba0", size = 441405, upload-time = "2025-07-03T17:02:27.115Z" }, ] [[package]] @@ -1274,7 +1271,7 @@ dev = [ [[package]] name = "langgraph-api" -version = "0.2.75" +version = "0.2.78" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle", marker = "python_full_version >= '3.11'" }, @@ -1297,9 +1294,9 @@ dependencies = [ { name = "uvicorn", marker = "python_full_version >= '3.11'" }, { name = "watchfiles", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/96/ddd4f965b66122ccf46924f9135b719e15140ef4f379d6133092fb2173a9/langgraph_api-0.2.75.tar.gz", hash = "sha256:96afc3bafe34d13f4a2acb3ab256b930888cd74facf394e4f5d4c23a3843e971", size = 231565, upload-time = "2025-06-30T23:10:43.479Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/88/1aa91bede22d1393a53605ff13a57d6cb3ce2831eb250a8a8f814c548d2e/langgraph_api-0.2.78.tar.gz", hash = "sha256:37aad4fe764934f358dbb26d50b4acbf4180aac673499d1502b5b468cc7acba4", size = 233581, upload-time = "2025-07-02T19:11:01.154Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/18/17b77fa7facfebc6642f2d5dc8d2a10723c2c551adc3090e608e349b4e58/langgraph_api-0.2.75-py3-none-any.whl", hash = "sha256:87fd6352916ad54a851b8add4378ef2ac30a5b1fde6917b8383b9964e84676b9", size = 188024, upload-time = "2025-06-30T23:10:42.083Z" }, + { url = "https://files.pythonhosted.org/packages/7e/76/46626d99c36fce0c1ee1e1581f54b29acbed5e9f2e7cf9a198c825547d91/langgraph_api-0.2.78-py3-none-any.whl", hash = "sha256:57abe7fff9763d6f4eaf4419d103f75e77dca6d271db3816777c04bd67db4449", size = 190222, upload-time = "2025-07-02T19:10:59.793Z" }, ] [[package]] @@ -2673,151 +2670,178 @@ wheels = [ [[package]] name = "rpds-py" -version = "0.25.1" +version = "0.26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304, upload-time = "2025-05-21T12:46:12.502Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385, upload-time = "2025-07-01T15:57:13.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/09/e1158988e50905b7f8306487a576b52d32aa9a87f79f7ab24ee8db8b6c05/rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9", size = 373140, upload-time = "2025-05-21T12:42:38.834Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4b/a284321fb3c45c02fc74187171504702b2934bfe16abab89713eedfe672e/rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40", size = 358860, upload-time = "2025-05-21T12:42:41.394Z" }, - { url = "https://files.pythonhosted.org/packages/4e/46/8ac9811150c75edeae9fc6fa0e70376c19bc80f8e1f7716981433905912b/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:605ffe7769e24b1800b4d024d24034405d9404f0bc2f55b6db3362cd34145a6f", size = 386179, upload-time = "2025-05-21T12:42:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ec/87eb42d83e859bce91dcf763eb9f2ab117142a49c9c3d17285440edb5b69/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc6f3ddef93243538be76f8e47045b4aad7a66a212cd3a0f23e34469473d36b", size = 400282, upload-time = "2025-05-21T12:42:44.92Z" }, - { url = "https://files.pythonhosted.org/packages/68/c8/2a38e0707d7919c8c78e1d582ab15cf1255b380bcb086ca265b73ed6db23/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f70316f760174ca04492b5ab01be631a8ae30cadab1d1081035136ba12738cfa", size = 521824, upload-time = "2025-05-21T12:42:46.856Z" }, - { url = "https://files.pythonhosted.org/packages/5e/2c/6a92790243569784dde84d144bfd12bd45102f4a1c897d76375076d730ab/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1dafef8df605fdb46edcc0bf1573dea0d6d7b01ba87f85cd04dc855b2b4479e", size = 411644, upload-time = "2025-05-21T12:42:48.838Z" }, - { url = "https://files.pythonhosted.org/packages/eb/76/66b523ffc84cf47db56efe13ae7cf368dee2bacdec9d89b9baca5e2e6301/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701942049095741a8aeb298a31b203e735d1c61f4423511d2b1a41dcd8a16da", size = 386955, upload-time = "2025-05-21T12:42:50.835Z" }, - { url = "https://files.pythonhosted.org/packages/b6/b9/a362d7522feaa24dc2b79847c6175daa1c642817f4a19dcd5c91d3e2c316/rpds_py-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e87798852ae0b37c88babb7f7bbbb3e3fecc562a1c340195b44c7e24d403e380", size = 421039, upload-time = "2025-05-21T12:42:52.348Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c4/b5b6f70b4d719b6584716889fd3413102acf9729540ee76708d56a76fa97/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bcce0edc1488906c2d4c75c94c70a0417e83920dd4c88fec1078c94843a6ce9", size = 563290, upload-time = "2025-05-21T12:42:54.404Z" }, - { url = "https://files.pythonhosted.org/packages/87/a3/2e6e816615c12a8f8662c9d8583a12eb54c52557521ef218cbe3095a8afa/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e2f6a2347d3440ae789505693a02836383426249d5293541cd712e07e7aecf54", size = 592089, upload-time = "2025-05-21T12:42:55.976Z" }, - { url = "https://files.pythonhosted.org/packages/c0/08/9b8e1050e36ce266135994e2c7ec06e1841f1c64da739daeb8afe9cb77a4/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4fd52d3455a0aa997734f3835cbc4c9f32571345143960e7d7ebfe7b5fbfa3b2", size = 558400, upload-time = "2025-05-21T12:42:58.032Z" }, - { url = "https://files.pythonhosted.org/packages/f2/df/b40b8215560b8584baccd839ff5c1056f3c57120d79ac41bd26df196da7e/rpds_py-0.25.1-cp310-cp310-win32.whl", hash = "sha256:3f0b1798cae2bbbc9b9db44ee068c556d4737911ad53a4e5093d09d04b3bbc24", size = 219741, upload-time = "2025-05-21T12:42:59.479Z" }, - { url = "https://files.pythonhosted.org/packages/10/99/e4c58be18cf5d8b40b8acb4122bc895486230b08f978831b16a3916bd24d/rpds_py-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ebd879ab996537fc510a2be58c59915b5dd63bccb06d1ef514fee787e05984a", size = 231553, upload-time = "2025-05-21T12:43:01.425Z" }, - { url = "https://files.pythonhosted.org/packages/95/e1/df13fe3ddbbea43567e07437f097863b20c99318ae1f58a0fe389f763738/rpds_py-0.25.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5f048bbf18b1f9120685c6d6bb70cc1a52c8cc11bdd04e643d28d3be0baf666d", size = 373341, upload-time = "2025-05-21T12:43:02.978Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/deef4d30fcbcbfef3b6d82d17c64490d5c94585a2310544ce8e2d3024f83/rpds_py-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fbb0dbba559959fcb5d0735a0f87cdbca9e95dac87982e9b95c0f8f7ad10255", size = 359111, upload-time = "2025-05-21T12:43:05.128Z" }, - { url = "https://files.pythonhosted.org/packages/bb/7e/39f1f4431b03e96ebaf159e29a0f82a77259d8f38b2dd474721eb3a8ac9b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4ca54b9cf9d80b4016a67a0193ebe0bcf29f6b0a96f09db942087e294d3d4c2", size = 386112, upload-time = "2025-05-21T12:43:07.13Z" }, - { url = "https://files.pythonhosted.org/packages/db/e7/847068a48d63aec2ae695a1646089620b3b03f8ccf9f02c122ebaf778f3c/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee3e26eb83d39b886d2cb6e06ea701bba82ef30a0de044d34626ede51ec98b0", size = 400362, upload-time = "2025-05-21T12:43:08.693Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3d/9441d5db4343d0cee759a7ab4d67420a476cebb032081763de934719727b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89706d0683c73a26f76a5315d893c051324d771196ae8b13e6ffa1ffaf5e574f", size = 522214, upload-time = "2025-05-21T12:43:10.694Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ec/2cc5b30d95f9f1a432c79c7a2f65d85e52812a8f6cbf8768724571710786/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2013ee878c76269c7b557a9a9c042335d732e89d482606990b70a839635feb7", size = 411491, upload-time = "2025-05-21T12:43:12.739Z" }, - { url = "https://files.pythonhosted.org/packages/dc/6c/44695c1f035077a017dd472b6a3253553780837af2fac9b6ac25f6a5cb4d/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45e484db65e5380804afbec784522de84fa95e6bb92ef1bd3325d33d13efaebd", size = 386978, upload-time = "2025-05-21T12:43:14.25Z" }, - { url = "https://files.pythonhosted.org/packages/b1/74/b4357090bb1096db5392157b4e7ed8bb2417dc7799200fcbaee633a032c9/rpds_py-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48d64155d02127c249695abb87d39f0faf410733428d499867606be138161d65", size = 420662, upload-time = "2025-05-21T12:43:15.8Z" }, - { url = "https://files.pythonhosted.org/packages/26/dd/8cadbebf47b96e59dfe8b35868e5c38a42272699324e95ed522da09d3a40/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:048893e902132fd6548a2e661fb38bf4896a89eea95ac5816cf443524a85556f", size = 563385, upload-time = "2025-05-21T12:43:17.78Z" }, - { url = "https://files.pythonhosted.org/packages/c3/ea/92960bb7f0e7a57a5ab233662f12152085c7dc0d5468534c65991a3d48c9/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0317177b1e8691ab5879f4f33f4b6dc55ad3b344399e23df2e499de7b10a548d", size = 592047, upload-time = "2025-05-21T12:43:19.457Z" }, - { url = "https://files.pythonhosted.org/packages/61/ad/71aabc93df0d05dabcb4b0c749277881f8e74548582d96aa1bf24379493a/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffcf57826d77a4151962bf1701374e0fc87f536e56ec46f1abdd6a903354042", size = 557863, upload-time = "2025-05-21T12:43:21.69Z" }, - { url = "https://files.pythonhosted.org/packages/93/0f/89df0067c41f122b90b76f3660028a466eb287cbe38efec3ea70e637ca78/rpds_py-0.25.1-cp311-cp311-win32.whl", hash = "sha256:cda776f1967cb304816173b30994faaf2fd5bcb37e73118a47964a02c348e1bc", size = 219627, upload-time = "2025-05-21T12:43:23.311Z" }, - { url = "https://files.pythonhosted.org/packages/7c/8d/93b1a4c1baa903d0229374d9e7aa3466d751f1d65e268c52e6039c6e338e/rpds_py-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:dc3c1ff0abc91444cd20ec643d0f805df9a3661fcacf9c95000329f3ddf268a4", size = 231603, upload-time = "2025-05-21T12:43:25.145Z" }, - { url = "https://files.pythonhosted.org/packages/cb/11/392605e5247bead2f23e6888e77229fbd714ac241ebbebb39a1e822c8815/rpds_py-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:5a3ddb74b0985c4387719fc536faced33cadf2172769540c62e2a94b7b9be1c4", size = 223967, upload-time = "2025-05-21T12:43:26.566Z" }, - { url = "https://files.pythonhosted.org/packages/7f/81/28ab0408391b1dc57393653b6a0cf2014cc282cc2909e4615e63e58262be/rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", size = 364647, upload-time = "2025-05-21T12:43:28.559Z" }, - { url = "https://files.pythonhosted.org/packages/2c/9a/7797f04cad0d5e56310e1238434f71fc6939d0bc517192a18bb99a72a95f/rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", size = 350454, upload-time = "2025-05-21T12:43:30.615Z" }, - { url = "https://files.pythonhosted.org/packages/69/3c/93d2ef941b04898011e5d6eaa56a1acf46a3b4c9f4b3ad1bbcbafa0bee1f/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", size = 389665, upload-time = "2025-05-21T12:43:32.629Z" }, - { url = "https://files.pythonhosted.org/packages/c1/57/ad0e31e928751dde8903a11102559628d24173428a0f85e25e187defb2c1/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", size = 403873, upload-time = "2025-05-21T12:43:34.576Z" }, - { url = "https://files.pythonhosted.org/packages/16/ad/c0c652fa9bba778b4f54980a02962748479dc09632e1fd34e5282cf2556c/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", size = 525866, upload-time = "2025-05-21T12:43:36.123Z" }, - { url = "https://files.pythonhosted.org/packages/2a/39/3e1839bc527e6fcf48d5fec4770070f872cdee6c6fbc9b259932f4e88a38/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", size = 416886, upload-time = "2025-05-21T12:43:38.034Z" }, - { url = "https://files.pythonhosted.org/packages/7a/95/dd6b91cd4560da41df9d7030a038298a67d24f8ca38e150562644c829c48/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", size = 390666, upload-time = "2025-05-21T12:43:40.065Z" }, - { url = "https://files.pythonhosted.org/packages/64/48/1be88a820e7494ce0a15c2d390ccb7c52212370badabf128e6a7bb4cb802/rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", size = 425109, upload-time = "2025-05-21T12:43:42.263Z" }, - { url = "https://files.pythonhosted.org/packages/cf/07/3e2a17927ef6d7720b9949ec1b37d1e963b829ad0387f7af18d923d5cfa5/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", size = 567244, upload-time = "2025-05-21T12:43:43.846Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e5/76cf010998deccc4f95305d827847e2eae9c568099c06b405cf96384762b/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", size = 596023, upload-time = "2025-05-21T12:43:45.932Z" }, - { url = "https://files.pythonhosted.org/packages/52/9a/df55efd84403736ba37a5a6377b70aad0fd1cb469a9109ee8a1e21299a1c/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", size = 561634, upload-time = "2025-05-21T12:43:48.263Z" }, - { url = "https://files.pythonhosted.org/packages/ab/aa/dc3620dd8db84454aaf9374bd318f1aa02578bba5e567f5bf6b79492aca4/rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", size = 222713, upload-time = "2025-05-21T12:43:49.897Z" }, - { url = "https://files.pythonhosted.org/packages/a3/7f/7cef485269a50ed5b4e9bae145f512d2a111ca638ae70cc101f661b4defd/rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", size = 235280, upload-time = "2025-05-21T12:43:51.893Z" }, - { url = "https://files.pythonhosted.org/packages/99/f2/c2d64f6564f32af913bf5f3f7ae41c7c263c5ae4c4e8f1a17af8af66cd46/rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", size = 225399, upload-time = "2025-05-21T12:43:53.351Z" }, - { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498, upload-time = "2025-05-21T12:43:54.841Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083, upload-time = "2025-05-21T12:43:56.428Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023, upload-time = "2025-05-21T12:43:57.995Z" }, - { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283, upload-time = "2025-05-21T12:43:59.546Z" }, - { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634, upload-time = "2025-05-21T12:44:01.087Z" }, - { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233, upload-time = "2025-05-21T12:44:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375, upload-time = "2025-05-21T12:44:04.162Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537, upload-time = "2025-05-21T12:44:06.175Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425, upload-time = "2025-05-21T12:44:08.242Z" }, - { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197, upload-time = "2025-05-21T12:44:10.449Z" }, - { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244, upload-time = "2025-05-21T12:44:12.387Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254, upload-time = "2025-05-21T12:44:14.261Z" }, - { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741, upload-time = "2025-05-21T12:44:16.236Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830, upload-time = "2025-05-21T12:44:17.749Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668, upload-time = "2025-05-21T12:44:19.322Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649, upload-time = "2025-05-21T12:44:20.962Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776, upload-time = "2025-05-21T12:44:22.516Z" }, - { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131, upload-time = "2025-05-21T12:44:24.147Z" }, - { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942, upload-time = "2025-05-21T12:44:25.915Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330, upload-time = "2025-05-21T12:44:27.638Z" }, - { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339, upload-time = "2025-05-21T12:44:29.292Z" }, - { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077, upload-time = "2025-05-21T12:44:30.877Z" }, - { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441, upload-time = "2025-05-21T12:44:32.541Z" }, - { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750, upload-time = "2025-05-21T12:44:34.557Z" }, - { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891, upload-time = "2025-05-21T12:44:37.358Z" }, - { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718, upload-time = "2025-05-21T12:44:38.969Z" }, - { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218, upload-time = "2025-05-21T12:44:40.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/74/716d42058ef501e2c08f27aa3ff455f6fc1bbbd19a6ab8dea07e6322d217/rpds_py-0.25.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ce4c8e485a3c59593f1a6f683cf0ea5ab1c1dc94d11eea5619e4fb5228b40fbd", size = 373475, upload-time = "2025-05-21T12:44:42.136Z" }, - { url = "https://files.pythonhosted.org/packages/e1/21/3faa9c523e2496a2505d7440b6f24c9166f37cb7ac027cac6cfbda9b4b5f/rpds_py-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8222acdb51a22929c3b2ddb236b69c59c72af4019d2cba961e2f9add9b6e634", size = 359349, upload-time = "2025-05-21T12:44:43.813Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/c747fe568d21b1d679079b52b926ebc4d1497457510a1773dc5fd4b7b4e2/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4593c4eae9b27d22df41cde518b4b9e4464d139e4322e2127daa9b5b981b76be", size = 386526, upload-time = "2025-05-21T12:44:45.452Z" }, - { url = "https://files.pythonhosted.org/packages/0b/cc/4a41703de4fb291f13660fa3d882cbd39db5d60497c6e7fa7f5142e5e69f/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd035756830c712b64725a76327ce80e82ed12ebab361d3a1cdc0f51ea21acb0", size = 400526, upload-time = "2025-05-21T12:44:47.011Z" }, - { url = "https://files.pythonhosted.org/packages/f1/78/60c980bedcad8418b614f0b4d6d420ecf11225b579cec0cb4e84d168b4da/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:114a07e85f32b125404f28f2ed0ba431685151c037a26032b213c882f26eb908", size = 525726, upload-time = "2025-05-21T12:44:48.838Z" }, - { url = "https://files.pythonhosted.org/packages/3f/37/f2f36b7f1314b3c3200d663decf2f8e29480492a39ab22447112aead4693/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dec21e02e6cc932538b5203d3a8bd6aa1480c98c4914cb88eea064ecdbc6396a", size = 412045, upload-time = "2025-05-21T12:44:50.433Z" }, - { url = "https://files.pythonhosted.org/packages/df/96/e03783e87a775b1242477ccbc35895f8e9b2bbdb60e199034a6da03c2687/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09eab132f41bf792c7a0ea1578e55df3f3e7f61888e340779b06050a9a3f16e9", size = 386953, upload-time = "2025-05-21T12:44:52.092Z" }, - { url = "https://files.pythonhosted.org/packages/7c/7d/1418f4b69bfb4b40481a3d84782113ad7d4cca0b38ae70b982dd5b20102a/rpds_py-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c98f126c4fc697b84c423e387337d5b07e4a61e9feac494362a59fd7a2d9ed80", size = 421144, upload-time = "2025-05-21T12:44:53.734Z" }, - { url = "https://files.pythonhosted.org/packages/b3/0e/61469912c6493ee3808012e60f4930344b974fcb6b35c4348e70b6be7bc7/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0e6a327af8ebf6baba1c10fadd04964c1965d375d318f4435d5f3f9651550f4a", size = 563730, upload-time = "2025-05-21T12:44:55.846Z" }, - { url = "https://files.pythonhosted.org/packages/f6/86/6d0a5cc56481ac61977b7c839677ed5c63d38cf0fcb3e2280843a8a6f476/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc120d1132cff853ff617754196d0ac0ae63befe7c8498bd67731ba368abe451", size = 592321, upload-time = "2025-05-21T12:44:57.514Z" }, - { url = "https://files.pythonhosted.org/packages/5d/87/d1e2453fe336f71e6aa296452a8c85c2118b587b1d25ce98014f75838a60/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:140f61d9bed7839446bdd44852e30195c8e520f81329b4201ceead4d64eb3a9f", size = 558162, upload-time = "2025-05-21T12:44:59.564Z" }, - { url = "https://files.pythonhosted.org/packages/ad/92/349f04b1644c5cef3e2e6c53b7168a28531945f9e6fca7425f6d20ddbc3c/rpds_py-0.25.1-cp39-cp39-win32.whl", hash = "sha256:9c006f3aadeda131b438c3092124bd196b66312f0caa5823ef09585a669cf449", size = 219920, upload-time = "2025-05-21T12:45:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/f2/84/3969bef883a3f37ff2213795257cb7b7e93a115829670befb8de0e003031/rpds_py-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:a61d0b2c7c9a0ae45732a77844917b427ff16ad5464b4d4f5e4adb955f582890", size = 231452, upload-time = "2025-05-21T12:45:02.85Z" }, - { url = "https://files.pythonhosted.org/packages/78/ff/566ce53529b12b4f10c0a348d316bd766970b7060b4fd50f888be3b3b281/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b24bf3cd93d5b6ecfbedec73b15f143596c88ee249fa98cefa9a9dc9d92c6f28", size = 373931, upload-time = "2025-05-21T12:45:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/83/5d/deba18503f7c7878e26aa696e97f051175788e19d5336b3b0e76d3ef9256/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0eb90e94f43e5085623932b68840b6f379f26db7b5c2e6bcef3179bd83c9330f", size = 359074, upload-time = "2025-05-21T12:45:06.714Z" }, - { url = "https://files.pythonhosted.org/packages/0d/74/313415c5627644eb114df49c56a27edba4d40cfd7c92bd90212b3604ca84/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50e4864498a9ab639d6d8854b25e80642bd362ff104312d9770b05d66e5fb13", size = 387255, upload-time = "2025-05-21T12:45:08.669Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c8/c723298ed6338963d94e05c0f12793acc9b91d04ed7c4ba7508e534b7385/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c9409b47ba0650544b0bb3c188243b83654dfe55dcc173a86832314e1a6a35d", size = 400714, upload-time = "2025-05-21T12:45:10.39Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/51f1f6aa653c2e110ed482ef2ae94140d56c910378752a1b483af11019ee/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:796ad874c89127c91970652a4ee8b00d56368b7e00d3477f4415fe78164c8000", size = 523105, upload-time = "2025-05-21T12:45:12.273Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a4/7873d15c088ad3bff36910b29ceb0f178e4b3232c2adbe9198de68a41e63/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85608eb70a659bf4c1142b2781083d4b7c0c4e2c90eff11856a9754e965b2540", size = 411499, upload-time = "2025-05-21T12:45:13.95Z" }, - { url = "https://files.pythonhosted.org/packages/90/f3/0ce1437befe1410766d11d08239333ac1b2d940f8a64234ce48a7714669c/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4feb9211d15d9160bc85fa72fed46432cdc143eb9cf6d5ca377335a921ac37b", size = 387918, upload-time = "2025-05-21T12:45:15.649Z" }, - { url = "https://files.pythonhosted.org/packages/94/d4/5551247988b2a3566afb8a9dba3f1d4a3eea47793fd83000276c1a6c726e/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccfa689b9246c48947d31dd9d8b16d89a0ecc8e0e26ea5253068efb6c542b76e", size = 421705, upload-time = "2025-05-21T12:45:17.788Z" }, - { url = "https://files.pythonhosted.org/packages/b0/25/5960f28f847bf736cc7ee3c545a7e1d2f3b5edaf82c96fb616c2f5ed52d0/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3c5b317ecbd8226887994852e85de562f7177add602514d4ac40f87de3ae45a8", size = 564489, upload-time = "2025-05-21T12:45:19.466Z" }, - { url = "https://files.pythonhosted.org/packages/02/66/1c99884a0d44e8c2904d3c4ec302f995292d5dde892c3bf7685ac1930146/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:454601988aab2c6e8fd49e7634c65476b2b919647626208e376afcd22019eeb8", size = 592557, upload-time = "2025-05-21T12:45:21.362Z" }, - { url = "https://files.pythonhosted.org/packages/55/ae/4aeac84ebeffeac14abb05b3bb1d2f728d00adb55d3fb7b51c9fa772e760/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1c0c434a53714358532d13539272db75a5ed9df75a4a090a753ac7173ec14e11", size = 558691, upload-time = "2025-05-21T12:45:23.084Z" }, - { url = "https://files.pythonhosted.org/packages/41/b3/728a08ff6f5e06fe3bb9af2e770e9d5fd20141af45cff8dfc62da4b2d0b3/rpds_py-0.25.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f73ce1512e04fbe2bc97836e89830d6b4314c171587a99688082d090f934d20a", size = 231651, upload-time = "2025-05-21T12:45:24.72Z" }, - { url = "https://files.pythonhosted.org/packages/49/74/48f3df0715a585cbf5d34919c9c757a4c92c1a9eba059f2d334e72471f70/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee86d81551ec68a5c25373c5643d343150cc54672b5e9a0cafc93c1870a53954", size = 374208, upload-time = "2025-05-21T12:45:26.306Z" }, - { url = "https://files.pythonhosted.org/packages/55/b0/9b01bb11ce01ec03d05e627249cc2c06039d6aa24ea5a22a39c312167c10/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89c24300cd4a8e4a51e55c31a8ff3918e6651b241ee8876a42cc2b2a078533ba", size = 359262, upload-time = "2025-05-21T12:45:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/a9/eb/5395621618f723ebd5116c53282052943a726dba111b49cd2071f785b665/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:771c16060ff4e79584dc48902a91ba79fd93eade3aa3a12d6d2a4aadaf7d542b", size = 387366, upload-time = "2025-05-21T12:45:30.42Z" }, - { url = "https://files.pythonhosted.org/packages/68/73/3d51442bdb246db619d75039a50ea1cf8b5b4ee250c3e5cd5c3af5981cd4/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785ffacd0ee61c3e60bdfde93baa6d7c10d86f15655bd706c89da08068dc5038", size = 400759, upload-time = "2025-05-21T12:45:32.516Z" }, - { url = "https://files.pythonhosted.org/packages/b7/4c/3a32d5955d7e6cb117314597bc0f2224efc798428318b13073efe306512a/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a40046a529cc15cef88ac5ab589f83f739e2d332cb4d7399072242400ed68c9", size = 523128, upload-time = "2025-05-21T12:45:34.396Z" }, - { url = "https://files.pythonhosted.org/packages/be/95/1ffccd3b0bb901ae60b1dd4b1be2ab98bb4eb834cd9b15199888f5702f7b/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85fc223d9c76cabe5d0bff82214459189720dc135db45f9f66aa7cffbf9ff6c1", size = 411597, upload-time = "2025-05-21T12:45:36.164Z" }, - { url = "https://files.pythonhosted.org/packages/ef/6d/6e6cd310180689db8b0d2de7f7d1eabf3fb013f239e156ae0d5a1a85c27f/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0be9965f93c222fb9b4cc254235b3b2b215796c03ef5ee64f995b1b69af0762", size = 388053, upload-time = "2025-05-21T12:45:38.45Z" }, - { url = "https://files.pythonhosted.org/packages/4a/87/ec4186b1fe6365ced6fa470960e68fc7804bafbe7c0cf5a36237aa240efa/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8378fa4a940f3fb509c081e06cb7f7f2adae8cf46ef258b0e0ed7519facd573e", size = 421821, upload-time = "2025-05-21T12:45:40.732Z" }, - { url = "https://files.pythonhosted.org/packages/7a/60/84f821f6bf4e0e710acc5039d91f8f594fae0d93fc368704920d8971680d/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:33358883a4490287e67a2c391dfaea4d9359860281db3292b6886bf0be3d8692", size = 564534, upload-time = "2025-05-21T12:45:42.672Z" }, - { url = "https://files.pythonhosted.org/packages/41/3a/bc654eb15d3b38f9330fe0f545016ba154d89cdabc6177b0295910cd0ebe/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1d1fadd539298e70cac2f2cb36f5b8a65f742b9b9f1014dd4ea1f7785e2470bf", size = 592674, upload-time = "2025-05-21T12:45:44.533Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ba/31239736f29e4dfc7a58a45955c5db852864c306131fd6320aea214d5437/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a46c2fb2545e21181445515960006e85d22025bd2fe6db23e76daec6eb689fe", size = 558781, upload-time = "2025-05-21T12:45:46.281Z" }, - { url = "https://files.pythonhosted.org/packages/78/b2/198266f070c6760e0e8cd00f9f2b9c86133ceebbe7c6d114bdcfea200180/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:50f2c501a89c9a5f4e454b126193c5495b9fb441a75b298c60591d8a2eb92e1b", size = 373973, upload-time = "2025-05-21T12:45:48.081Z" }, - { url = "https://files.pythonhosted.org/packages/13/79/1265eae618f88aa5d5e7122bd32dd41700bafe5a8bcea404e998848cd844/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d779b325cc8238227c47fbc53964c8cc9a941d5dbae87aa007a1f08f2f77b23", size = 359326, upload-time = "2025-05-21T12:45:49.825Z" }, - { url = "https://files.pythonhosted.org/packages/30/ab/6913b96f3ac072e87e76e45fe938263b0ab0d78b6b2cef3f2e56067befc0/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:036ded36bedb727beeabc16dc1dad7cb154b3fa444e936a03b67a86dc6a5066e", size = 387544, upload-time = "2025-05-21T12:45:51.764Z" }, - { url = "https://files.pythonhosted.org/packages/b0/23/129ed12d25229acc6deb8cbe90baadd8762e563c267c9594eb2fcc15be0c/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:245550f5a1ac98504147cba96ffec8fabc22b610742e9150138e5d60774686d7", size = 400240, upload-time = "2025-05-21T12:45:54.061Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e0/6811a38a5efa46b7ee6ed2103c95cb9abb16991544c3b69007aa679b6944/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff7c23ba0a88cb7b104281a99476cccadf29de2a0ef5ce864959a52675b1ca83", size = 525599, upload-time = "2025-05-21T12:45:56.457Z" }, - { url = "https://files.pythonhosted.org/packages/6c/10/2dc88bcaa0d86bdb59e017a330b1972ffeeb7f5061bb5a180c9a2bb73bbf/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e37caa8cdb3b7cf24786451a0bdb853f6347b8b92005eeb64225ae1db54d1c2b", size = 411154, upload-time = "2025-05-21T12:45:58.525Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d1/a72d522eb7d934fb33e9c501e6ecae00e2035af924d4ff37d964e9a3959b/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2f48ab00181600ee266a095fe815134eb456163f7d6699f525dee471f312cf", size = 388297, upload-time = "2025-05-21T12:46:00.264Z" }, - { url = "https://files.pythonhosted.org/packages/55/90/0dd7169ec74f042405b6b73512200d637a3088c156f64e1c07c18aa2fe59/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e5fc7484fa7dce57e25063b0ec9638ff02a908304f861d81ea49273e43838c1", size = 421894, upload-time = "2025-05-21T12:46:02.065Z" }, - { url = "https://files.pythonhosted.org/packages/37/e9/45170894add451783ed839c5c4a495e050aa8baa06d720364d9dff394dac/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d3c10228d6cf6fe2b63d2e7985e94f6916fa46940df46b70449e9ff9297bd3d1", size = 564409, upload-time = "2025-05-21T12:46:03.891Z" }, - { url = "https://files.pythonhosted.org/packages/59/d0/31cece9090e76fbdb50c758c165d40da604b03b37c3ba53f010bbfeb130a/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:5d9e40f32745db28c1ef7aad23f6fc458dc1e29945bd6781060f0d15628b8ddf", size = 592681, upload-time = "2025-05-21T12:46:06.009Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4c/22ef535efb2beec614ba7be83e62b439eb83b0b0d7b1775e22d35af3f9b5/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:35a8d1a24b5936b35c5003313bc177403d8bdef0f8b24f28b1c4a255f94ea992", size = 558744, upload-time = "2025-05-21T12:46:07.78Z" }, - { url = "https://files.pythonhosted.org/packages/79/ff/f2150efc8daf0581d4dfaf0a2a30b08088b6df900230ee5ae4f7c8cd5163/rpds_py-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6099263f526efff9cf3883dfef505518730f7a7a93049b1d90d42e50a22b4793", size = 231305, upload-time = "2025-05-21T12:46:10.52Z" }, + { url = "https://files.pythonhosted.org/packages/b9/31/1459645f036c3dfeacef89e8e5825e430c77dde8489f3b99eaafcd4a60f5/rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37", size = 372466, upload-time = "2025-07-01T15:53:40.55Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ff/3d0727f35836cc8773d3eeb9a46c40cc405854e36a8d2e951f3a8391c976/rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0", size = 357825, upload-time = "2025-07-01T15:53:42.247Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ce/badc5e06120a54099ae287fa96d82cbb650a5f85cf247ffe19c7b157fd1f/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec671691e72dff75817386aa02d81e708b5a7ec0dec6669ec05213ff6b77e1bd", size = 381530, upload-time = "2025-07-01T15:53:43.585Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a5/fa5d96a66c95d06c62d7a30707b6a4cfec696ab8ae280ee7be14e961e118/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a1cb5d6ce81379401bbb7f6dbe3d56de537fb8235979843f0d53bc2e9815a79", size = 396933, upload-time = "2025-07-01T15:53:45.78Z" }, + { url = "https://files.pythonhosted.org/packages/00/a7/7049d66750f18605c591a9db47d4a059e112a0c9ff8de8daf8fa0f446bba/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f789e32fa1fb6a7bf890e0124e7b42d1e60d28ebff57fe806719abb75f0e9a3", size = 513973, upload-time = "2025-07-01T15:53:47.085Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f1/528d02c7d6b29d29fac8fd784b354d3571cc2153f33f842599ef0cf20dd2/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c55b0a669976cf258afd718de3d9ad1b7d1fe0a91cd1ab36f38b03d4d4aeaaf", size = 402293, upload-time = "2025-07-01T15:53:48.117Z" }, + { url = "https://files.pythonhosted.org/packages/15/93/fde36cd6e4685df2cd08508f6c45a841e82f5bb98c8d5ecf05649522acb5/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70d9ec912802ecfd6cd390dadb34a9578b04f9bcb8e863d0a7598ba5e9e7ccc", size = 383787, upload-time = "2025-07-01T15:53:50.874Z" }, + { url = "https://files.pythonhosted.org/packages/69/f2/5007553aaba1dcae5d663143683c3dfd03d9395289f495f0aebc93e90f24/rpds_py-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3021933c2cb7def39d927b9862292e0f4c75a13d7de70eb0ab06efed4c508c19", size = 416312, upload-time = "2025-07-01T15:53:52.046Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/ce52c75c1e624a79e48a69e611f1c08844564e44c85db2b6f711d76d10ce/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a7898b6ca3b7d6659e55cdac825a2e58c638cbf335cde41f4619e290dd0ad11", size = 558403, upload-time = "2025-07-01T15:53:53.192Z" }, + { url = "https://files.pythonhosted.org/packages/79/d5/e119db99341cc75b538bf4cb80504129fa22ce216672fb2c28e4a101f4d9/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:12bff2ad9447188377f1b2794772f91fe68bb4bbfa5a39d7941fbebdbf8c500f", size = 588323, upload-time = "2025-07-01T15:53:54.336Z" }, + { url = "https://files.pythonhosted.org/packages/93/94/d28272a0b02f5fe24c78c20e13bbcb95f03dc1451b68e7830ca040c60bd6/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:191aa858f7d4902e975d4cf2f2d9243816c91e9605070aeb09c0a800d187e323", size = 554541, upload-time = "2025-07-01T15:53:55.469Z" }, + { url = "https://files.pythonhosted.org/packages/93/e0/8c41166602f1b791da892d976057eba30685486d2e2c061ce234679c922b/rpds_py-0.26.0-cp310-cp310-win32.whl", hash = "sha256:b37a04d9f52cb76b6b78f35109b513f6519efb481d8ca4c321f6a3b9580b3f45", size = 220442, upload-time = "2025-07-01T15:53:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/509736bb752a7ab50fb0270c2a4134d671a7b3038030837e5536c3de0e0b/rpds_py-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:38721d4c9edd3eb6670437d8d5e2070063f305bfa2d5aa4278c51cedcd508a84", size = 231314, upload-time = "2025-07-01T15:53:57.842Z" }, + { url = "https://files.pythonhosted.org/packages/09/4c/4ee8f7e512030ff79fda1df3243c88d70fc874634e2dbe5df13ba4210078/rpds_py-0.26.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e8cb77286025bdb21be2941d64ac6ca016130bfdcd228739e8ab137eb4406ed", size = 372610, upload-time = "2025-07-01T15:53:58.844Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9d/3dc16be00f14fc1f03c71b1d67c8df98263ab2710a2fbd65a6193214a527/rpds_py-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e09330b21d98adc8ccb2dbb9fc6cb434e8908d4c119aeaa772cb1caab5440a0", size = 358032, upload-time = "2025-07-01T15:53:59.985Z" }, + { url = "https://files.pythonhosted.org/packages/e7/5a/7f1bf8f045da2866324a08ae80af63e64e7bfaf83bd31f865a7b91a58601/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9c1b92b774b2e68d11193dc39620d62fd8ab33f0a3c77ecdabe19c179cdbc1", size = 381525, upload-time = "2025-07-01T15:54:01.162Z" }, + { url = "https://files.pythonhosted.org/packages/45/8a/04479398c755a066ace10e3d158866beb600867cacae194c50ffa783abd0/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:824e6d3503ab990d7090768e4dfd9e840837bae057f212ff9f4f05ec6d1975e7", size = 397089, upload-time = "2025-07-01T15:54:02.319Z" }, + { url = "https://files.pythonhosted.org/packages/72/88/9203f47268db488a1b6d469d69c12201ede776bb728b9d9f29dbfd7df406/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ad7fd2258228bf288f2331f0a6148ad0186b2e3643055ed0db30990e59817a6", size = 514255, upload-time = "2025-07-01T15:54:03.38Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b4/01ce5d1e853ddf81fbbd4311ab1eff0b3cf162d559288d10fd127e2588b5/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dc23bbb3e06ec1ea72d515fb572c1fea59695aefbffb106501138762e1e915e", size = 402283, upload-time = "2025-07-01T15:54:04.923Z" }, + { url = "https://files.pythonhosted.org/packages/34/a2/004c99936997bfc644d590a9defd9e9c93f8286568f9c16cdaf3e14429a7/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80bf832ac7b1920ee29a426cdca335f96a2b5caa839811803e999b41ba9030d", size = 383881, upload-time = "2025-07-01T15:54:06.482Z" }, + { url = "https://files.pythonhosted.org/packages/05/1b/ef5fba4a8f81ce04c427bfd96223f92f05e6cd72291ce9d7523db3b03a6c/rpds_py-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0919f38f5542c0a87e7b4afcafab6fd2c15386632d249e9a087498571250abe3", size = 415822, upload-time = "2025-07-01T15:54:07.605Z" }, + { url = "https://files.pythonhosted.org/packages/16/80/5c54195aec456b292f7bd8aa61741c8232964063fd8a75fdde9c1e982328/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d422b945683e409000c888e384546dbab9009bb92f7c0b456e217988cf316107", size = 558347, upload-time = "2025-07-01T15:54:08.591Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1c/1845c1b1fd6d827187c43afe1841d91678d7241cbdb5420a4c6de180a538/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a7711fa562ba2da1aa757e11024ad6d93bad6ad7ede5afb9af144623e5f76a", size = 587956, upload-time = "2025-07-01T15:54:09.963Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ff/9e979329dd131aa73a438c077252ddabd7df6d1a7ad7b9aacf6261f10faa/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238e8c8610cb7c29460e37184f6799547f7e09e6a9bdbdab4e8edb90986a2318", size = 554363, upload-time = "2025-07-01T15:54:11.073Z" }, + { url = "https://files.pythonhosted.org/packages/00/8b/d78cfe034b71ffbe72873a136e71acc7a831a03e37771cfe59f33f6de8a2/rpds_py-0.26.0-cp311-cp311-win32.whl", hash = "sha256:893b022bfbdf26d7bedb083efeea624e8550ca6eb98bf7fea30211ce95b9201a", size = 220123, upload-time = "2025-07-01T15:54:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/94/c1/3c8c94c7dd3905dbfde768381ce98778500a80db9924731d87ddcdb117e9/rpds_py-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:87a5531de9f71aceb8af041d72fc4cab4943648d91875ed56d2e629bef6d4c03", size = 231732, upload-time = "2025-07-01T15:54:13.434Z" }, + { url = "https://files.pythonhosted.org/packages/67/93/e936fbed1b734eabf36ccb5d93c6a2e9246fbb13c1da011624b7286fae3e/rpds_py-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:de2713f48c1ad57f89ac25b3cb7daed2156d8e822cf0eca9b96a6f990718cc41", size = 221917, upload-time = "2025-07-01T15:54:14.559Z" }, + { url = "https://files.pythonhosted.org/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d", size = 363933, upload-time = "2025-07-01T15:54:15.734Z" }, + { url = "https://files.pythonhosted.org/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136", size = 350447, upload-time = "2025-07-01T15:54:16.922Z" }, + { url = "https://files.pythonhosted.org/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582", size = 384711, upload-time = "2025-07-01T15:54:18.101Z" }, + { url = "https://files.pythonhosted.org/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e", size = 400865, upload-time = "2025-07-01T15:54:19.295Z" }, + { url = "https://files.pythonhosted.org/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15", size = 517763, upload-time = "2025-07-01T15:54:20.858Z" }, + { url = "https://files.pythonhosted.org/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8", size = 406651, upload-time = "2025-07-01T15:54:22.508Z" }, + { url = "https://files.pythonhosted.org/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a", size = 386079, upload-time = "2025-07-01T15:54:23.987Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323", size = 421379, upload-time = "2025-07-01T15:54:25.073Z" }, + { url = "https://files.pythonhosted.org/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158", size = 562033, upload-time = "2025-07-01T15:54:26.225Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3", size = 591639, upload-time = "2025-07-01T15:54:27.424Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2", size = 557105, upload-time = "2025-07-01T15:54:29.93Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44", size = 223272, upload-time = "2025-07-01T15:54:31.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c", size = 234995, upload-time = "2025-07-01T15:54:32.195Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8", size = 223198, upload-time = "2025-07-01T15:54:33.271Z" }, + { url = "https://files.pythonhosted.org/packages/6a/67/bb62d0109493b12b1c6ab00de7a5566aa84c0e44217c2d94bee1bd370da9/rpds_py-0.26.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:696764a5be111b036256c0b18cd29783fab22154690fc698062fc1b0084b511d", size = 363917, upload-time = "2025-07-01T15:54:34.755Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f3/34e6ae1925a5706c0f002a8d2d7f172373b855768149796af87bd65dcdb9/rpds_py-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6c15d2080a63aaed876e228efe4f814bc7889c63b1e112ad46fdc8b368b9e1", size = 350073, upload-time = "2025-07-01T15:54:36.292Z" }, + { url = "https://files.pythonhosted.org/packages/75/83/1953a9d4f4e4de7fd0533733e041c28135f3c21485faaef56a8aadbd96b5/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390e3170babf42462739a93321e657444f0862c6d722a291accc46f9d21ed04e", size = 384214, upload-time = "2025-07-01T15:54:37.469Z" }, + { url = "https://files.pythonhosted.org/packages/48/0e/983ed1b792b3322ea1d065e67f4b230f3b96025f5ce3878cc40af09b7533/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7da84c2c74c0f5bc97d853d9e17bb83e2dcafcff0dc48286916001cc114379a1", size = 400113, upload-time = "2025-07-01T15:54:38.954Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/36c0925fff6f660a80be259c5b4f5e53a16851f946eb080351d057698528/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c5fe114a6dd480a510b6d3661d09d67d1622c4bf20660a474507aaee7eeeee9", size = 515189, upload-time = "2025-07-01T15:54:40.57Z" }, + { url = "https://files.pythonhosted.org/packages/13/45/cbf07fc03ba7a9b54662c9badb58294ecfb24f828b9732970bd1a431ed5c/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3100b3090269f3a7ea727b06a6080d4eb7439dca4c0e91a07c5d133bb1727ea7", size = 406998, upload-time = "2025-07-01T15:54:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/6c/b0/8fa5e36e58657997873fd6a1cf621285ca822ca75b4b3434ead047daa307/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c03c9b0c64afd0320ae57de4c982801271c0c211aa2d37f3003ff5feb75bb04", size = 385903, upload-time = "2025-07-01T15:54:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f7/b25437772f9f57d7a9fbd73ed86d0dcd76b4c7c6998348c070d90f23e315/rpds_py-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5963b72ccd199ade6ee493723d18a3f21ba7d5b957017607f815788cef50eaf1", size = 419785, upload-time = "2025-07-01T15:54:46.043Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6b/63ffa55743dfcb4baf2e9e77a0b11f7f97ed96a54558fcb5717a4b2cd732/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da4e873860ad5bab3291438525cae80169daecbfafe5657f7f5fb4d6b3f96b9", size = 561329, upload-time = "2025-07-01T15:54:47.64Z" }, + { url = "https://files.pythonhosted.org/packages/2f/07/1f4f5e2886c480a2346b1e6759c00278b8a69e697ae952d82ae2e6ee5db0/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5afaddaa8e8c7f1f7b4c5c725c0070b6eed0228f705b90a1732a48e84350f4e9", size = 590875, upload-time = "2025-07-01T15:54:48.9Z" }, + { url = "https://files.pythonhosted.org/packages/cc/bc/e6639f1b91c3a55f8c41b47d73e6307051b6e246254a827ede730624c0f8/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4916dc96489616a6f9667e7526af8fa693c0fdb4f3acb0e5d9f4400eb06a47ba", size = 556636, upload-time = "2025-07-01T15:54:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/05/4c/b3917c45566f9f9a209d38d9b54a1833f2bb1032a3e04c66f75726f28876/rpds_py-0.26.0-cp313-cp313-win32.whl", hash = "sha256:2a343f91b17097c546b93f7999976fd6c9d5900617aa848c81d794e062ab302b", size = 222663, upload-time = "2025-07-01T15:54:52.023Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0b/0851bdd6025775aaa2365bb8de0697ee2558184c800bfef8d7aef5ccde58/rpds_py-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0a0b60701f2300c81b2ac88a5fb893ccfa408e1c4a555a77f908a2596eb875a5", size = 234428, upload-time = "2025-07-01T15:54:53.692Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e8/a47c64ed53149c75fb581e14a237b7b7cd18217e969c30d474d335105622/rpds_py-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:257d011919f133a4746958257f2c75238e3ff54255acd5e3e11f3ff41fd14256", size = 222571, upload-time = "2025-07-01T15:54:54.822Z" }, + { url = "https://files.pythonhosted.org/packages/89/bf/3d970ba2e2bcd17d2912cb42874107390f72873e38e79267224110de5e61/rpds_py-0.26.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:529c8156d7506fba5740e05da8795688f87119cce330c244519cf706a4a3d618", size = 360475, upload-time = "2025-07-01T15:54:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/82/9f/283e7e2979fc4ec2d8ecee506d5a3675fce5ed9b4b7cb387ea5d37c2f18d/rpds_py-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f53ec51f9d24e9638a40cabb95078ade8c99251945dad8d57bf4aabe86ecee35", size = 346692, upload-time = "2025-07-01T15:54:58.561Z" }, + { url = "https://files.pythonhosted.org/packages/e3/03/7e50423c04d78daf391da3cc4330bdb97042fc192a58b186f2d5deb7befd/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab504c4d654e4a29558eaa5bb8cea5fdc1703ea60a8099ffd9c758472cf913f", size = 379415, upload-time = "2025-07-01T15:54:59.751Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/d11ee60d4d3b16808432417951c63df803afb0e0fc672b5e8d07e9edaaae/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd0641abca296bc1a00183fe44f7fced8807ed49d501f188faa642d0e4975b83", size = 391783, upload-time = "2025-07-01T15:55:00.898Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/1069c394d9c0d6d23c5b522e1f6546b65793a22950f6e0210adcc6f97c3e/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b312fecc1d017b5327afa81d4da1480f51c68810963a7336d92203dbb3d4f1", size = 512844, upload-time = "2025-07-01T15:55:02.201Z" }, + { url = "https://files.pythonhosted.org/packages/08/3b/c4fbf0926800ed70b2c245ceca99c49f066456755f5d6eb8863c2c51e6d0/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c741107203954f6fc34d3066d213d0a0c40f7bb5aafd698fb39888af277c70d8", size = 402105, upload-time = "2025-07-01T15:55:03.698Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b0/db69b52ca07413e568dae9dc674627a22297abb144c4d6022c6d78f1e5cc/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3e55a7db08dc9a6ed5fb7103019d2c1a38a349ac41901f9f66d7f95750942f", size = 383440, upload-time = "2025-07-01T15:55:05.398Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e1/c65255ad5b63903e56b3bb3ff9dcc3f4f5c3badde5d08c741ee03903e951/rpds_py-0.26.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e851920caab2dbcae311fd28f4313c6953993893eb5c1bb367ec69d9a39e7ed", size = 412759, upload-time = "2025-07-01T15:55:08.316Z" }, + { url = "https://files.pythonhosted.org/packages/e4/22/bb731077872377a93c6e93b8a9487d0406c70208985831034ccdeed39c8e/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dfbf280da5f876d0b00c81f26bedce274e72a678c28845453885a9b3c22ae632", size = 556032, upload-time = "2025-07-01T15:55:09.52Z" }, + { url = "https://files.pythonhosted.org/packages/e0/8b/393322ce7bac5c4530fb96fc79cc9ea2f83e968ff5f6e873f905c493e1c4/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1cc81d14ddfa53d7f3906694d35d54d9d3f850ef8e4e99ee68bc0d1e5fed9a9c", size = 585416, upload-time = "2025-07-01T15:55:11.216Z" }, + { url = "https://files.pythonhosted.org/packages/49/ae/769dc372211835bf759319a7aae70525c6eb523e3371842c65b7ef41c9c6/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dca83c498b4650a91efcf7b88d669b170256bf8017a5db6f3e06c2bf031f57e0", size = 554049, upload-time = "2025-07-01T15:55:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f9/4c43f9cc203d6ba44ce3146246cdc38619d92c7bd7bad4946a3491bd5b70/rpds_py-0.26.0-cp313-cp313t-win32.whl", hash = "sha256:4d11382bcaf12f80b51d790dee295c56a159633a8e81e6323b16e55d81ae37e9", size = 218428, upload-time = "2025-07-01T15:55:14.486Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8b/9286b7e822036a4a977f2f1e851c7345c20528dbd56b687bb67ed68a8ede/rpds_py-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff110acded3c22c033e637dd8896e411c7d3a11289b2edf041f86663dbc791e9", size = 231524, upload-time = "2025-07-01T15:55:15.745Z" }, + { url = "https://files.pythonhosted.org/packages/55/07/029b7c45db910c74e182de626dfdae0ad489a949d84a468465cd0ca36355/rpds_py-0.26.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:da619979df60a940cd434084355c514c25cf8eb4cf9a508510682f6c851a4f7a", size = 364292, upload-time = "2025-07-01T15:55:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/13/d1/9b3d3f986216b4d1f584878dca15ce4797aaf5d372d738974ba737bf68d6/rpds_py-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea89a2458a1a75f87caabefe789c87539ea4e43b40f18cff526052e35bbb4fdf", size = 350334, upload-time = "2025-07-01T15:55:18.922Z" }, + { url = "https://files.pythonhosted.org/packages/18/98/16d5e7bc9ec715fa9668731d0cf97f6b032724e61696e2db3d47aeb89214/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feac1045b3327a45944e7dcbeb57530339f6b17baff154df51ef8b0da34c8c12", size = 384875, upload-time = "2025-07-01T15:55:20.399Z" }, + { url = "https://files.pythonhosted.org/packages/f9/13/aa5e2b1ec5ab0e86a5c464d53514c0467bec6ba2507027d35fc81818358e/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b818a592bd69bfe437ee8368603d4a2d928c34cffcdf77c2e761a759ffd17d20", size = 399993, upload-time = "2025-07-01T15:55:21.729Z" }, + { url = "https://files.pythonhosted.org/packages/17/03/8021810b0e97923abdbab6474c8b77c69bcb4b2c58330777df9ff69dc559/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a8b0dd8648709b62d9372fc00a57466f5fdeefed666afe3fea5a6c9539a0331", size = 516683, upload-time = "2025-07-01T15:55:22.918Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b1/da8e61c87c2f3d836954239fdbbfb477bb7b54d74974d8f6fcb34342d166/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d3498ad0df07d81112aa6ec6c95a7e7b1ae00929fb73e7ebee0f3faaeabad2f", size = 408825, upload-time = "2025-07-01T15:55:24.207Z" }, + { url = "https://files.pythonhosted.org/packages/38/bc/1fc173edaaa0e52c94b02a655db20697cb5fa954ad5a8e15a2c784c5cbdd/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4146ccb15be237fdef10f331c568e1b0e505f8c8c9ed5d67759dac58ac246", size = 387292, upload-time = "2025-07-01T15:55:25.554Z" }, + { url = "https://files.pythonhosted.org/packages/7c/eb/3a9bb4bd90867d21916f253caf4f0d0be7098671b6715ad1cead9fe7bab9/rpds_py-0.26.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9a63785467b2d73635957d32a4f6e73d5e4df497a16a6392fa066b753e87387", size = 420435, upload-time = "2025-07-01T15:55:27.798Z" }, + { url = "https://files.pythonhosted.org/packages/cd/16/e066dcdb56f5632713445271a3f8d3d0b426d51ae9c0cca387799df58b02/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de4ed93a8c91debfd5a047be327b7cc8b0cc6afe32a716bbbc4aedca9e2a83af", size = 562410, upload-time = "2025-07-01T15:55:29.057Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/ddbdec7eb82a0dc2e455be44c97c71c232983e21349836ce9f272e8a3c29/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:caf51943715b12af827696ec395bfa68f090a4c1a1d2509eb4e2cb69abbbdb33", size = 590724, upload-time = "2025-07-01T15:55:30.719Z" }, + { url = "https://files.pythonhosted.org/packages/2c/b4/95744085e65b7187d83f2fcb0bef70716a1ea0a9e5d8f7f39a86e5d83424/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4a59e5bc386de021f56337f757301b337d7ab58baa40174fb150accd480bc953", size = 558285, upload-time = "2025-07-01T15:55:31.981Z" }, + { url = "https://files.pythonhosted.org/packages/37/37/6309a75e464d1da2559446f9c811aa4d16343cebe3dbb73701e63f760caa/rpds_py-0.26.0-cp314-cp314-win32.whl", hash = "sha256:92c8db839367ef16a662478f0a2fe13e15f2227da3c1430a782ad0f6ee009ec9", size = 223459, upload-time = "2025-07-01T15:55:33.312Z" }, + { url = "https://files.pythonhosted.org/packages/d9/6f/8e9c11214c46098b1d1391b7e02b70bb689ab963db3b19540cba17315291/rpds_py-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:b0afb8cdd034150d4d9f53926226ed27ad15b7f465e93d7468caaf5eafae0d37", size = 236083, upload-time = "2025-07-01T15:55:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/47/af/9c4638994dd623d51c39892edd9d08e8be8220a4b7e874fa02c2d6e91955/rpds_py-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:ca3f059f4ba485d90c8dc75cb5ca897e15325e4e609812ce57f896607c1c0867", size = 223291, upload-time = "2025-07-01T15:55:36.202Z" }, + { url = "https://files.pythonhosted.org/packages/4d/db/669a241144460474aab03e254326b32c42def83eb23458a10d163cb9b5ce/rpds_py-0.26.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5afea17ab3a126006dc2f293b14ffc7ef3c85336cf451564a0515ed7648033da", size = 361445, upload-time = "2025-07-01T15:55:37.483Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2d/133f61cc5807c6c2fd086a46df0eb8f63a23f5df8306ff9f6d0fd168fecc/rpds_py-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69f0c0a3df7fd3a7eec50a00396104bb9a843ea6d45fcc31c2d5243446ffd7a7", size = 347206, upload-time = "2025-07-01T15:55:38.828Z" }, + { url = "https://files.pythonhosted.org/packages/05/bf/0e8fb4c05f70273469eecf82f6ccf37248558526a45321644826555db31b/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:801a71f70f9813e82d2513c9a96532551fce1e278ec0c64610992c49c04c2dad", size = 380330, upload-time = "2025-07-01T15:55:40.175Z" }, + { url = "https://files.pythonhosted.org/packages/d4/a8/060d24185d8b24d3923322f8d0ede16df4ade226a74e747b8c7c978e3dd3/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df52098cde6d5e02fa75c1f6244f07971773adb4a26625edd5c18fee906fa84d", size = 392254, upload-time = "2025-07-01T15:55:42.015Z" }, + { url = "https://files.pythonhosted.org/packages/b9/7b/7c2e8a9ee3e6bc0bae26bf29f5219955ca2fbb761dca996a83f5d2f773fe/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bc596b30f86dc6f0929499c9e574601679d0341a0108c25b9b358a042f51bca", size = 516094, upload-time = "2025-07-01T15:55:43.603Z" }, + { url = "https://files.pythonhosted.org/packages/75/d6/f61cafbed8ba1499b9af9f1777a2a199cd888f74a96133d8833ce5eaa9c5/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dfbe56b299cf5875b68eb6f0ebaadc9cac520a1989cac0db0765abfb3709c19", size = 402889, upload-time = "2025-07-01T15:55:45.275Z" }, + { url = "https://files.pythonhosted.org/packages/92/19/c8ac0a8a8df2dd30cdec27f69298a5c13e9029500d6d76718130f5e5be10/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac64f4b2bdb4ea622175c9ab7cf09444e412e22c0e02e906978b3b488af5fde8", size = 384301, upload-time = "2025-07-01T15:55:47.098Z" }, + { url = "https://files.pythonhosted.org/packages/41/e1/6b1859898bc292a9ce5776016c7312b672da00e25cec74d7beced1027286/rpds_py-0.26.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:181ef9b6bbf9845a264f9aa45c31836e9f3c1f13be565d0d010e964c661d1e2b", size = 412891, upload-time = "2025-07-01T15:55:48.412Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b9/ceb39af29913c07966a61367b3c08b4f71fad841e32c6b59a129d5974698/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:49028aa684c144ea502a8e847d23aed5e4c2ef7cadfa7d5eaafcb40864844b7a", size = 557044, upload-time = "2025-07-01T15:55:49.816Z" }, + { url = "https://files.pythonhosted.org/packages/2f/27/35637b98380731a521f8ec4f3fd94e477964f04f6b2f8f7af8a2d889a4af/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e5d524d68a474a9688336045bbf76cb0def88549c1b2ad9dbfec1fb7cfbe9170", size = 585774, upload-time = "2025-07-01T15:55:51.192Z" }, + { url = "https://files.pythonhosted.org/packages/52/d9/3f0f105420fecd18551b678c9a6ce60bd23986098b252a56d35781b3e7e9/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1851f429b822831bd2edcbe0cfd12ee9ea77868f8d3daf267b189371671c80e", size = 554886, upload-time = "2025-07-01T15:55:52.541Z" }, + { url = "https://files.pythonhosted.org/packages/6b/c5/347c056a90dc8dd9bc240a08c527315008e1b5042e7a4cf4ac027be9d38a/rpds_py-0.26.0-cp314-cp314t-win32.whl", hash = "sha256:7bdb17009696214c3b66bb3590c6d62e14ac5935e53e929bcdbc5a495987a84f", size = 219027, upload-time = "2025-07-01T15:55:53.874Z" }, + { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821, upload-time = "2025-07-01T15:55:55.167Z" }, + { url = "https://files.pythonhosted.org/packages/fb/74/846ab687119c9d31fc21ab1346ef9233c31035ce53c0e2d43a130a0c5a5e/rpds_py-0.26.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:7a48af25d9b3c15684059d0d1fc0bc30e8eee5ca521030e2bffddcab5be40226", size = 372786, upload-time = "2025-07-01T15:55:56.512Z" }, + { url = "https://files.pythonhosted.org/packages/33/02/1f9e465cb1a6032d02b17cd117c7bd9fb6156bc5b40ffeb8053d8a2aa89c/rpds_py-0.26.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0c71c2f6bf36e61ee5c47b2b9b5d47e4d1baad6426bfed9eea3e858fc6ee8806", size = 358062, upload-time = "2025-07-01T15:55:58.084Z" }, + { url = "https://files.pythonhosted.org/packages/2a/49/81a38e3c67ac943907a9711882da3d87758c82cf26b2120b8128e45d80df/rpds_py-0.26.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d815d48b1804ed7867b539236b6dd62997850ca1c91cad187f2ddb1b7bbef19", size = 381576, upload-time = "2025-07-01T15:55:59.422Z" }, + { url = "https://files.pythonhosted.org/packages/14/37/418f030a76ef59f41e55f9dc916af8afafa3c9e3be38df744b2014851474/rpds_py-0.26.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84cfbd4d4d2cdeb2be61a057a258d26b22877266dd905809e94172dff01a42ae", size = 397062, upload-time = "2025-07-01T15:56:00.868Z" }, + { url = "https://files.pythonhosted.org/packages/47/e3/9090817a8f4388bfe58e28136e9682fa7872a06daff2b8a2f8c78786a6e1/rpds_py-0.26.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbaa70553ca116c77717f513e08815aec458e6b69a028d4028d403b3bc84ff37", size = 516277, upload-time = "2025-07-01T15:56:02.672Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3a/1ec3dd93250fb8023f27d49b3f92e13f679141f2e59a61563f88922c2821/rpds_py-0.26.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39bfea47c375f379d8e87ab4bb9eb2c836e4f2069f0f65731d85e55d74666387", size = 402604, upload-time = "2025-07-01T15:56:04.453Z" }, + { url = "https://files.pythonhosted.org/packages/f2/98/9133c06e42ec3ce637936263c50ac647f879b40a35cfad2f5d4ad418a439/rpds_py-0.26.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1533b7eb683fb5f38c1d68a3c78f5fdd8f1412fa6b9bf03b40f450785a0ab915", size = 383664, upload-time = "2025-07-01T15:56:05.823Z" }, + { url = "https://files.pythonhosted.org/packages/a9/10/a59ce64099cc77c81adb51f06909ac0159c19a3e2c9d9613bab171f4730f/rpds_py-0.26.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c5ab0ee51f560d179b057555b4f601b7df909ed31312d301b99f8b9fc6028284", size = 415944, upload-time = "2025-07-01T15:56:07.132Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f1/ae0c60b3be9df9d5bef3527d83b8eb4b939e3619f6dd8382840e220a27df/rpds_py-0.26.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e5162afc9e0d1f9cae3b577d9c29ddbab3505ab39012cb794d94a005825bde21", size = 558311, upload-time = "2025-07-01T15:56:08.484Z" }, + { url = "https://files.pythonhosted.org/packages/fb/2b/bf1498ebb3ddc5eff2fe3439da88963d1fc6e73d1277fa7ca0c72620d167/rpds_py-0.26.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:43f10b007033f359bc3fa9cd5e6c1e76723f056ffa9a6b5c117cc35720a80292", size = 587928, upload-time = "2025-07-01T15:56:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/e6b949edf7af5629848c06d6e544a36c9f2781e2d8d03b906de61ada04d0/rpds_py-0.26.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e3730a48e5622e598293eee0762b09cff34dd3f271530f47b0894891281f051d", size = 554554, upload-time = "2025-07-01T15:56:11.775Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1c/aa0298372ea898620d4706ad26b5b9e975550a4dd30bd042b0fe9ae72cce/rpds_py-0.26.0-cp39-cp39-win32.whl", hash = "sha256:4b1f66eb81eab2e0ff5775a3a312e5e2e16bf758f7b06be82fb0d04078c7ac51", size = 220273, upload-time = "2025-07-01T15:56:13.273Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b0/8b3bef6ad0b35c172d1c87e2e5c2bb027d99e2a7bc7a16f744e66cf318f3/rpds_py-0.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:519067e29f67b5c90e64fb1a6b6e9d2ec0ba28705c51956637bac23a2f4ddae1", size = 231627, upload-time = "2025-07-01T15:56:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/ef/9a/1f033b0b31253d03d785b0cd905bc127e555ab496ea6b4c7c2e1f951f2fd/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3c0909c5234543ada2515c05dc08595b08d621ba919629e94427e8e03539c958", size = 373226, upload-time = "2025-07-01T15:56:16.578Z" }, + { url = "https://files.pythonhosted.org/packages/58/29/5f88023fd6aaaa8ca3c4a6357ebb23f6f07da6079093ccf27c99efce87db/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c1fb0cda2abcc0ac62f64e2ea4b4e64c57dfd6b885e693095460c61bde7bb18e", size = 359230, upload-time = "2025-07-01T15:56:17.978Z" }, + { url = "https://files.pythonhosted.org/packages/6c/6c/13eaebd28b439da6964dde22712b52e53fe2824af0223b8e403249d10405/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d142d2d6cf9b31c12aa4878d82ed3b2324226270b89b676ac62ccd7df52d08", size = 382363, upload-time = "2025-07-01T15:56:19.977Z" }, + { url = "https://files.pythonhosted.org/packages/55/fc/3bb9c486b06da19448646f96147796de23c5811ef77cbfc26f17307b6a9d/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a547e21c5610b7e9093d870be50682a6a6cf180d6da0f42c47c306073bfdbbf6", size = 397146, upload-time = "2025-07-01T15:56:21.39Z" }, + { url = "https://files.pythonhosted.org/packages/15/18/9d1b79eb4d18e64ba8bba9e7dec6f9d6920b639f22f07ee9368ca35d4673/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35e9a70a0f335371275cdcd08bc5b8051ac494dd58bff3bbfb421038220dc871", size = 514804, upload-time = "2025-07-01T15:56:22.78Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5a/175ad7191bdbcd28785204621b225ad70e85cdfd1e09cc414cb554633b21/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dfa6115c6def37905344d56fb54c03afc49104e2ca473d5dedec0f6606913b4", size = 402820, upload-time = "2025-07-01T15:56:24.584Z" }, + { url = "https://files.pythonhosted.org/packages/11/45/6a67ecf6d61c4d4aff4bc056e864eec4b2447787e11d1c2c9a0242c6e92a/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:313cfcd6af1a55a286a3c9a25f64af6d0e46cf60bc5798f1db152d97a216ff6f", size = 384567, upload-time = "2025-07-01T15:56:26.064Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ba/16589da828732b46454c61858950a78fe4c931ea4bf95f17432ffe64b241/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7bf2496fa563c046d05e4d232d7b7fd61346e2402052064b773e5c378bf6f73", size = 416520, upload-time = "2025-07-01T15:56:27.608Z" }, + { url = "https://files.pythonhosted.org/packages/81/4b/00092999fc7c0c266045e984d56b7314734cc400a6c6dc4d61a35f135a9d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:aa81873e2c8c5aa616ab8e017a481a96742fdf9313c40f14338ca7dbf50cb55f", size = 559362, upload-time = "2025-07-01T15:56:29.078Z" }, + { url = "https://files.pythonhosted.org/packages/96/0c/43737053cde1f93ac4945157f7be1428724ab943e2132a0d235a7e161d4e/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:68ffcf982715f5b5b7686bdd349ff75d422e8f22551000c24b30eaa1b7f7ae84", size = 588113, upload-time = "2025-07-01T15:56:30.485Z" }, + { url = "https://files.pythonhosted.org/packages/46/46/8e38f6161466e60a997ed7e9951ae5de131dedc3cf778ad35994b4af823d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6188de70e190847bb6db3dc3981cbadff87d27d6fe9b4f0e18726d55795cee9b", size = 555429, upload-time = "2025-07-01T15:56:31.956Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ac/65da605e9f1dd643ebe615d5bbd11b6efa1d69644fc4bf623ea5ae385a82/rpds_py-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1c962145c7473723df9722ba4c058de12eb5ebedcb4e27e7d902920aa3831ee8", size = 231950, upload-time = "2025-07-01T15:56:33.337Z" }, + { url = "https://files.pythonhosted.org/packages/51/f2/b5c85b758a00c513bb0389f8fc8e61eb5423050c91c958cdd21843faa3e6/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f61a9326f80ca59214d1cceb0a09bb2ece5b2563d4e0cd37bfd5515c28510674", size = 373505, upload-time = "2025-07-01T15:56:34.716Z" }, + { url = "https://files.pythonhosted.org/packages/23/e0/25db45e391251118e915e541995bb5f5ac5691a3b98fb233020ba53afc9b/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:183f857a53bcf4b1b42ef0f57ca553ab56bdd170e49d8091e96c51c3d69ca696", size = 359468, upload-time = "2025-07-01T15:56:36.219Z" }, + { url = "https://files.pythonhosted.org/packages/0b/73/dd5ee6075bb6491be3a646b301dfd814f9486d924137a5098e61f0487e16/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:941c1cfdf4799d623cf3aa1d326a6b4fdb7a5799ee2687f3516738216d2262fb", size = 382680, upload-time = "2025-07-01T15:56:37.644Z" }, + { url = "https://files.pythonhosted.org/packages/2f/10/84b522ff58763a5c443f5bcedc1820240e454ce4e620e88520f04589e2ea/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72a8d9564a717ee291f554eeb4bfeafe2309d5ec0aa6c475170bdab0f9ee8e88", size = 397035, upload-time = "2025-07-01T15:56:39.241Z" }, + { url = "https://files.pythonhosted.org/packages/06/ea/8667604229a10a520fcbf78b30ccc278977dcc0627beb7ea2c96b3becef0/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:511d15193cbe013619dd05414c35a7dedf2088fcee93c6bbb7c77859765bd4e8", size = 514922, upload-time = "2025-07-01T15:56:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/24/e6/9ed5b625c0661c4882fc8cdf302bf8e96c73c40de99c31e0b95ed37d508c/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aea1f9741b603a8d8fedb0ed5502c2bc0accbc51f43e2ad1337fe7259c2b77a5", size = 402822, upload-time = "2025-07-01T15:56:42.137Z" }, + { url = "https://files.pythonhosted.org/packages/8a/58/212c7b6fd51946047fb45d3733da27e2fa8f7384a13457c874186af691b1/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4019a9d473c708cf2f16415688ef0b4639e07abaa569d72f74745bbeffafa2c7", size = 384336, upload-time = "2025-07-01T15:56:44.239Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f5/a40ba78748ae8ebf4934d4b88e77b98497378bc2c24ba55ebe87a4e87057/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:093d63b4b0f52d98ebae33b8c50900d3d67e0666094b1be7a12fffd7f65de74b", size = 416871, upload-time = "2025-07-01T15:56:46.284Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a6/33b1fc0c9f7dcfcfc4a4353daa6308b3ece22496ceece348b3e7a7559a09/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2abe21d8ba64cded53a2a677e149ceb76dcf44284202d737178afe7ba540c1eb", size = 559439, upload-time = "2025-07-01T15:56:48.549Z" }, + { url = "https://files.pythonhosted.org/packages/71/2d/ceb3f9c12f8cfa56d34995097f6cd99da1325642c60d1b6680dd9df03ed8/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:4feb7511c29f8442cbbc28149a92093d32e815a28aa2c50d333826ad2a20fdf0", size = 588380, upload-time = "2025-07-01T15:56:50.086Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c", size = 555334, upload-time = "2025-07-01T15:56:51.703Z" }, + { url = "https://files.pythonhosted.org/packages/7e/78/a08e2f28e91c7e45db1150813c6d760a0fb114d5652b1373897073369e0d/rpds_py-0.26.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a90a13408a7a856b87be8a9f008fff53c5080eea4e4180f6c2e546e4a972fb5d", size = 373157, upload-time = "2025-07-01T15:56:53.291Z" }, + { url = "https://files.pythonhosted.org/packages/52/01/ddf51517497c8224fb0287e9842b820ed93748bc28ea74cab56a71e3dba4/rpds_py-0.26.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ac51b65e8dc76cf4949419c54c5528adb24fc721df722fd452e5fbc236f5c40", size = 358827, upload-time = "2025-07-01T15:56:54.963Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f4/acaefa44b83705a4fcadd68054280127c07cdb236a44a1c08b7c5adad40b/rpds_py-0.26.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59b2093224a18c6508d95cfdeba8db9cbfd6f3494e94793b58972933fcee4c6d", size = 382182, upload-time = "2025-07-01T15:56:56.474Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a2/d72ac03d37d33f6ff4713ca4c704da0c3b1b3a959f0bf5eb738c0ad94ea2/rpds_py-0.26.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f01a5d6444a3258b00dc07b6ea4733e26f8072b788bef750baa37b370266137", size = 397123, upload-time = "2025-07-01T15:56:58.272Z" }, + { url = "https://files.pythonhosted.org/packages/74/58/c053e9d1da1d3724434dd7a5f506623913e6404d396ff3cf636a910c0789/rpds_py-0.26.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6e2c12160c72aeda9d1283e612f68804621f448145a210f1bf1d79151c47090", size = 516285, upload-time = "2025-07-01T15:57:00.283Z" }, + { url = "https://files.pythonhosted.org/packages/94/41/c81e97ee88b38b6d1847c75f2274dee8d67cb8d5ed7ca8c6b80442dead75/rpds_py-0.26.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb28c1f569f8d33b2b5dcd05d0e6ef7005d8639c54c2f0be824f05aedf715255", size = 402182, upload-time = "2025-07-01T15:57:02.587Z" }, + { url = "https://files.pythonhosted.org/packages/74/74/38a176b34ce5197b4223e295f36350dd90713db13cf3c3b533e8e8f7484e/rpds_py-0.26.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1766b5724c3f779317d5321664a343c07773c8c5fd1532e4039e6cc7d1a815be", size = 384436, upload-time = "2025-07-01T15:57:04.125Z" }, + { url = "https://files.pythonhosted.org/packages/e4/21/f40b9a5709d7078372c87fd11335469dc4405245528b60007cd4078ed57a/rpds_py-0.26.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b6d9e5a2ed9c4988c8f9b28b3bc0e3e5b1aaa10c28d210a594ff3a8c02742daf", size = 417039, upload-time = "2025-07-01T15:57:05.608Z" }, + { url = "https://files.pythonhosted.org/packages/02/ee/ed835925731c7e87306faa80a3a5e17b4d0f532083155e7e00fe1cd4e242/rpds_py-0.26.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:b5f7a446ddaf6ca0fad9a5535b56fbfc29998bf0e0b450d174bbec0d600e1d72", size = 559111, upload-time = "2025-07-01T15:57:07.371Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/d6e9e686b8ffb6139b82eb1c319ef32ae99aeb21f7e4bf45bba44a760d09/rpds_py-0.26.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:eed5ac260dd545fbc20da5f4f15e7efe36a55e0e7cf706e4ec005b491a9546a0", size = 588609, upload-time = "2025-07-01T15:57:09.319Z" }, + { url = "https://files.pythonhosted.org/packages/e5/96/09bcab08fa12a69672716b7f86c672ee7f79c5319f1890c5a79dcb8e0df2/rpds_py-0.26.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:582462833ba7cee52e968b0341b85e392ae53d44c0f9af6a5927c80e539a8b67", size = 555212, upload-time = "2025-07-01T15:57:10.905Z" }, + { url = "https://files.pythonhosted.org/packages/2c/07/c554b6ed0064b6e0350a622714298e930b3cf5a3d445a2e25c412268abcf/rpds_py-0.26.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:69a607203441e07e9a8a529cff1d5b73f6a160f22db1097211e6212a68567d11", size = 232048, upload-time = "2025-07-01T15:57:12.473Z" }, ] [[package]] name = "ruff" -version = "0.12.1" +version = "0.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/97/38/796a101608a90494440856ccfb52b1edae90de0b817e76bfade66b12d320/ruff-0.12.1.tar.gz", hash = "sha256:806bbc17f1104fd57451a98a58df35388ee3ab422e029e8f5cf30aa4af2c138c", size = 4413426, upload-time = "2025-06-26T20:34:14.784Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/3d/d9a195676f25d00dbfcf3cf95fdd4c685c497fcfa7e862a44ac5e4e96480/ruff-0.12.2.tar.gz", hash = "sha256:d7b4f55cd6f325cb7621244f19c873c565a08aff5a4ba9c69aa7355f3f7afd3e", size = 4432239, upload-time = "2025-07-03T16:40:19.566Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/bf/3dba52c1d12ab5e78d75bd78ad52fb85a6a1f29cc447c2423037b82bed0d/ruff-0.12.1-py3-none-linux_armv6l.whl", hash = "sha256:6013a46d865111e2edb71ad692fbb8262e6c172587a57c0669332a449384a36b", size = 10305649, upload-time = "2025-06-26T20:33:39.242Z" }, - { url = "https://files.pythonhosted.org/packages/8c/65/dab1ba90269bc8c81ce1d499a6517e28fe6f87b2119ec449257d0983cceb/ruff-0.12.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b3f75a19e03a4b0757d1412edb7f27cffb0c700365e9d6b60bc1b68d35bc89e0", size = 11120201, upload-time = "2025-06-26T20:33:42.207Z" }, - { url = "https://files.pythonhosted.org/packages/3f/3e/2d819ffda01defe857fa2dd4cba4d19109713df4034cc36f06bbf582d62a/ruff-0.12.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9a256522893cb7e92bb1e1153283927f842dea2e48619c803243dccc8437b8be", size = 10466769, upload-time = "2025-06-26T20:33:44.102Z" }, - { url = "https://files.pythonhosted.org/packages/63/37/bde4cf84dbd7821c8de56ec4ccc2816bce8125684f7b9e22fe4ad92364de/ruff-0.12.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:069052605fe74c765a5b4272eb89880e0ff7a31e6c0dbf8767203c1fbd31c7ff", size = 10660902, upload-time = "2025-06-26T20:33:45.98Z" }, - { url = "https://files.pythonhosted.org/packages/0e/3a/390782a9ed1358c95e78ccc745eed1a9d657a537e5c4c4812fce06c8d1a0/ruff-0.12.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a684f125a4fec2d5a6501a466be3841113ba6847827be4573fddf8308b83477d", size = 10167002, upload-time = "2025-06-26T20:33:47.81Z" }, - { url = "https://files.pythonhosted.org/packages/6d/05/f2d4c965009634830e97ffe733201ec59e4addc5b1c0efa035645baa9e5f/ruff-0.12.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdecdef753bf1e95797593007569d8e1697a54fca843d78f6862f7dc279e23bd", size = 11751522, upload-time = "2025-06-26T20:33:49.857Z" }, - { url = "https://files.pythonhosted.org/packages/35/4e/4bfc519b5fcd462233f82fc20ef8b1e5ecce476c283b355af92c0935d5d9/ruff-0.12.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:70d52a058c0e7b88b602f575d23596e89bd7d8196437a4148381a3f73fcd5010", size = 12520264, upload-time = "2025-06-26T20:33:52.199Z" }, - { url = "https://files.pythonhosted.org/packages/85/b2/7756a6925da236b3a31f234b4167397c3e5f91edb861028a631546bad719/ruff-0.12.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84d0a69d1e8d716dfeab22d8d5e7c786b73f2106429a933cee51d7b09f861d4e", size = 12133882, upload-time = "2025-06-26T20:33:54.231Z" }, - { url = "https://files.pythonhosted.org/packages/dd/00/40da9c66d4a4d51291e619be6757fa65c91b92456ff4f01101593f3a1170/ruff-0.12.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cc32e863adcf9e71690248607ccdf25252eeeab5193768e6873b901fd441fed", size = 11608941, upload-time = "2025-06-26T20:33:56.202Z" }, - { url = "https://files.pythonhosted.org/packages/91/e7/f898391cc026a77fbe68dfea5940f8213622474cb848eb30215538a2dadf/ruff-0.12.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fd49a4619f90d5afc65cf42e07b6ae98bb454fd5029d03b306bd9e2273d44cc", size = 11602887, upload-time = "2025-06-26T20:33:58.47Z" }, - { url = "https://files.pythonhosted.org/packages/f6/02/0891872fc6aab8678084f4cf8826f85c5d2d24aa9114092139a38123f94b/ruff-0.12.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ed5af6aaaea20710e77698e2055b9ff9b3494891e1b24d26c07055459bb717e9", size = 10521742, upload-time = "2025-06-26T20:34:00.465Z" }, - { url = "https://files.pythonhosted.org/packages/2a/98/d6534322c74a7d47b0f33b036b2498ccac99d8d8c40edadb552c038cecf1/ruff-0.12.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:801d626de15e6bf988fbe7ce59b303a914ff9c616d5866f8c79eb5012720ae13", size = 10149909, upload-time = "2025-06-26T20:34:02.603Z" }, - { url = "https://files.pythonhosted.org/packages/34/5c/9b7ba8c19a31e2b6bd5e31aa1e65b533208a30512f118805371dbbbdf6a9/ruff-0.12.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2be9d32a147f98a1972c1e4df9a6956d612ca5f5578536814372113d09a27a6c", size = 11136005, upload-time = "2025-06-26T20:34:04.723Z" }, - { url = "https://files.pythonhosted.org/packages/dc/34/9bbefa4d0ff2c000e4e533f591499f6b834346025e11da97f4ded21cb23e/ruff-0.12.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:49b7ce354eed2a322fbaea80168c902de9504e6e174fd501e9447cad0232f9e6", size = 11648579, upload-time = "2025-06-26T20:34:06.766Z" }, - { url = "https://files.pythonhosted.org/packages/6f/1c/20cdb593783f8f411839ce749ec9ae9e4298c2b2079b40295c3e6e2089e1/ruff-0.12.1-py3-none-win32.whl", hash = "sha256:d973fa626d4c8267848755bd0414211a456e99e125dcab147f24daa9e991a245", size = 10519495, upload-time = "2025-06-26T20:34:08.718Z" }, - { url = "https://files.pythonhosted.org/packages/cf/56/7158bd8d3cf16394928f47c637d39a7d532268cd45220bdb6cd622985760/ruff-0.12.1-py3-none-win_amd64.whl", hash = "sha256:9e1123b1c033f77bd2590e4c1fe7e8ea72ef990a85d2484351d408224d603013", size = 11547485, upload-time = "2025-06-26T20:34:11.008Z" }, - { url = "https://files.pythonhosted.org/packages/91/d0/6902c0d017259439d6fd2fd9393cea1cfe30169940118b007d5e0ea7e954/ruff-0.12.1-py3-none-win_arm64.whl", hash = "sha256:78ad09a022c64c13cc6077707f036bab0fac8cd7088772dcd1e5be21c5002efc", size = 10691209, upload-time = "2025-06-26T20:34:12.928Z" }, + { url = "https://files.pythonhosted.org/packages/74/b6/2098d0126d2d3318fd5bec3ad40d06c25d377d95749f7a0c5af17129b3b1/ruff-0.12.2-py3-none-linux_armv6l.whl", hash = "sha256:093ea2b221df1d2b8e7ad92fc6ffdca40a2cb10d8564477a987b44fd4008a7be", size = 10369761, upload-time = "2025-07-03T16:39:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/b1/4b/5da0142033dbe155dc598cfb99262d8ee2449d76920ea92c4eeb9547c208/ruff-0.12.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:09e4cf27cc10f96b1708100fa851e0daf21767e9709e1649175355280e0d950e", size = 11155659, upload-time = "2025-07-03T16:39:42.294Z" }, + { url = "https://files.pythonhosted.org/packages/3e/21/967b82550a503d7c5c5c127d11c935344b35e8c521f52915fc858fb3e473/ruff-0.12.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ae64755b22f4ff85e9c52d1f82644abd0b6b6b6deedceb74bd71f35c24044cc", size = 10537769, upload-time = "2025-07-03T16:39:44.75Z" }, + { url = "https://files.pythonhosted.org/packages/33/91/00cff7102e2ec71a4890fb7ba1803f2cdb122d82787c7d7cf8041fe8cbc1/ruff-0.12.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eb3a6b2db4d6e2c77e682f0b988d4d61aff06860158fdb413118ca133d57922", size = 10717602, upload-time = "2025-07-03T16:39:47.652Z" }, + { url = "https://files.pythonhosted.org/packages/9b/eb/928814daec4e1ba9115858adcda44a637fb9010618721937491e4e2283b8/ruff-0.12.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73448de992d05517170fc37169cbca857dfeaeaa8c2b9be494d7bcb0d36c8f4b", size = 10198772, upload-time = "2025-07-03T16:39:49.641Z" }, + { url = "https://files.pythonhosted.org/packages/50/fa/f15089bc20c40f4f72334f9145dde55ab2b680e51afb3b55422effbf2fb6/ruff-0.12.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b94317cbc2ae4a2771af641739f933934b03555e51515e6e021c64441532d", size = 11845173, upload-time = "2025-07-03T16:39:52.069Z" }, + { url = "https://files.pythonhosted.org/packages/43/9f/1f6f98f39f2b9302acc161a4a2187b1e3a97634fe918a8e731e591841cf4/ruff-0.12.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45fc42c3bf1d30d2008023a0a9a0cfb06bf9835b147f11fe0679f21ae86d34b1", size = 12553002, upload-time = "2025-07-03T16:39:54.551Z" }, + { url = "https://files.pythonhosted.org/packages/d8/70/08991ac46e38ddd231c8f4fd05ef189b1b94be8883e8c0c146a025c20a19/ruff-0.12.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce48f675c394c37e958bf229fb5c1e843e20945a6d962cf3ea20b7a107dcd9f4", size = 12171330, upload-time = "2025-07-03T16:39:57.55Z" }, + { url = "https://files.pythonhosted.org/packages/88/a9/5a55266fec474acfd0a1c73285f19dd22461d95a538f29bba02edd07a5d9/ruff-0.12.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793d8859445ea47591272021a81391350205a4af65a9392401f418a95dfb75c9", size = 11774717, upload-time = "2025-07-03T16:39:59.78Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/0c270e458fc73c46c0d0f7cf970bb14786e5fdb88c87b5e423a4bd65232b/ruff-0.12.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6932323db80484dda89153da3d8e58164d01d6da86857c79f1961934354992da", size = 11646659, upload-time = "2025-07-03T16:40:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b6/45ab96070c9752af37f0be364d849ed70e9ccede07675b0ec4e3ef76b63b/ruff-0.12.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6aa7e623a3a11538108f61e859ebf016c4f14a7e6e4eba1980190cacb57714ce", size = 10604012, upload-time = "2025-07-03T16:40:04.363Z" }, + { url = "https://files.pythonhosted.org/packages/86/91/26a6e6a424eb147cc7627eebae095cfa0b4b337a7c1c413c447c9ebb72fd/ruff-0.12.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2a4a20aeed74671b2def096bdf2eac610c7d8ffcbf4fb0e627c06947a1d7078d", size = 10176799, upload-time = "2025-07-03T16:40:06.514Z" }, + { url = "https://files.pythonhosted.org/packages/f5/0c/9f344583465a61c8918a7cda604226e77b2c548daf8ef7c2bfccf2b37200/ruff-0.12.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:71a4c550195612f486c9d1f2b045a600aeba851b298c667807ae933478fcef04", size = 11241507, upload-time = "2025-07-03T16:40:08.708Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/99c34ded8fb5f86c0280278fa89a0066c3760edc326e935ce0b1550d315d/ruff-0.12.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4987b8f4ceadf597c927beee65a5eaf994c6e2b631df963f86d8ad1bdea99342", size = 11717609, upload-time = "2025-07-03T16:40:10.836Z" }, + { url = "https://files.pythonhosted.org/packages/51/de/8589fa724590faa057e5a6d171e7f2f6cffe3287406ef40e49c682c07d89/ruff-0.12.2-py3-none-win32.whl", hash = "sha256:369ffb69b70cd55b6c3fc453b9492d98aed98062db9fec828cdfd069555f5f1a", size = 10523823, upload-time = "2025-07-03T16:40:13.203Z" }, + { url = "https://files.pythonhosted.org/packages/94/47/8abf129102ae4c90cba0c2199a1a9b0fa896f6f806238d6f8c14448cc748/ruff-0.12.2-py3-none-win_amd64.whl", hash = "sha256:dca8a3b6d6dc9810ed8f328d406516bf4d660c00caeaef36eb831cf4871b0639", size = 11629831, upload-time = "2025-07-03T16:40:15.478Z" }, + { url = "https://files.pythonhosted.org/packages/e2/1f/72d2946e3cc7456bb837e88000eb3437e55f80db339c840c04015a11115d/ruff-0.12.2-py3-none-win_arm64.whl", hash = "sha256:48d6c6bfb4761df68bc05ae630e24f506755e702d4fb08f08460be778c7ccb12", size = 10735334, upload-time = "2025-07-03T16:40:17.677Z" }, ] [[package]] @@ -3092,18 +3116,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] -[[package]] -name = "typing-inspection" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, -] - [[package]] name = "tzdata" version = "2025.2" From f5b888dd720136caa0feb9559df67c0b4368a7d6 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Thu, 3 Jul 2025 19:53:38 -0400 Subject: [PATCH 05/22] run CI on `v1` branch temporarily (#5341) temporarily run CI on v1 as well --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 050daa704..db20a88de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: - branches: [main] + branches: [main, v1] pull_request: permissions: From d1710e2eacc5c04a28cd2380ae17e749b8b6cd5e Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:03:08 -0400 Subject: [PATCH 06/22] change[langgraph]: clean up `Interrupt` interface for v1 (#5405) --- .../cloud/how-tos/add-human-in-the-loop.md | 8 +- docs/docs/concepts/functional_api.md | 56 ++++--- .../add-human-in-the-loop.md | 59 +++---- libs/langgraph/langgraph/errors.py | 27 +++- libs/langgraph/langgraph/pregel/remote.py | 8 +- libs/langgraph/langgraph/types.py | 71 +++++--- .../tests/test_checkpoint_migration.py | 22 ++- libs/langgraph/tests/test_deprecation.py | 21 ++- .../tests/test_interrupt_migration.py | 50 ++++++ libs/langgraph/tests/test_large_cases.py | 59 +++---- libs/langgraph/tests/test_pregel.py | 107 ++++-------- libs/langgraph/tests/test_pregel_async.py | 152 +++++------------- libs/langgraph/tests/test_remote_graph.py | 17 +- libs/prebuilt/langgraph/prebuilt/tool_node.py | 7 +- libs/prebuilt/tests/test_react_agent.py | 4 +- libs/prebuilt/tests/test_tool_node.py | 12 +- libs/sdk-py/langgraph_sdk/schema.py | 10 +- 17 files changed, 320 insertions(+), 370 deletions(-) create mode 100644 libs/langgraph/tests/test_interrupt_migration.py diff --git a/docs/docs/cloud/how-tos/add-human-in-the-loop.md b/docs/docs/cloud/how-tos/add-human-in-the-loop.md index ecdd2004c..7077d01b4 100644 --- a/docs/docs/cloud/how-tos/add-human-in-the-loop.md +++ b/docs/docs/cloud/how-tos/add-human-in-the-loop.md @@ -30,9 +30,7 @@ To review, edit, and approve tool calls in an agent or workflow, use LangGraph's # > [ # > { # > 'value': {'text_to_revise': 'original text'}, - # > 'resumable': True, - # > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'], - # > 'when': 'during' + # > 'id': '...', # > } # > ] @@ -203,9 +201,7 @@ To review, edit, and approve tool calls in an agent or workflow, use LangGraph's # > [ # > { # > 'value': {'text_to_revise': 'original text'}, - # > 'resumable': True, - # > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'], - # > 'when': 'during' + # > 'id': '...', # > } # > ] diff --git a/docs/docs/concepts/functional_api.md b/docs/docs/concepts/functional_api.md index a0c910139..c81511f71 100644 --- a/docs/docs/concepts/functional_api.md +++ b/docs/docs/concepts/functional_api.md @@ -79,51 +79,55 @@ def workflow(topic: str) -> dict: ```python import time import uuid - from langgraph.func import entrypoint, task from langgraph.types import interrupt from langgraph.checkpoint.memory import MemorySaver + @task def write_essay(topic: str) -> str: """Write an essay about the given topic.""" - time.sleep(1) # This is a placeholder for a long-running task. + time.sleep(1) # This is a placeholder for a long-running task. return f"An essay about topic: {topic}" + @entrypoint(checkpointer=MemorySaver()) def workflow(topic: str) -> dict: """A simple workflow that writes an essay and asks for a review.""" essay = write_essay("cat").result() - is_approved = interrupt({ - # Any json-serializable payload provided to interrupt as argument. - # It will be surfaced on the client side as an Interrupt when streaming data - # from the workflow. - "essay": essay, # The essay we want reviewed. - # We can add any additional information that we need. - # For example, introduce a key called "action" with some instructions. - "action": "Please approve/reject the essay", - }) - + is_approved = interrupt( + { + # Any json-serializable payload provided to interrupt as argument. + # It will be surfaced on the client side as an Interrupt when streaming data + # from the workflow. + "essay": essay, # The essay we want reviewed. + # We can add any additional information that we need. + # For example, introduce a key called "action" with some instructions. + "action": "Please approve/reject the essay", + } + ) return { - "essay": essay, # The essay that was generated - "is_approved": is_approved, # Response from HIL + "essay": essay, # The essay that was generated + "is_approved": is_approved, # Response from HIL } + thread_id = str(uuid.uuid4()) - - config = { - "configurable": { - "thread_id": thread_id - } - } - + config = {"configurable": {"thread_id": thread_id}} for item in workflow.stream("cat", config): print(item) - ``` - - ```pycon - {'write_essay': 'An essay about topic: cat'} - {'__interrupt__': (Interrupt(value={'essay': 'An essay about topic: cat', 'action': 'Please approve/reject the essay'}, resumable=True, ns=['workflow:f7b8508b-21c0-8b4c-5958-4e8de74d2684'], when='during'),)} + # > {'write_essay': 'An essay about topic: cat'} + # > { + # > '__interrupt__': ( + # > Interrupt( + # > value={ + # > 'essay': 'An essay about topic: cat', + # > 'action': 'Please approve/reject the essay' + # > }, + # > id='b9b2b9d788f482663ced6dc755c9e981' + # > ), + # > ) + # > } ``` An essay has been written and is ready for review. Once the review is provided, we can resume the workflow: diff --git a/docs/docs/how-tos/human_in_the_loop/add-human-in-the-loop.md b/docs/docs/how-tos/human_in_the_loop/add-human-in-the-loop.md index 01166b008..b1d9c7aea 100644 --- a/docs/docs/how-tos/human_in_the_loop/add-human-in-the-loop.md +++ b/docs/docs/how-tos/human_in_the_loop/add-human-in-the-loop.md @@ -46,13 +46,7 @@ graph = graph_builder.compile(checkpointer=checkpointer) # (4)! config = {"configurable": {"thread_id": "some_id"}} result = graph.invoke({"some_text": "original text"}, config=config) # (5)! print(result['__interrupt__']) # (6)! -# > [ -# > Interrupt( -# > value={'text_to_revise': 'original text'}, -# > resumable=True, -# > ns=['human_node:6ce9e64f-edef-fe5d-f7dc-511fa9526960'] -# > ) -# > ] +# > [Interrupt(value={'text_to_revise': 'original text'}, id='a0d9dd40440ac7be2720dc5c20858627')] # highlight-next-line print(graph.invoke(Command(resume="Edited text"), config=config)) # (7)! @@ -72,25 +66,27 @@ print(graph.invoke(Command(resume="Edited text"), config=config)) # (7)! ```python from typing import TypedDict import uuid - from langgraph.checkpoint.memory import InMemorySaver from langgraph.constants import START from langgraph.graph import StateGraph + # highlight-next-line from langgraph.types import interrupt, Command + class State(TypedDict): some_text: str + def human_node(state: State): # highlight-next-line - value = interrupt( # (1)! + value = interrupt( # (1)! { - "text_to_revise": state["some_text"] # (2)! + "text_to_revise": state["some_text"] # (2)! } ) return { - "some_text": value # (3)! + "some_text": value # (3)! } @@ -98,25 +94,15 @@ print(graph.invoke(Command(resume="Edited text"), config=config)) # (7)! graph_builder = StateGraph(State) graph_builder.add_node("human_node", human_node) graph_builder.add_edge(START, "human_node") - - checkpointer = InMemorySaver() # (4)! - + checkpointer = InMemorySaver() # (4)! graph = graph_builder.compile(checkpointer=checkpointer) - # Pass a thread ID to the graph to run it. config = {"configurable": {"thread_id": uuid.uuid4()}} - # Run the graph until the interrupt is hit. - result = graph.invoke({"some_text": "original text"}, config=config) # (5)! + result = graph.invoke({"some_text": "original text"}, config=config) # (5)! - print(result['__interrupt__']) # (6)! - # > [ - # > Interrupt( - # > value={'text_to_revise': 'original text'}, - # > resumable=True, - # > ns=['human_node:6ce9e64f-edef-fe5d-f7dc-511fa9526960'] - # > ) - # > ] + print(result["__interrupt__"]) # (6)! + # > [Interrupt(value={'text_to_revise': 'original text'}, id='6d7c4048049254c83195429a3659661d')] # highlight-next-line print(graph.invoke(Command(resume="Edited text"), config=config)) # (7)! @@ -164,7 +150,7 @@ For example, once your graph has been interrupted (multiple times, theoretically ```python resume_map = { - i.interrupt_id: f"human input for prompt {i.value}" + i.id: f"human input for prompt {i.value}" for i in parent.get_state(thread_config).interrupts } @@ -385,14 +371,15 @@ graph.invoke( # Output interrupt payload print(result["__interrupt__"]) # Example output: - # Interrupt( - # value={ - # 'task': 'Please review and edit the generated summary if necessary.', - # 'generated_summary': 'The cat sat on the mat and looked at the stars.' - # }, - # resumable=True, - # ... - # ) + # > [ + # > Interrupt( + # > value={ + # > 'task': 'Please review and edit the generated summary if necessary.', + # > 'generated_summary': 'The cat sat on the mat and looked at the stars.' + # > }, + # > id='...' + # > ) + # > ] # Resume the graph with human-edited input edited_summary = "The cat lay on the rug, gazing peacefully at the night sky." @@ -873,7 +860,7 @@ def node_in_parent_graph(state: State): Entered `parent_node` a total of 1 times Entered `node_in_subgraph` a total of 1 times Entered human_node in sub-graph a total of 1 times - {'__interrupt__': (Interrupt(value='what is your name?', resumable=True, ns=['parent_node:4c3a0248-21f0-1287-eacf-3002bc304db4', 'human_node:2fe86d52-6f70-2a3f-6b2f-b1eededd6348'], when='during'),)} + {'__interrupt__': (Interrupt(value='what is your name?', id='...'),)} --- Resuming --- Entered `parent_node` a total of 2 times Entered human_node in sub-graph a total of 2 times @@ -949,7 +936,7 @@ To avoid issues, refrain from dynamically changing the node's structure between ``` ```pycon - {'__interrupt__': (Interrupt(value='what is your name?', resumable=True, ns=['human_node:3a007ef9-c30d-c357-1ec1-86a1a70d8fba'], when='during'),)} + {'__interrupt__': (Interrupt(value='what is your name?', id='...'),)} Name: N/A. Age: John {'human_node': {'age': 'John', 'name': 'N/A'}} ``` diff --git a/libs/langgraph/langgraph/errors.py b/libs/langgraph/langgraph/errors.py index e1121c1bf..841abbaf7 100644 --- a/libs/langgraph/langgraph/errors.py +++ b/libs/langgraph/langgraph/errors.py @@ -1,10 +1,16 @@ +from __future__ import annotations + from collections.abc import Sequence from enum import Enum from typing import Any +from warnings import warn + +from typing_extensions import deprecated # EmptyChannelError is re-exported from langgraph.channels.base from langgraph.checkpoint.base import EmptyChannelError # noqa: F401 from langgraph.types import Command, Interrupt +from langgraph.warnings import LangGraphDeprecatedSinceV10 __all__ = ( "EmptyChannelError", @@ -83,11 +89,26 @@ class GraphInterrupt(GraphBubbleUp): super().__init__(interrupts) +@deprecated( + "NodeInterrupt is deprecated. Please use `langgraph.types.interrupt` instead.", + stacklevel=2, +) class NodeInterrupt(GraphInterrupt): - """Raised by a node to interrupt execution.""" + """Raised by a node to interrupt execution. - def __init__(self, value: Any) -> None: - super().__init__([Interrupt(value=value)]) + Deprecated in V1.0.0 in favor of [`interrupt`][langgraph.types.interrupt]. + """ + + def __init__(self, value: Any, id: str | None = None) -> None: + warn( + "NodeInterrupt is deprecated. Please use `langgraph.types.interrupt` instead.", + LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + if id is None: + super().__init__([Interrupt(value=value)]) + else: + super().__init__([Interrupt(value=value, id=id)]) class ParentCommand(GraphBubbleUp): diff --git a/libs/langgraph/langgraph/pregel/remote.py b/libs/langgraph/langgraph/pregel/remote.py index 145dd548f..c5e0e8dcd 100644 --- a/libs/langgraph/langgraph/pregel/remote.py +++ b/libs/langgraph/langgraph/pregel/remote.py @@ -260,9 +260,9 @@ class RemoteGraph(PregelProtocol): def _create_state_snapshot(self, state: ThreadState) -> StateSnapshot: tasks: list[PregelTask] = [] for task in state["tasks"]: - interrupts = [] - for interrupt in task["interrupts"]: - interrupts.append(Interrupt(**interrupt)) + interrupts = tuple( + Interrupt(**interrupt) for interrupt in task["interrupts"] + ) tasks.append( PregelTask( @@ -270,7 +270,7 @@ class RemoteGraph(PregelProtocol): name=task["name"], path=tuple(), error=Exception(task["error"]) if task["error"] else None, - interrupts=tuple(interrupts), + interrupts=interrupts, state=( self._create_state_snapshot(task["state"]) if task["state"] diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 80ac19176..11016c536 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -14,16 +14,20 @@ from typing import ( NamedTuple, TypeVar, Union, - cast, + final, ) +from warnings import warn from langchain_core.runnables import Runnable, RunnableConfig +from typing_extensions import Unpack, deprecated from xxhash import xxh3_128_hexdigest from langgraph._internal._cache import default_cache_key from langgraph._internal._fields import get_cached_annotated_keys, get_update_as_tuples from langgraph._internal._retry import default_retry_on +from langgraph._internal._typing import UNSET, DeprecatedKwargs from langgraph.checkpoint.base import BaseCheckpointSaver, CheckpointMetadata +from langgraph.warnings import LangGraphDeprecatedSinceV10 if TYPE_CHECKING: from langgraph.pregel.protocol import PregelProtocol @@ -86,8 +90,10 @@ Always injected into nodes if requested as a keyword argument, but it's a no-op when not using stream_mode="custom".""" if sys.version_info >= (3, 10): + _DC_SLOTS = {"slots": True} _DC_KWARGS = {"kw_only": True, "slots": True, "frozen": True} else: + _DC_SLOTS = {} _DC_KWARGS = {"frozen": True} @@ -128,7 +134,11 @@ class CachePolicy(Generic[KeyFuncT]): """Time to live for the cache entry in seconds. If None, the entry never expires.""" -@dataclasses.dataclass(**_DC_KWARGS) +_DEFAULT_INTERRUPT_ID = "placeholder-id" + + +@final +@dataclasses.dataclass(init=False, **_DC_SLOTS) class Interrupt: """Information about an interrupt that occurred in a node. @@ -136,16 +146,41 @@ class Interrupt: """ value: Any - resumable: bool = False - ns: Sequence[str] | None = None - when: Literal["during"] = dataclasses.field(default="during", repr=False) + id: str + + def __init__( + self, + value: Any, + id: str = _DEFAULT_INTERRUPT_ID, + **deprecated_kwargs: Unpack[DeprecatedKwargs], + ) -> None: + self.value = value + + if ( + (ns := deprecated_kwargs.get("ns", UNSET)) is not UNSET + and (id == _DEFAULT_INTERRUPT_ID) + and (isinstance(ns, Sequence)) + ): + self.id = xxh3_128_hexdigest("|".join(ns).encode()) + else: + self.id = id + + @classmethod + def from_ns(cls, value: Any, ns: str) -> Interrupt: + return cls(value=value, id=xxh3_128_hexdigest(ns.encode())) @property + @deprecated( + "`interrupt_id` is deprecated. Use `id` instead.", + stacklevel=2, + ) def interrupt_id(self) -> str: - """Generate a unique ID for the interrupt based on its namespace.""" - if self.ns is None: - return "placeholder-id" - return xxh3_128_hexdigest("|".join(self.ns).encode()) + warn( + "`interrupt_id` is deprecated. Use `id` instead.", + LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + return self.id class StateUpdate(NamedTuple): @@ -419,22 +454,16 @@ def interrupt(value: Any) -> Any: for chunk in graph.stream({\"foo\": \"abc\"}, config): print(chunk) - ``` - ```pycon - {'__interrupt__': (Interrupt(value='what is your age?', resumable=True, ns=['node:62e598fa-8653-9d6d-2046-a70203020e37'], when='during'),)} - ``` + # > {'__interrupt__': (Interrupt(value='what is your age?', id='45fda8478b2ef754419799e10992af06'),)} - ```python command = Command(resume=\"some input from a human!!!\") for chunk in graph.stream(Command(resume=\"some input from a human!!!\"), config): print(chunk) - ``` - ```pycon - Received an input from the interrupt: some input from a human!!! - {'node': {'human_value': 'some input from a human!!!'}} + # > Received an input from the interrupt: some input from a human!!! + # > {'node': {'human_value': 'some input from a human!!!'}} ``` Args: @@ -451,7 +480,6 @@ def interrupt(value: Any) -> Any: CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_SEND, - NS_SEP, RESUME, ) from langgraph.errors import GraphInterrupt @@ -474,10 +502,9 @@ def interrupt(value: Any) -> Any: # no resume value found raise GraphInterrupt( ( - Interrupt( + Interrupt.from_ns( value=value, - resumable=True, - ns=cast(str, conf[CONFIG_KEY_CHECKPOINT_NS]).split(NS_SEP), + ns=conf[CONFIG_KEY_CHECKPOINT_NS], ), ) ) diff --git a/libs/langgraph/tests/test_checkpoint_migration.py b/libs/langgraph/tests/test_checkpoint_migration.py index d1dd65e61..9fa6d7421 100644 --- a/libs/langgraph/tests/test_checkpoint_migration.py +++ b/libs/langgraph/tests/test_checkpoint_migration.py @@ -92,8 +92,7 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]: else ( Interrupt( value="", - resumable=True, - ns=[AnyStr("qa:")], + id=AnyStr(), ), ), state=None, @@ -107,8 +106,7 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]: else ( Interrupt( value="", - resumable=True, - ns=[AnyStr("qa:")], + id=AnyStr(), ), ), ), @@ -412,8 +410,8 @@ SAVED_CHECKPOINTS = { [ Interrupt( value="", - resumable=True, - ns=["qa:2430f303-da9f-2e3e-738c-2e8ea28e8973"], + resumable=True, # type: ignore[arg-type] + ns=["qa:2430f303-da9f-2e3e-738c-2e8ea28e8973"], # type: ignore[arg-type] ) ], ), @@ -786,8 +784,8 @@ SAVED_CHECKPOINTS = { [ Interrupt( value="", - resumable=True, - ns=["qa:4ee8637e-0a95-285e-75bc-4da721c0beab"], + resumable=True, # type: ignore[arg-type] + ns=["qa:4ee8637e-0a95-285e-75bc-4da721c0beab"], # type: ignore[arg-type] ) ], ), @@ -1173,7 +1171,7 @@ SAVED_CHECKPOINTS = { Interrupt( value="", resumable=True, - ns=["qa:369e94b1-77d1-d67a-ab59-23d1ba20ee73"], + ns=["qa:369e94b1-77d1-d67a-ab59-23d1ba20ee73"], # type: ignore[arg-type] ) ], ), @@ -1525,8 +1523,7 @@ def test_latest_checkpoint_state_graph( "__interrupt__": ( Interrupt( value="", - resumable=True, - ns=[AnyStr("qa:")], + id=AnyStr(), ), ) }, @@ -1570,8 +1567,7 @@ async def test_latest_checkpoint_state_graph_async( "__interrupt__": ( Interrupt( value="", - resumable=True, - ns=[AnyStr("qa:")], + id=AnyStr(), ), ) }, diff --git a/libs/langgraph/tests/test_deprecation.py b/libs/langgraph/tests/test_deprecation.py index 0edb00b6f..bd592f922 100644 --- a/libs/langgraph/tests/test_deprecation.py +++ b/libs/langgraph/tests/test_deprecation.py @@ -1,9 +1,10 @@ import pytest from typing_extensions import TypedDict +from langgraph.errors import NodeInterrupt from langgraph.func import entrypoint, task from langgraph.graph import StateGraph -from langgraph.types import RetryPolicy +from langgraph.types import Interrupt, RetryPolicy from langgraph.warnings import LangGraphDeprecatedSinceV05, LangGraphDeprecatedSinceV10 @@ -88,3 +89,21 @@ def test_pregel_deprecation() -> None: match="Importing from langgraph.pregel.types is deprecated. Please use 'from langgraph.types import ...' instead.", ): from langgraph.pregel.types import StateSnapshot # noqa: F401 + + +def test_interrupt_attributes_deprecation() -> None: + interrupt = Interrupt(value="question", id="abc") + + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="`interrupt_id` is deprecated. Use `id` instead.", + ): + interrupt.interrupt_id + + +def test_node_interrupt_deprecation() -> None: + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="NodeInterrupt is deprecated. Please use `langgraph.types.interrupt` instead.", + ): + NodeInterrupt(value="test") diff --git a/libs/langgraph/tests/test_interrupt_migration.py b/libs/langgraph/tests/test_interrupt_migration.py new file mode 100644 index 000000000..8149e0f85 --- /dev/null +++ b/libs/langgraph/tests/test_interrupt_migration.py @@ -0,0 +1,50 @@ +import warnings + +import pytest + +from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer +from langgraph.types import Interrupt +from langgraph.warnings import LangGraphDeprecatedSinceV10 + + +@pytest.mark.filterwarnings("ignore:LangGraphDeprecatedSinceV10") +def test_interrupt_legacy_ns() -> None: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=LangGraphDeprecatedSinceV10) + + old_interrupt = Interrupt( + value="abc", resumable=True, when="during", ns=["a:b", "c:d"] + ) + + new_interrupt = Interrupt.from_ns(value="abc", ns="a:b|c:d") + assert new_interrupt.value == old_interrupt.value + assert new_interrupt.id == old_interrupt.id + + +serializer = JsonPlusSerializer() + + +def test_serialization_roundtrip() -> None: + """Test that the legacy interrupt (pre v1) can be reserialized as the modern interrupt without id corruption.""" + + # generated with: + # JsonPlusSerializer().dumps(Interrupt(value="legacy_test", ns=["legacy_test"], resumable=True, when="during")) + legacy_interrupt_bytes = b'{"lc": 2, "type": "constructor", "id": ["langgraph", "types", "Interrupt"], "kwargs": {"value": "legacy_test", "resumable": true, "ns": ["legacy_test"], "when": "during"}}' + legacy_interrupt_id = "f1fa625689ec006a5b32b76863e22a6c" + + interrupt = serializer.loads(legacy_interrupt_bytes) + assert interrupt.id == legacy_interrupt_id + assert interrupt.value == "legacy_test" + + +def test_serialization_roundtrip_complex_ns() -> None: + """Test that the legacy interrupt (pre v1), with a more complex ns can be reserialized as the modern interrupt without id corruption.""" + + # generated with: + # JsonPlusSerializer().dumps(Interrupt(value="legacy_test", ns=["legacy:test", "with:complex", "name:space"], resumable=True, when="during")) + legacy_interrupt_bytes = b'{"lc": 2, "type": "constructor", "id": ["langgraph", "types", "Interrupt"], "kwargs": {"value": "legacy_test", "resumable": true, "ns": ["legacy:test", "with:complex", "name:space"], "when": "during"}}' + legacy_interrupt_id = "e69356a9ee3630ee7f4f597f2693000c" + + interrupt = serializer.loads(legacy_interrupt_bytes) + assert interrupt.id == legacy_interrupt_id + assert interrupt.value == "legacy_test" diff --git a/libs/langgraph/tests/test_large_cases.py b/libs/langgraph/tests/test_large_cases.py index e642d07da..419686255 100644 --- a/libs/langgraph/tests/test_large_cases.py +++ b/libs/langgraph/tests/test_large_cases.py @@ -16,7 +16,6 @@ from langgraph.channels.untracked_value import UntrackedValue from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.checkpoint.memory import InMemorySaver from langgraph.constants import END, PULL, PUSH, START -from langgraph.errors import NodeInterrupt from langgraph.graph import StateGraph from langgraph.graph.message import MessageGraph, MessagesState, add_messages from langgraph.prebuilt.chat_agent_executor import create_react_agent @@ -4173,9 +4172,7 @@ def test_dynamic_interrupt(sync_checkpointer: BaseCheckpointSaver) -> None: ) == { "my_key": "value", "market": "DE", - "__interrupt__": [ - Interrupt(value="Just because...", resumable=True, ns=[AnyStr("tool_two:")]) - ], + "__interrupt__": [Interrupt(value="Just because...", id=AnyStr())], } assert tool_two_node_count == 1, "interrupts aren't retried" assert len(tracer.runs) == 1 @@ -4205,8 +4202,7 @@ def test_dynamic_interrupt(sync_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ) }, @@ -4224,9 +4220,7 @@ def test_dynamic_interrupt(sync_checkpointer: BaseCheckpointSaver) -> None: ) == { "my_key": "value ⛰️", "market": "DE", - "__interrupt__": [ - Interrupt(value="Just because...", resumable=True, ns=[AnyStr("tool_two:")]) - ], + "__interrupt__": [Interrupt(value="Just because...", id=AnyStr())], } assert [c.metadata for c in tool_two.checkpointer.list(thread1)] == [ @@ -4248,8 +4242,7 @@ def test_dynamic_interrupt(sync_checkpointer: BaseCheckpointSaver) -> None: interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ), @@ -4271,8 +4264,7 @@ def test_dynamic_interrupt(sync_checkpointer: BaseCheckpointSaver) -> None: interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ) @@ -4336,9 +4328,7 @@ def test_partial_pending_checkpoint(sync_checkpointer: BaseCheckpointSaver) -> N ) == { "my_key": "value one", "market": "DE", - "__interrupt__": [ - Interrupt(value="Just because...", resumable=True, ns=[AnyStr("tool_two:")]) - ], + "__interrupt__": [Interrupt(value="Just because...", id=AnyStr())], } assert tool_two_node_count == 1, "interrupts aren't retried" assert len(tracer.runs) == 1 @@ -4371,8 +4361,7 @@ def test_partial_pending_checkpoint(sync_checkpointer: BaseCheckpointSaver) -> N "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ) }, @@ -4391,9 +4380,7 @@ def test_partial_pending_checkpoint(sync_checkpointer: BaseCheckpointSaver) -> N ) == { "my_key": "value ⛰️ one", "market": "DE", - "__interrupt__": [ - Interrupt(value="Just because...", resumable=True, ns=[AnyStr("tool_two:")]) - ], + "__interrupt__": [Interrupt(value="Just because...", id=AnyStr())], } assert [c.metadata for c in tool_two.checkpointer.list(thread1)] == [ { @@ -4420,8 +4407,7 @@ def test_partial_pending_checkpoint(sync_checkpointer: BaseCheckpointSaver) -> N interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ), @@ -4443,8 +4429,7 @@ def test_partial_pending_checkpoint(sync_checkpointer: BaseCheckpointSaver) -> N interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ) @@ -4513,8 +4498,7 @@ def test_dynamic_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> N "__interrupt__": [ Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ) ], } @@ -4546,8 +4530,7 @@ def test_dynamic_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> N "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ), ) }, @@ -4568,8 +4551,7 @@ def test_dynamic_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> N "__interrupt__": [ Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ) ], } @@ -4598,8 +4580,7 @@ def test_dynamic_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> N interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ), ), state={ @@ -4627,8 +4608,7 @@ def test_dynamic_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> N interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ), ), ) @@ -4672,7 +4652,7 @@ def test_send_dedupe_on_resume( def __call__(self, state): self.ticks += 1 if self.ticks == 1: - raise NodeInterrupt("Bahh") + interrupt("Bahh") return ["|".join(("flaky", str(state)))] class Node: @@ -4722,8 +4702,7 @@ def test_send_dedupe_on_resume( "__interrupt__": [ Interrupt( value="Bahh", - resumable=False, - ns=None, + id=AnyStr(), ), ], } @@ -4884,7 +4863,7 @@ def test_send_dedupe_on_resume( name="flaky", path=("__pregel_push", 1, False), error=None, - interrupts=(Interrupt(value="Bahh", resumable=False, ns=None),), + interrupts=(Interrupt(value="Bahh", id=AnyStr()),), state=None, result=["flaky|4"] if checkpoint_during else None, ), @@ -4898,7 +4877,7 @@ def test_send_dedupe_on_resume( result=["3"], ), ), - interrupts=(Interrupt(value="Bahh", resumable=False, ns=None),), + interrupts=(Interrupt(value="Bahh", id=AnyStr()),), ), StateSnapshot( values=["0", "1"], diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index c82bec7ba..f89c727ae 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -1280,9 +1280,7 @@ def test_imp_task( "__interrupt__": ( Interrupt( value="question", - resumable=True, - ns=[AnyStr("graph:")], - when="during", + id=AnyStr(), ), ) }, @@ -1349,9 +1347,7 @@ def test_imp_nested( "__interrupt__": ( Interrupt( value="question", - resumable=True, - ns=[AnyStr("graph:")], - when="during", + id=AnyStr(), ), ) }, @@ -3371,8 +3367,7 @@ def test_subgraph_checkpoint_true_interrupt( "__interrupt__": [ Interrupt( value="Provide baz value", - resumable=True, - ns=[AnyStr("node_2"), AnyStr("subgraph_node_1:")], + id=AnyStr(), ) ], } @@ -4900,9 +4895,7 @@ def test_interrupt_multiple(sync_checkpointer: BaseCheckpointSaver): "__interrupt__": ( Interrupt( value={"value": 1}, - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -4918,9 +4911,7 @@ def test_interrupt_multiple(sync_checkpointer: BaseCheckpointSaver): "__interrupt__": ( Interrupt( value={"value": 2}, - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -4968,9 +4959,7 @@ def test_interrupt_loop(sync_checkpointer: BaseCheckpointSaver): "__interrupt__": ( Interrupt( value="How old are you?", - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -4987,9 +4976,7 @@ def test_interrupt_loop(sync_checkpointer: BaseCheckpointSaver): "__interrupt__": ( Interrupt( value="invalid response", - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -5006,9 +4993,7 @@ def test_interrupt_loop(sync_checkpointer: BaseCheckpointSaver): "__interrupt__": ( Interrupt( value="invalid response", - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -5044,8 +5029,7 @@ def test_interrupt_functional( "__interrupt__": [ Interrupt( value="Provide value for bar:", - resumable=True, - ns=[AnyStr("graph:")], + id=AnyStr(), ) ] } @@ -5078,8 +5062,7 @@ def test_interrupt_task_functional( "__interrupt__": [ Interrupt( value="Provide value for bar:", - resumable=True, - ns=[AnyStr("graph:"), AnyStr("bar:")], + id=AnyStr(), ), ] } @@ -5102,8 +5085,7 @@ def test_interrupt_task_functional( "__interrupt__": [ Interrupt( value="Provide value for bar:", - resumable=True, - ns=[AnyStr("graph:"), AnyStr("bar:")], + id=AnyStr(), ), ] } @@ -5628,12 +5610,8 @@ def test_falsy_return_from_task(sync_checkpointer: BaseCheckpointSaver): "id": AnyStr(), "interrupts": [ { - "ns": [ - AnyStr(), - ], - "resumable": True, + "id": AnyStr(), "value": "test", - "when": "during", }, ], "name": "graph", @@ -5676,12 +5654,8 @@ def test_falsy_return_from_task(sync_checkpointer: BaseCheckpointSaver): "id": AnyStr(), "interrupts": ( { - "ns": [ - AnyStr(), - ], - "resumable": True, + "id": AnyStr(), "value": "test", - "when": "during", }, ), "name": "graph", @@ -5901,9 +5875,7 @@ def test_double_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> No "__interrupt__": ( Interrupt( value="interrupt node 1", - resumable=True, - ns=[AnyStr("node_1:")], - when="during", + id=AnyStr(), ), ) }, @@ -5917,9 +5889,7 @@ def test_double_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> No "__interrupt__": ( Interrupt( value="interrupt node 2", - resumable=True, - ns=[AnyStr("node_2:")], - when="during", + id=AnyStr(), ), ) }, @@ -5950,9 +5920,7 @@ def test_double_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> No "__interrupt__": ( Interrupt( value="interrupt node 1", - resumable=True, - ns=[AnyStr("invoke_sub_agent:"), AnyStr("node_1:")], - when="during", + id=AnyStr(), ), ) }, @@ -5964,9 +5932,7 @@ def test_double_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> No "__interrupt__": ( Interrupt( value="interrupt node 2", - resumable=True, - ns=[AnyStr("invoke_sub_agent:"), AnyStr("node_2:")], - when="during", + id=AnyStr(), ), ) } @@ -6045,7 +6011,7 @@ def test_multi_resume(sync_checkpointer: BaseCheckpointSaver) -> None: assert interrupt_values == set(prompts) resume_map: dict[str, str] = { - i.interrupt_id: f"human input for prompt {i.value}" + i.id: f"human input for prompt {i.value}" for i in parent_graph.get_state(thread_config).interrupts } @@ -7123,8 +7089,7 @@ def test_interrupt_subgraph_reenter_checkpointer_true( "__interrupt__": [ Interrupt( value="Provide value", - resumable=True, - ns=[AnyStr("call_subgraph"), AnyStr("subnode_2:")], + id=AnyStr(), ) ], } @@ -7134,8 +7099,7 @@ def test_interrupt_subgraph_reenter_checkpointer_true( "__interrupt__": [ Interrupt( value="Provide value", - resumable=True, - ns=[AnyStr("call_subgraph"), AnyStr("subnode_2:")], + id=AnyStr(), ) ], } @@ -7165,8 +7129,7 @@ def test_interrupt_subgraph_reenter_checkpointer_true( "__interrupt__": [ Interrupt( value="Provide value", - resumable=True, - ns=[AnyStr("call_subgraph"), AnyStr("subnode_2:")], + id=AnyStr(), ) ], } @@ -7312,7 +7275,7 @@ def test_parallel_interrupts(sync_checkpointer: BaseCheckpointSaver) -> None: # assume that it breaks here, because it is an interrupt # get human input and resume - if any(i.resumable for i in current_interrupts): + if len(current_interrupts) > 0: current_input = Command(resume=f"Resume #{invokes}") # not more human input required, must be completed @@ -7329,11 +7292,7 @@ def test_parallel_interrupts(sync_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="a", - resumable=True, - ns=[ - AnyStr("child_graph:"), - AnyStr("get_human_input:"), - ], + id=AnyStr(), ), ) }, @@ -7341,11 +7300,7 @@ def test_parallel_interrupts(sync_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="b", - resumable=True, - ns=[ - AnyStr("child_graph:"), - AnyStr("get_human_input:"), - ], + id=AnyStr(), ), ) }, @@ -7356,11 +7311,7 @@ def test_parallel_interrupts(sync_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="a", - resumable=True, - ns=[ - AnyStr("child_graph:"), - AnyStr("get_human_input:"), - ], + id=AnyStr(), ), ) }, @@ -7371,11 +7322,7 @@ def test_parallel_interrupts(sync_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="b", - resumable=True, - ns=[ - AnyStr("child_graph:"), - AnyStr("get_human_input:"), - ], + id=AnyStr(), ), ) }, @@ -7489,7 +7436,7 @@ def test_parallel_interrupts_double(sync_checkpointer: BaseCheckpointSaver) -> N # assume that it breaks here, because it is an interrupt # get human input and resume - if any(i.resumable for i in current_interrupts): + if len(current_interrupts) > 0: current_input = Command(resume=f"Resume #{invokes}") # not more human input required, must be completed diff --git a/libs/langgraph/tests/test_pregel_async.py b/libs/langgraph/tests/test_pregel_async.py index dc7a9c3d0..b2e99af23 100644 --- a/libs/langgraph/tests/test_pregel_async.py +++ b/libs/langgraph/tests/test_pregel_async.py @@ -45,7 +45,6 @@ from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START from langgraph.errors import ( GraphRecursionError, InvalidUpdateError, - NodeInterrupt, ParentCommand, ) from langgraph.func import entrypoint, task @@ -592,9 +591,7 @@ async def test_dynamic_interrupt(async_checkpointer: BaseCheckpointSaver) -> Non ) == { "my_key": "value", "market": "DE", - "__interrupt__": [ - Interrupt(value="Just because...", resumable=True, ns=[AnyStr("tool_two:")]) - ], + "__interrupt__": [Interrupt(value="Just because...", id=AnyStr())], } assert tool_two_node_count == 1, "interrupts aren't retried" assert len(tracer.runs) == 1 @@ -625,8 +622,7 @@ async def test_dynamic_interrupt(async_checkpointer: BaseCheckpointSaver) -> Non "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ) }, @@ -651,8 +647,7 @@ async def test_dynamic_interrupt(async_checkpointer: BaseCheckpointSaver) -> Non "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ) }, @@ -676,8 +671,7 @@ async def test_dynamic_interrupt(async_checkpointer: BaseCheckpointSaver) -> Non interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ), @@ -693,8 +687,7 @@ async def test_dynamic_interrupt(async_checkpointer: BaseCheckpointSaver) -> Non interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ) @@ -762,8 +755,7 @@ async def test_dynamic_interrupt_subgraph( "__interrupt__": [ Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ) ], } @@ -796,8 +788,7 @@ async def test_dynamic_interrupt_subgraph( "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ), ) }, @@ -823,8 +814,7 @@ async def test_dynamic_interrupt_subgraph( "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ), ) }, @@ -848,8 +838,7 @@ async def test_dynamic_interrupt_subgraph( interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ), ), state={ @@ -871,8 +860,7 @@ async def test_dynamic_interrupt_subgraph( interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:"), AnyStr("do:")], + id=AnyStr(), ), ), ) @@ -938,9 +926,7 @@ async def test_partial_pending_checkpoint( ) == { "my_key": "value one", "market": "DE", - "__interrupt__": [ - Interrupt(value="Just because...", resumable=True, ns=[AnyStr("tool_two:")]) - ], + "__interrupt__": [Interrupt(value="Just because...", id=AnyStr())], } assert tool_two_node_count == 1, "interrupts aren't retried" assert len(tracer.runs) == 1 @@ -971,8 +957,7 @@ async def test_partial_pending_checkpoint( "__interrupt__": ( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ) }, @@ -1002,8 +987,7 @@ async def test_partial_pending_checkpoint( "__interrupt__": [ Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ) ], } @@ -1037,8 +1021,7 @@ async def test_partial_pending_checkpoint( interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ), @@ -1054,8 +1037,7 @@ async def test_partial_pending_checkpoint( interrupts=( Interrupt( value="Just because...", - resumable=True, - ns=[AnyStr("tool_two:")], + id=AnyStr(), ), ), ) @@ -1119,8 +1101,7 @@ async def test_node_not_cancelled_on_other_node_interrupted( "__interrupt__": [ Interrupt( value="I am bad", - resumable=True, - ns=[AnyStr("bad:")], + id=AnyStr(), ) ], } @@ -1133,8 +1114,7 @@ async def test_node_not_cancelled_on_other_node_interrupted( "__interrupt__": [ Interrupt( value="I am bad", - resumable=True, - ns=[AnyStr("bad:")], + id=AnyStr(), ) ], } @@ -2292,9 +2272,7 @@ async def test_imp_task( "__interrupt__": ( Interrupt( value="question", - resumable=True, - ns=[AnyStr("graph:")], - when="during", + id=AnyStr(), ), ) }, @@ -2373,9 +2351,7 @@ async def test_imp_nested( "__interrupt__": ( Interrupt( value="question", - resumable=True, - ns=[AnyStr("graph:")], - when="during", + id=AnyStr(), ), ) }, @@ -2428,9 +2404,7 @@ async def test_imp_task_cancel( "__interrupt__": ( Interrupt( value="question", - resumable=True, - ns=[AnyStr("graph:")], - when="during", + id=AnyStr(), ), ) }, @@ -2522,6 +2496,10 @@ async def test_imp_stream_order( ] +@pytest.mark.skipif( + sys.version_info < (3, 11), + reason="Requires Python 3.11 or higher for context management", +) async def test_send_dedupe_on_resume( async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool ) -> None: @@ -2531,7 +2509,7 @@ async def test_send_dedupe_on_resume( def __call__(self, state): self.ticks += 1 if self.ticks == 1: - raise NodeInterrupt("Bahh") + interrupt("Bahh") return ["|".join(("flaky", str(state)))] class Node: @@ -2578,8 +2556,7 @@ async def test_send_dedupe_on_resume( "__interrupt__": [ Interrupt( value="Bahh", - resumable=False, - ns=None, + id=AnyStr(), ), ], } @@ -2730,7 +2707,7 @@ async def test_send_dedupe_on_resume( name="flaky", path=("__pregel_push", 1, False), error=None, - interrupts=(Interrupt(value="Bahh", resumable=False, ns=None),), + interrupts=(Interrupt(value="Bahh", id=AnyStr()),), state=None, result=["flaky|4"] if checkpoint_during else None, ), @@ -2744,7 +2721,7 @@ async def test_send_dedupe_on_resume( result=["3"], ), ), - interrupts=(Interrupt(value="Bahh", resumable=False, ns=None),), + interrupts=(Interrupt(value="Bahh", id=AnyStr()),), ), StateSnapshot( values=["0", "1"], @@ -5143,8 +5120,7 @@ async def test_subgraph_checkpoint_true_interrupt( "__interrupt__": [ Interrupt( value="Provide baz value", - resumable=True, - ns=[AnyStr("node_2"), AnyStr("subgraph_node_1:")], + id=AnyStr(), ) ], } @@ -6220,9 +6196,7 @@ async def test_interrupt_multiple(async_checkpointer: BaseCheckpointSaver): "__interrupt__": ( Interrupt( value={"value": 1}, - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -6240,9 +6214,7 @@ async def test_interrupt_multiple(async_checkpointer: BaseCheckpointSaver): "__interrupt__": ( Interrupt( value={"value": 2}, - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -6290,9 +6262,7 @@ async def test_interrupt_loop(async_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="How old are you?", - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -6309,9 +6279,7 @@ async def test_interrupt_loop(async_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="invalid response", - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -6328,9 +6296,7 @@ async def test_interrupt_loop(async_checkpointer: BaseCheckpointSaver) -> None: "__interrupt__": ( Interrupt( value="invalid response", - resumable=True, - ns=[AnyStr("node:")], - when="during", + id=AnyStr(), ), ) } @@ -6392,8 +6358,7 @@ async def test_interrupt_task_functional( "__interrupt__": [ Interrupt( value="Provide value for bar:", - resumable=True, - ns=[AnyStr("graph:"), AnyStr("bar:")], + id=AnyStr(), ), ] } @@ -6914,9 +6879,7 @@ async def test_double_interrupt_subgraph( "__interrupt__": ( Interrupt( value="interrupt node 1", - resumable=True, - ns=[AnyStr("node_1:")], - when="during", + id=AnyStr(), ), ) }, @@ -6930,9 +6893,7 @@ async def test_double_interrupt_subgraph( "__interrupt__": ( Interrupt( value="interrupt node 2", - resumable=True, - ns=[AnyStr("node_2:")], - when="during", + id=AnyStr(), ), ) }, @@ -6964,9 +6925,7 @@ async def test_double_interrupt_subgraph( "__interrupt__": ( Interrupt( value="interrupt node 1", - resumable=True, - ns=[AnyStr("invoke_sub_agent:"), AnyStr("node_1:")], - when="during", + id=AnyStr(), ), ) }, @@ -6978,9 +6937,7 @@ async def test_double_interrupt_subgraph( "__interrupt__": ( Interrupt( value="interrupt node 2", - resumable=True, - ns=[AnyStr("invoke_sub_agent:"), AnyStr("node_2:")], - when="during", + id=AnyStr(), ), ) } @@ -7843,8 +7800,7 @@ async def test_interrupt_subgraph_reenter_checkpointer_true( "__interrupt__": [ Interrupt( value="Provide value", - resumable=True, - ns=[AnyStr("call_subgraph"), AnyStr("subnode_2:")], + id=AnyStr(), ) ], } @@ -7854,8 +7810,7 @@ async def test_interrupt_subgraph_reenter_checkpointer_true( "__interrupt__": [ Interrupt( value="Provide value", - resumable=True, - ns=[AnyStr("call_subgraph"), AnyStr("subnode_2")], + id=AnyStr(), ) ], } @@ -7885,8 +7840,7 @@ async def test_interrupt_subgraph_reenter_checkpointer_true( "__interrupt__": [ Interrupt( value="Provide value", - resumable=True, - ns=[AnyStr("call_subgraph"), AnyStr("subnode_2:")], + id=AnyStr(), ) ], } @@ -7923,8 +7877,7 @@ async def test_handles_multiple_interrupts_from_tasks( "__interrupt__": [ Interrupt( value="Hey do you want to add James?", - resumable=True, - ns=[AnyStr("program:"), AnyStr("add_participant:")], + id=AnyStr(), ), ] } @@ -7932,10 +7885,6 @@ async def test_handles_multiple_interrupts_from_tasks( state = await program.aget_state(config=config) assert len(state.tasks[0].interrupts) == 1 task_interrupt = state.tasks[0].interrupts[0] - assert task_interrupt.resumable is True - assert len(task_interrupt.ns) == 2 - assert task_interrupt.ns[0].startswith("program:") - assert task_interrupt.ns[1].startswith("add_participant:") assert task_interrupt.value == "Hey do you want to add James?" result = await program.ainvoke(Command(resume=True), config=config) @@ -7943,8 +7892,7 @@ async def test_handles_multiple_interrupts_from_tasks( "__interrupt__": [ Interrupt( value="Hey do you want to add Will?", - resumable=True, - ns=[AnyStr("program:"), AnyStr("add_participant:")], + id=AnyStr(), ), ] } @@ -7952,10 +7900,6 @@ async def test_handles_multiple_interrupts_from_tasks( state = await program.aget_state(config=config) assert len(state.tasks[0].interrupts) == 1 task_interrupt = state.tasks[0].interrupts[0] - assert task_interrupt.resumable is True - assert len(task_interrupt.ns) == 2 - assert task_interrupt.ns[0].startswith("program:") - assert task_interrupt.ns[1].startswith("add_participant:") assert task_interrupt.value == "Hey do you want to add Will?" result = await program.ainvoke(Command(resume=True), config=config) @@ -7999,10 +7943,6 @@ async def test_interrupts_in_tasks_surfaced_once( state = await program.aget_state(config=config) assert len(state.tasks[0].interrupts) == 1 task_interrupt = state.tasks[0].interrupts[0] - assert task_interrupt.resumable is True - assert len(task_interrupt.ns) == 2 - assert task_interrupt.ns[0].startswith("program:") - assert task_interrupt.ns[1].startswith("add_participant:") assert task_interrupt.value == "Hey do you want to add James?" interrupts = [ @@ -8015,10 +7955,6 @@ async def test_interrupts_in_tasks_surfaced_once( state = await program.aget_state(config=config) assert len(state.tasks[0].interrupts) == 1 task_interrupt = state.tasks[0].interrupts[0] - assert task_interrupt.resumable is True - assert len(task_interrupt.ns) == 2 - assert task_interrupt.ns[0].startswith("program:") - assert task_interrupt.ns[1].startswith("add_participant:") assert task_interrupt.value == "Hey do you want to add Will?" result = await program.ainvoke(Command(resume=True), config=config) diff --git a/libs/langgraph/tests/test_remote_graph.py b/libs/langgraph/tests/test_remote_graph.py index 262109673..76aa1c592 100644 --- a/libs/langgraph/tests/test_remote_graph.py +++ b/libs/langgraph/tests/test_remote_graph.py @@ -16,6 +16,7 @@ from langgraph.graph import StateGraph, add_messages from langgraph.pregel import Pregel from langgraph.pregel.remote import RemoteGraph from langgraph.types import Interrupt, StateSnapshot +from tests.any_str import AnyStr from tests.conftest import NO_DOCKER from tests.example_app.example_graph import app @@ -459,9 +460,7 @@ def test_stream(): "__interrupt__": [ { "value": {"question": "Does this look good?"}, - "resumable": True, - "ns": ["some_ns"], - "when": "during", + "id": AnyStr(), } ] }, @@ -489,9 +488,7 @@ def test_stream(): assert exc.value.args[0] == [ Interrupt( value={"question": "Does this look good?"}, - resumable=True, - ns=["some_ns"], - when="during", + id=AnyStr(), ) ] @@ -632,9 +629,7 @@ async def test_astream(): "__interrupt__": [ { "value": {"question": "Does this look good?"}, - "resumable": True, - "ns": ["some_ns"], - "when": "during", + "id": AnyStr(), } ] }, @@ -663,9 +658,7 @@ async def test_astream(): assert exc.value.args[0] == [ Interrupt( value={"question": "Does this look good?"}, - resumable=True, - ns=["some_ns"], - when="during", + id=AnyStr(), ) ] diff --git a/libs/prebuilt/langgraph/prebuilt/tool_node.py b/libs/prebuilt/langgraph/prebuilt/tool_node.py index 3de19db62..7ec65d44d 100644 --- a/libs/prebuilt/langgraph/prebuilt/tool_node.py +++ b/libs/prebuilt/langgraph/prebuilt/tool_node.py @@ -320,9 +320,10 @@ class ToolNode(RunnableCallable): response = self.tools_by_name[call["name"]].invoke(input, config) # GraphInterrupt is a special exception that will always be raised. - # It can be triggered in the following scenarios: - # (1) a NodeInterrupt is raised inside a tool - # (2) a NodeInterrupt is raised inside a graph node for a graph called as a tool + # It can be triggered in the following scenarios, + # Where GraphInterrupt(GraphBubbleUp) is raised from an `interrupt` invocation most commonly: + # (1) a GraphInterrupt is raised inside a tool + # (2) a GraphInterrupt is raised inside a graph node for a graph called as a tool # (3) a GraphInterrupt is raised when a subgraph is interrupted inside a graph called as a tool # (2 and 3 can happen in a "supervisor w/ tools" multi-agent architecture) except GraphBubbleUp as e: diff --git a/libs/prebuilt/tests/test_react_agent.py b/libs/prebuilt/tests/test_react_agent.py index d704616db..2aac3cbf6 100644 --- a/libs/prebuilt/tests/test_react_agent.py +++ b/libs/prebuilt/tests/test_react_agent.py @@ -1298,9 +1298,7 @@ def test_tool_node_node_interrupt( assert task.interrupts == ( Interrupt( value="provide value for foo", - when="during", - resumable=True, - ns=[AnyStr("tools:")], + id=AnyStr(), ), ) diff --git a/libs/prebuilt/tests/test_tool_node.py b/libs/prebuilt/tests/test_tool_node.py index b94094a23..ec7c91fdd 100644 --- a/libs/prebuilt/tests/test_tool_node.py +++ b/libs/prebuilt/tests/test_tool_node.py @@ -14,7 +14,7 @@ from langchain_core.tools import tool as dec_tool from pydantic import BaseModel, ValidationError from pydantic.v1 import ValidationError as ValidationErrorV1 -from langgraph.errors import NodeInterrupt +from langgraph.errors import GraphBubbleUp, GraphInterrupt from langgraph.prebuilt import ToolNode from langgraph.prebuilt.tool_node import TOOL_CALL_ERROR_TEMPLATE from langgraph.types import Command, Send @@ -462,16 +462,16 @@ def test_tool_node_incorrect_tool_name(): def test_tool_node_node_interrupt(): - def tool_interrupt(some_val: int) -> str: + def tool_interrupt(some_val: int) -> None: """Tool docstring.""" - raise NodeInterrupt("foo") + raise GraphBubbleUp("foo") - def handle(e: NodeInterrupt): + def handle(e: GraphInterrupt): return "handled" - for handle_tool_errors in (True, (NodeInterrupt,), "handled", handle, False): + for handle_tool_errors in (True, (GraphBubbleUp,), "handled", handle, False): node = ToolNode([tool_interrupt], handle_tool_errors=handle_tool_errors) - with pytest.raises(NodeInterrupt) as exc_info: + with pytest.raises(GraphBubbleUp) as exc_info: node.invoke( { "messages": [ diff --git a/libs/sdk-py/langgraph_sdk/schema.py b/libs/sdk-py/langgraph_sdk/schema.py index 9ad1c5b8b..13b525bca 100644 --- a/libs/sdk-py/langgraph_sdk/schema.py +++ b/libs/sdk-py/langgraph_sdk/schema.py @@ -222,17 +222,13 @@ class Assistant(AssistantBase): """The last time the assistant was updated.""" -class Interrupt(TypedDict, total=False): +class Interrupt(TypedDict): """Represents an interruption in the execution flow.""" value: Any """The value associated with the interrupt.""" - when: Literal["during"] - """When the interrupt occurred.""" - resumable: bool - """Whether the interrupt can be resumed.""" - ns: list[str] | None - """Optional namespace for the interrupt.""" + id: str + """The ID of the interrupt. Can be used to resume the interrupt.""" class Thread(TypedDict): From 5f00938aa25e60501d03e66fb32551b6f61a7311 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Thu, 10 Jul 2025 09:42:37 -0400 Subject: [PATCH 07/22] feat(langgraph): add type checking for matching `node` signatures vs `input_schema` for `add_node` (#5424) --- libs/langgraph/langgraph/graph/_node.py | 66 +++++++------- libs/langgraph/langgraph/graph/state.py | 101 +++++++++++++++++---- libs/langgraph/langgraph/pregel/main.py | 9 +- libs/langgraph/langgraph/typing.py | 5 +- libs/langgraph/tests/test_type_checking.py | 56 ++++++++++++ 5 files changed, 181 insertions(+), 56 deletions(-) diff --git a/libs/langgraph/langgraph/graph/_node.py b/libs/langgraph/langgraph/graph/_node.py index f63d29dbe..54f9a1fab 100644 --- a/libs/langgraph/langgraph/graph/_node.py +++ b/libs/langgraph/langgraph/graph/_node.py @@ -1,7 +1,9 @@ from __future__ import annotations +import sys from collections.abc import Sequence -from typing import Any, NamedTuple, Protocol, Union +from dataclasses import dataclass +from typing import Any, Generic, Protocol, Union from langchain_core.runnables import Runnable, RunnableConfig from typing_extensions import TypeAlias @@ -9,47 +11,49 @@ from typing_extensions import TypeAlias from langgraph.constants import EMPTY_SEQ from langgraph.store.base import BaseStore from langgraph.types import CachePolicy, RetryPolicy, StreamWriter -from langgraph.typing import StateT_contra +from langgraph.typing import NodeInputT, NodeInputT_contra + +_DC_SLOTS = {"slots": True} if sys.version_info >= (3, 10) else {} -class _Node(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra) -> Any: ... +class _Node(Protocol[NodeInputT_contra]): + def __call__(self, state: NodeInputT_contra) -> Any: ... -class _NodeWithConfig(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra, config: RunnableConfig) -> Any: ... +class _NodeWithConfig(Protocol[NodeInputT_contra]): + def __call__(self, state: NodeInputT_contra, config: RunnableConfig) -> Any: ... -class _NodeWithWriter(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra, *, writer: StreamWriter) -> Any: ... +class _NodeWithWriter(Protocol[NodeInputT_contra]): + def __call__(self, state: NodeInputT_contra, *, writer: StreamWriter) -> Any: ... -class _NodeWithStore(Protocol[StateT_contra]): - def __call__(self, state: StateT_contra, *, store: BaseStore) -> Any: ... +class _NodeWithStore(Protocol[NodeInputT_contra]): + def __call__(self, state: NodeInputT_contra, *, store: BaseStore) -> Any: ... -class _NodeWithWriterStore(Protocol[StateT_contra]): +class _NodeWithWriterStore(Protocol[NodeInputT_contra]): def __call__( - self, state: StateT_contra, *, writer: StreamWriter, store: BaseStore + self, state: NodeInputT_contra, *, writer: StreamWriter, store: BaseStore ) -> Any: ... -class _NodeWithConfigWriter(Protocol[StateT_contra]): +class _NodeWithConfigWriter(Protocol[NodeInputT_contra]): def __call__( - self, state: StateT_contra, *, config: RunnableConfig, writer: StreamWriter + self, state: NodeInputT_contra, *, config: RunnableConfig, writer: StreamWriter ) -> Any: ... -class _NodeWithConfigStore(Protocol[StateT_contra]): +class _NodeWithConfigStore(Protocol[NodeInputT_contra]): def __call__( - self, state: StateT_contra, *, config: RunnableConfig, store: BaseStore + self, state: NodeInputT_contra, *, config: RunnableConfig, store: BaseStore ) -> Any: ... -class _NodeWithConfigWriterStore(Protocol[StateT_contra]): +class _NodeWithConfigWriterStore(Protocol[NodeInputT_contra]): def __call__( self, - state: StateT_contra, + state: NodeInputT_contra, *, config: RunnableConfig, writer: StreamWriter, @@ -61,23 +65,23 @@ class _NodeWithConfigWriterStore(Protocol[StateT_contra]): # we move to adding a context arg. Maybe what we do is we add support for kwargs with param spec # this is purely for typing purposes though, so can easily change in the coming weeks. StateNode: TypeAlias = Union[ - _Node[StateT_contra], - _NodeWithConfig[StateT_contra], - _NodeWithWriter[StateT_contra], - _NodeWithStore[StateT_contra], - _NodeWithWriterStore[StateT_contra], - _NodeWithConfigWriter[StateT_contra], - _NodeWithConfigStore[StateT_contra], - _NodeWithConfigWriterStore[StateT_contra], - Runnable[StateT_contra, Any], + _Node[NodeInputT], + _NodeWithConfig[NodeInputT], + _NodeWithWriter[NodeInputT], + _NodeWithStore[NodeInputT], + _NodeWithWriterStore[NodeInputT], + _NodeWithConfigWriter[NodeInputT], + _NodeWithConfigStore[NodeInputT], + _NodeWithConfigWriterStore[NodeInputT], + Runnable[NodeInputT, Any], ] -# TODO: use a dataclass generic on NodeInputType -class StateNodeSpec(NamedTuple): - runnable: StateNode +@dataclass(**_DC_SLOTS) +class StateNodeSpec(Generic[NodeInputT]): + runnable: StateNode[NodeInputT] metadata: dict[str, Any] | None - input_schema: type[Any] + input_schema: type[NodeInputT] retry_policy: RetryPolicy | Sequence[RetryPolicy] | None cache_policy: CachePolicy | None ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 8a094c6c4..041923dbb 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -83,7 +83,7 @@ from langgraph.types import ( RetryPolicy, Send, ) -from langgraph.typing import InputT, OutputT, StateT +from langgraph.typing import InputT, NodeInputT, OutputT, StateT from langgraph.warnings import LangGraphDeprecatedSinceV05 __all__ = ("StateGraph", "CompiledStateGraph") @@ -267,13 +267,31 @@ class StateGraph(Generic[StateT, InputT, OutputT]): *, defer: bool = False, metadata: dict[str, Any] | None = None, - input_schema: type[Any] | None = None, + input_schema: None = None, retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None, cache_policy: CachePolicy | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None, **kwargs: Unpack[DeprecatedKwargs], ) -> Self: - """Add a new node to the state graph. + """Add a new node to the state graph, input schema is inferred as the state schema. + Will take the name of the function/runnable as the node name. + """ + ... + + @overload + def add_node( + self, + node: StateNode[NodeInputT], + *, + defer: bool = False, + metadata: dict[str, Any] | None = None, + input_schema: type[NodeInputT], + retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None, + cache_policy: CachePolicy | None = None, + destinations: dict[str, str] | tuple[str, ...] | None = None, + **kwargs: Unpack[DeprecatedKwargs], + ) -> Self: + """Add a new node to the state graph, input schema is specified. Will take the name of the function/runnable as the node name. """ ... @@ -286,23 +304,40 @@ class StateGraph(Generic[StateT, InputT, OutputT]): *, defer: bool = False, metadata: dict[str, Any] | None = None, - input_schema: type[Any] | None = None, + input_schema: None = None, retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None, cache_policy: CachePolicy | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None, **kwargs: Unpack[DeprecatedKwargs], ) -> Self: - """Add a new node to the state graph.""" + """Add a new node to the state graph, input schema is inferred as the state schema.""" + ... + + @overload + def add_node( + self, + node: str, + action: StateNode[NodeInputT], + *, + defer: bool = False, + metadata: dict[str, Any] | None = None, + input_schema: type[NodeInputT], + retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None, + cache_policy: CachePolicy | None = None, + destinations: dict[str, str] | tuple[str, ...] | None = None, + **kwargs: Unpack[DeprecatedKwargs], + ) -> Self: + """Add a new node to the state graph, input schema is specified.""" ... def add_node( self, - node: str | StateNode[StateT], - action: StateNode[StateT] | None = None, + node: str | StateNode[StateT] | StateNode[NodeInputT], + action: StateNode[StateT] | StateNode[NodeInputT] | None = None, *, defer: bool = False, metadata: dict[str, Any] | None = None, - input_schema: type[Any] | None = None, + input_schema: type[NodeInputT] | None = None, retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None, cache_policy: CachePolicy | None = None, destinations: dict[str, str] | tuple[str, ...] | None = None, @@ -375,7 +410,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): category=LangGraphDeprecatedSinceV05, ) if input_schema is None: - input_schema = cast(Union[type[InputT], None], input_) + input_schema = cast(Union[type[NodeInputT], None], input_) if not isinstance(node, str): action = node @@ -412,6 +447,8 @@ class StateGraph(Generic[StateT, InputT, OutputT]): f"'{character}' is a reserved character and is not allowed in the node names." ) + inferred_input_schema = None + ends: tuple[str, ...] | dict[str, str] = EMPTY_SEQ try: if ( @@ -432,7 +469,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): ) if input_hint := hints.get(first_parameter_name): if isinstance(input_hint, type) and get_type_hints(input_hint): - input_schema = input_hint + inferred_input_schema = input_hint if rtn := hints.get("return"): # Handle Union types rtn_origin = get_origin(rtn) @@ -460,17 +497,41 @@ class StateGraph(Generic[StateT, InputT, OutputT]): if destinations is not None: ends = destinations + if input_schema is not None: + self.nodes[node] = StateNodeSpec[NodeInputT]( + coerce_to_runnable(action, name=node, trace=False), + metadata, + input_schema=input_schema, + retry_policy=retry_policy, + cache_policy=cache_policy, + ends=ends, + defer=defer, + ) + elif inferred_input_schema is not None: + self.nodes[node] = StateNodeSpec( + coerce_to_runnable(action, name=node, trace=False), + metadata, + input_schema=inferred_input_schema, + retry_policy=retry_policy, + cache_policy=cache_policy, + ends=ends, + defer=defer, + ) + else: + self.nodes[node] = StateNodeSpec[StateT]( + coerce_to_runnable(action, name=node, trace=False), + metadata, + input_schema=self.state_schema, + retry_policy=retry_policy, + cache_policy=cache_policy, + ends=ends, + defer=defer, + ) + + input_schema = input_schema or inferred_input_schema if input_schema is not None: self._add_schema(input_schema) - self.nodes[node] = StateNodeSpec( - coerce_to_runnable(action, name=node, trace=False), - metadata, - input_schema=input_schema or self.state_schema, - retry_policy=retry_policy, - cache_policy=cache_policy, - ends=ends, - defer=defer, - ) + return self def add_edge(self, start_key: str | list[str], end_key: str) -> Self: @@ -923,7 +984,7 @@ class CompiledStateGraph( writers=[ChannelWrite(write_entries)], ) elif node is not None: - input_schema = node.input_schema if node else self.builder._state_schema + input_schema = node.input_schema if node else self.builder.state_schema input_channels = list(self.builder.schemas[input_schema]) is_single_input = len(input_channels) == 1 and "__root__" in input_channels if input_schema in self.schema_to_mapper: diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index 539f0aedd..b165c3318 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -118,6 +118,7 @@ from langgraph.types import ( All, CachePolicy, Checkpointer, + Command, Interrupt, Send, StateSnapshot, @@ -2346,7 +2347,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou def stream( self, - input: InputT, + input: InputT | Command | None, config: RunnableConfig | None = None, *, stream_mode: StreamMode | Sequence[StreamMode] | None = None, @@ -2568,7 +2569,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou async def astream( self, - input: InputT, + input: InputT | Command | None, config: RunnableConfig | None = None, *, stream_mode: StreamMode | Sequence[StreamMode] | None = None, @@ -2812,7 +2813,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou def invoke( self, - input: InputT, + input: InputT | Command | None, config: RunnableConfig | None = None, *, stream_mode: StreamMode = "values", @@ -2887,7 +2888,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou async def ainvoke( self, - input: InputT, + input: InputT | Command | None, config: RunnableConfig | None = None, *, stream_mode: StreamMode = "values", diff --git a/libs/langgraph/langgraph/typing.py b/libs/langgraph/langgraph/typing.py index d7a82e9fd..bfddc9ae5 100644 --- a/libs/langgraph/langgraph/typing.py +++ b/libs/langgraph/langgraph/typing.py @@ -27,6 +27,9 @@ InputT = TypeVar("InputT", bound=StateLike, default=StateT) Defaults to `StateT`. """ - OutputT = TypeVar("OutputT", bound=Union[StateLike, None], default=StateT) """Type variable used to represent the output of a state graph.""" + +NodeInputT = TypeVar("NodeInputT", bound=StateLike) + +NodeInputT_contra = TypeVar("NodeInputT_contra", bound=StateLike, contravariant=True) diff --git a/libs/langgraph/tests/test_type_checking.py b/libs/langgraph/tests/test_type_checking.py index 0b7ee0679..5f807ddaa 100644 --- a/libs/langgraph/tests/test_type_checking.py +++ b/libs/langgraph/tests/test_type_checking.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from operator import add from typing import Annotated, Any +import pytest from langchain_core.runnables import RunnableConfig from pydantic import BaseModel from typing_extensions import TypedDict from langgraph.graph import StateGraph +from langgraph.types import Command def test_typed_dict_state() -> None: @@ -103,3 +105,57 @@ def test_input_state_specified() -> None: new_graph.invoke({"something": 1}) new_graph.invoke({"something": 2, "info": ["hello", "world"]}) # type: ignore[arg-type] + + +@pytest.mark.skip("Purely for type checking") +def test_invoke_with_all_valid_types() -> None: + class State(TypedDict): + a: int + + def a(state: State) -> Any: ... + + graph = StateGraph(State).add_node("a", a).set_entry_point("a").compile() + graph.invoke({"a": 1}) + graph.invoke(None) + graph.invoke(Command()) + + +def test_add_node_with_explicit_input_schema() -> None: + class A(TypedDict): + a1: int + a2: str + + class B(TypedDict): + b1: int + b2: str + + class ANarrow(TypedDict): + a1: int + + class BNarrow(TypedDict): + b1: int + + class State(A, B): ... + + def a(state: A) -> Any: ... + + def b(state: B) -> Any: ... + + workflow = StateGraph(State) + # input schema matches typed schemas + workflow.add_node("a", a, input_schema=A) + workflow.add_node("b", b, input_schema=B) + + # input schema does not match typed schemas + workflow.add_node("a_wrong", a, input_schema=B) # type: ignore[arg-type] + workflow.add_node("b_wrong", b, input_schema=A) # type: ignore[arg-type] + + # input schema is more broad than the typed schemas, which is allowed + # by the principles of contravariance + workflow.add_node("a_inclusive", a, input_schema=State) + workflow.add_node("b_inclusive", b, input_schema=State) + + # input schema is more narrow than the typed schemas, which is not allowed + # because it violates the principles of contravariance + workflow.add_node("a_narrow", a, input_schema=ANarrow) # type: ignore[arg-type] + workflow.add_node("b_narrow", b, input_schema=BNarrow) # type: ignore[arg-type] From 08372635424fd9e2957d763cb0f2f466da552141 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 15 Jul 2025 09:20:20 -0400 Subject: [PATCH 08/22] feat(langgraph): new context api (replacing `config['configurable']` and `config_schema`) (#5243) --- docs/docs/agents/context.md | 54 +++--- docs/docs/cloud/deployment/setup.md | 6 +- docs/docs/cloud/deployment/setup_pyproject.md | 6 +- .../docs/cloud/how-tos/configuration_cloud.md | 15 +- docs/docs/concepts/low_level.md | 31 ++-- docs/docs/how-tos/graph-api.md | 60 +++--- docs/docs/tutorials/tot/tot.ipynb | 56 +++--- libs/cli/examples/graphs/agent.py | 4 +- .../langgraph/_internal/_runnable.py | 157 +++++++++------- .../langgraph/langgraph/_internal/_runtime.py | 39 ++++ libs/langgraph/langgraph/config.py | 9 +- libs/langgraph/langgraph/constants.py | 11 +- libs/langgraph/langgraph/func/__init__.py | 28 ++- libs/langgraph/langgraph/graph/_branch.py | 1 - libs/langgraph/langgraph/graph/_node.py | 14 +- libs/langgraph/langgraph/graph/state.py | 98 ++++++---- libs/langgraph/langgraph/pregel/_algo.py | 33 ++-- libs/langgraph/langgraph/pregel/_read.py | 1 - libs/langgraph/langgraph/pregel/_write.py | 1 - libs/langgraph/langgraph/pregel/main.py | 172 ++++++++++++++---- libs/langgraph/langgraph/pregel/protocol.py | 8 +- libs/langgraph/langgraph/runtime.py | 55 ++++++ libs/langgraph/langgraph/types.py | 14 +- libs/langgraph/langgraph/typing.py | 19 +- .../tests/__snapshots__/test_pregel.ambr | 2 +- libs/langgraph/tests/test_deprecation.py | 53 +++++- libs/langgraph/tests/test_pregel.py | 80 +++----- libs/langgraph/tests/test_runnable.py | 133 ++++++++++++-- libs/langgraph/tests/test_runtime.py | 47 +++++ .../langgraph/prebuilt/chat_agent_executor.py | 24 ++- 30 files changed, 845 insertions(+), 386 deletions(-) create mode 100644 libs/langgraph/langgraph/_internal/_runtime.py create mode 100644 libs/langgraph/langgraph/runtime.py create mode 100644 libs/langgraph/tests/test_runtime.py diff --git a/docs/docs/agents/context.md b/docs/docs/agents/context.md index 542379a44..c9338d50f 100644 --- a/docs/docs/agents/context.md +++ b/docs/docs/agents/context.md @@ -12,56 +12,66 @@ LangGraph provides **three** primary ways to supply context: | Type | Description | Mutable? | Lifetime | |------------------------------------------------------------------------------|-----------------------------------------------|----------|-------------------------| -| [**Config**](#config-static-context) | data passed at the start of a run | ❌ | per run | +| [**Runtime Context**](#runtime-context) | data passed at the start of a run | ❌ | per run | | [**Short-term memory (State)**](#short-term-memory-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation | | [**Long-term memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations | ## Provide runtime context -### Config (static context) +### Runtime Context -Config is for immutable data like user metadata or API keys. Use -when you have values that don't change mid-run. +!!! note "`config['configurable']` -> `runtime.context`" -Specify configuration using a key called **"configurable"** which is reserved -for this purpose: + In LangGraph < v1.0, static runtime context was passed via the `config['configurable']` key, paired with a `config_schema` argument + to `StateGraph` or `Pregel`. This is now deprecated and will be removed in v2.0. + + As of LangGraph v1.0, the Runtime object is recommended to access static context and runtime-specific information like the store and stream writer. + +Runtime context is for immutable data like user metadata or API keys. Use this when you have values that don't change mid-run. + +Specify static context via the `context` argument to `invoke` / `stream`, which is reserved for this purpose: ```python +@dataclass +class ContextSchema: + user_name: str + graph.invoke( # (1)! {"messages": [{"role": "user", "content": "hi!"}]}, # (2)! # highlight-next-line - config={"configurable": {"user_id": "user_123"}} # (3)! + context={"user_name": "John Smith"} # (3)! ) ``` 1. This is the invocation of the agent or graph. The `invoke` method runs the underlying graph with the provided input. 2. This example uses messages as an input, which is common, but your application may use different input structures. -3. This is where you pass the configuration data. The `config` parameter allows you to provide additional context that the agent can use during its execution. +3. This is where you pass the runtime data. The `context` parameter allows you to provide additional dependencies that the agent can use during its execution. === "Agent prompt" ```python from langchain_core.messages import AnyMessage - from langchain_core.runnables import RunnableConfig + from langgraph.runtime import get_runtime from langgraph.prebuilt.chat_agent_executor import AgentState from langgraph.prebuilt import create_react_agent # highlight-next-line - def prompt(state: AgentState, config: RunnableConfig) -> list[AnyMessage]: - user_name = config["configurable"].get("user_name") - system_msg = f"You are a helpful assistant. Address the user as {user_name}." + def prompt(state: AgentState) -> list[AnyMessage]: + runtime = get_runtime(ContextSchema) + system_msg = f"You are a helpful assistant. Address the user as {runtime.context.user_name}." return [{"role": "system", "content": system_msg}] + state["messages"] agent = create_react_agent( model="anthropic:claude-3-7-sonnet-latest", tools=[get_weather], - prompt=prompt + prompt=prompt, + context_schema=ContextSchema ) agent.invoke( {"messages": [{"role": "user", "content": "what is the weather in sf"}]}, # highlight-next-line - config={"configurable": {"user_name": "John Smith"}} + context={"user_name": "John Smith"} ) ``` @@ -70,11 +80,11 @@ graph.invoke( # (1)! === "Workflow node" ```python - from langchain_core.runnables import RunnableConfig + from langgraph.runtime import Runtime # highlight-next-line - def node(state: State, config: RunnableConfig): - user_name = config["configurable"].get("user_name") + def node(state: State, config: Runtime[ContextSchema]): + user_name = runtime.context.user_name ... ``` @@ -83,14 +93,16 @@ graph.invoke( # (1)! === "In a tool" ```python - from langchain_core.runnables import RunnableConfig + from langgraph.runtime import get_runtime @tool # highlight-next-line - def get_user_info(config: RunnableConfig) -> str: + def get_user_email() -> str: """Retrieve user information based on user ID.""" - user_id = config["configurable"].get("user_id") - return "User is John Smith" if user_id == "user_123" else "Unknown user" + # simulate fetching user info from a database + runtime = get_runtime(ContextSchema) + email = get_user_email_from_db(runtime.context.user_name) + return email ``` See the [tool calling guide](../how-tos/tool-calling.md#configuration) for details. diff --git a/docs/docs/cloud/deployment/setup.md b/docs/docs/cloud/deployment/setup.md index 4fc52c7e5..91aecf989 100644 --- a/docs/docs/cloud/deployment/setup.md +++ b/docs/docs/cloud/deployment/setup.md @@ -108,11 +108,11 @@ from langgraph.graph import StateGraph, END, START from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes from my_agent.utils.state import AgentState # import state -# Define the config -class GraphConfig(TypedDict): +# Define the runtime context +class GraphContext(TypedDict): model_name: Literal["anthropic", "openai"] -workflow = StateGraph(AgentState, config_schema=GraphConfig) +workflow = StateGraph(AgentState, context_schema=GraphContext) workflow.add_node("agent", call_model) workflow.add_node("action", tool_node) workflow.add_edge(START, "agent") diff --git a/docs/docs/cloud/deployment/setup_pyproject.md b/docs/docs/cloud/deployment/setup_pyproject.md index 033ab7763..0b06149f6 100644 --- a/docs/docs/cloud/deployment/setup_pyproject.md +++ b/docs/docs/cloud/deployment/setup_pyproject.md @@ -121,11 +121,11 @@ from langgraph.graph import StateGraph, END, START from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes from my_agent.utils.state import AgentState # import state -# Define the config -class GraphConfig(TypedDict): +# Define the runtime context +class GraphContext(TypedDict): model_name: Literal["anthropic", "openai"] -workflow = StateGraph(AgentState, config_schema=GraphConfig) +workflow = StateGraph(AgentState, context_schema=GraphContext) workflow.add_node("agent", call_model) workflow.add_node("action", tool_node) workflow.add_edge(START, "agent") diff --git a/docs/docs/cloud/how-tos/configuration_cloud.md b/docs/docs/cloud/how-tos/configuration_cloud.md index adc2fd841..77f2a0aa6 100644 --- a/docs/docs/cloud/how-tos/configuration_cloud.md +++ b/docs/docs/cloud/how-tos/configuration_cloud.md @@ -2,21 +2,20 @@ In this guide we will show how to create, configure, and manage an [assistant](../../concepts/assistants.md). -First, as a brief refresher on the concept of configurations, consider the following simple `call_model` node and configuration schema. Observe that this node tries to read and use the `model_name` as defined by the `config` object's `configurable`. +First, as a brief refresher on the concept of runtime context, consider the following simple `call_model` node and configuration schema. Observe that this node tries to read and use the `model_provider` as defined by the `Runtime` object's `context` property. === "Python" ```python + @dataclass + class ContextSchema: + llm_provider: str = "anthropic" - class ConfigSchema(TypedDict): - model_name: str + builder = StateGraph(AgentState, context_schema=ContextSchema) - builder = StateGraph(AgentState, config_schema=ConfigSchema) - - def call_model(state, config): + def call_model(state, runtime: Runtime[ContextSchema]): messages = state["messages"] - model_name = config.get('configurable', {}).get("model_name", "anthropic") - model = _get_model(model_name) + model = _get_model(runtime.context.llm_provider) response = model.invoke(messages) # We return a list, because this will get added to the existing list return {"messages": [response]} diff --git a/docs/docs/concepts/low_level.md b/docs/docs/concepts/low_level.md index 13c0fa0f7..7eab909f3 100644 --- a/docs/docs/concepts/low_level.md +++ b/docs/docs/concepts/low_level.md @@ -459,33 +459,32 @@ LangGraph can easily handle migrations of graph definitions (nodes, edges, and s - State keys that are renamed lose their saved state in existing threads - State keys whose types change in incompatible ways could currently cause issues in threads with state from before the change -- if this is a blocker please reach out and we can prioritize a solution. -## Configuration +## Runtime Context -When creating a graph, you can also mark that certain parts of the graph are configurable. This is commonly done to enable easily switching between models or system prompts. This allows you to create a single "cognitive architecture" (the graph) but have multiple different instance of it. - -You can optionally specify a `config_schema` when creating a graph. +When creating a graph, you can specify a `context_schema` for runtime context passed to nodes. This is useful for passing +information to nodes that is not part of the graph state. For example, you might want to pass dependencies such as model name or a database connection. ```python -class ConfigSchema(TypedDict): - llm: str +@dataclass +class ContextSchema: + llm_provider: str = "openai" -graph = StateGraph(State, config_schema=ConfigSchema) +graph = StateGraph(State, context_schema=ContextSchema) ``` -You can then pass this configuration into the graph using the `configurable` config field. +You can then pass this context into the graph using the `context` parameter of the `invoke` method. ```python -config = {"configurable": {"llm": "anthropic"}} - -graph.invoke(inputs, config=config) +graph.invoke(inputs, context={"llm_provider": "anthropic"}) ``` -You can then access and use this configuration inside a node or conditional edge: +You can then access and use this context inside a node or conditional edge: ```python -def node_a(state, config): - llm_type = config.get("configurable", {}).get("llm", "openai") - llm = get_llm(llm_type) +from langgraph.runtime import Runtime + +def node_a(state: State, runtime: Runtime[ContextSchema]): + llm = get_llm(runtime.context.llm_provider) ... ``` @@ -496,7 +495,7 @@ See [this guide](../how-tos/graph-api.md#add-runtime-configuration) for a full b The recursion limit sets the maximum number of [super-steps](#graphs) the graph can execute during a single execution. Once the limit is reached, LangGraph will raise `GraphRecursionError`. By default this value is set to 25 steps. The recursion limit can be set on any graph at runtime, and is passed to `.invoke`/`.stream` via the config dictionary. Importantly, `recursion_limit` is a standalone `config` key and should not be passed inside the `configurable` key as all other user-defined configuration. See the example below: ```python -graph.invoke(inputs, config={"recursion_limit": 5, "configurable":{"llm": "anthropic"}}) +graph.invoke(inputs, config={"recursion_limit": 5}, context={"llm": "anthropic"}) ``` Read [this how-to](https://langchain-ai.github.io/langgraph/how-tos/recursion-limit/) to learn more about how the recursion limit works. diff --git a/docs/docs/how-tos/graph-api.md b/docs/docs/how-tos/graph-api.md index b0f692738..cec0e746f 100644 --- a/docs/docs/how-tos/graph-api.md +++ b/docs/docs/how-tos/graph-api.md @@ -513,12 +513,12 @@ To add runtime configuration: See below for a simple example: ```python -from langchain_core.runnables import RunnableConfig from langgraph.graph import END, StateGraph, START +from langgraph.runtime import Runtime from typing_extensions import TypedDict # 1. Specify config schema -class ConfigSchema(TypedDict): +class ContextSchema(TypedDict): my_runtime_value: str # 2. Define a graph that accesses the config in a node @@ -526,18 +526,18 @@ class State(TypedDict): my_state_value: str # highlight-next-line -def node(state: State, config: RunnableConfig): +def node(state: State, runtime: Runtime[ContextSchema]): # highlight-next-line - if config["configurable"]["my_runtime_value"] == "a": + if runtime.context["my_runtime_value"] == "a": return {"my_state_value": 1} # highlight-next-line - elif config["configurable"]["my_runtime_value"] == "b": + elif runtime.context["my_runtime_value"] == "b": return {"my_state_value": 2} else: raise ValueError("Unknown values.") # highlight-next-line -builder = StateGraph(State, config_schema=ConfigSchema) +builder = StateGraph(State, context_schema=ContextSchema) builder.add_node(node) builder.add_edge(START, "node") builder.add_edge("node", END) @@ -546,9 +546,9 @@ graph = builder.compile() # 3. Pass in configuration at runtime: # highlight-next-line -print(graph.invoke({}, {"configurable": {"my_runtime_value": "a"}})) +print(graph.invoke({}, context={"my_runtime_value": "a"})) # highlight-next-line -print(graph.invoke({}, {"configurable": {"my_runtime_value": "b"}})) +print(graph.invoke({}, context={"my_runtime_value": "b"})) ``` ``` {'my_state_value': 1} @@ -559,27 +559,28 @@ print(graph.invoke({}, {"configurable": {"my_runtime_value": "b"}})) Below we demonstrate a practical example in which we configure what LLM to use at runtime. We will use both OpenAI and Anthropic models. ```python + from dataclasses import dataclass + from langchain.chat_models import init_chat_model - from langchain_core.runnables import RunnableConfig - from langgraph.graph import MessagesState - from langgraph.graph import END, StateGraph, START + from langgraph.graph import MessagesState, END, StateGraph, START + from langgraph.runtime import Runtime from typing_extensions import TypedDict - class ConfigSchema(TypedDict): - model: str + @dataclass + class ContextSchema: + model_provider: str = "anthropic" MODELS = { "anthropic": init_chat_model("anthropic:claude-3-5-haiku-latest"), "openai": init_chat_model("openai:gpt-4.1-mini"), } - def call_model(state: MessagesState, config: RunnableConfig): - model = config["configurable"].get("model", "anthropic") - model = MODELS[model] + def call_model(state: MessagesState, runtime: Runtime[ContextSchema]): + model = MODELS[runtime.context.model_provider] response = model.invoke(state["messages"]) return {"messages": [response]} - builder = StateGraph(MessagesState, config_schema=ConfigSchema) + builder = StateGraph(MessagesState, context_schema=ContextSchema) builder.add_node("model", call_model) builder.add_edge(START, "model") builder.add_edge("model", END) @@ -591,8 +592,7 @@ print(graph.invoke({}, {"configurable": {"my_runtime_value": "b"}})) # With no configuration, uses default (Anthropic) response_1 = graph.invoke({"messages": [input_message]})["messages"][-1] # Or, can set OpenAI - config = {"configurable": {"model": "openai"}} - response_2 = graph.invoke({"messages": [input_message]}, config=config)["messages"][-1] + response_2 = graph.invoke({"messages": [input_message]}, context={"model_provider": "openai"})["messages"][-1] print(response_1.response_metadata["model_name"]) print(response_2.response_metadata["model_name"]) @@ -606,32 +606,33 @@ print(graph.invoke({}, {"configurable": {"my_runtime_value": "b"}})) Below we demonstrate a practical example in which we configure two parameters: the LLM and system message to use at runtime. ```python + from dataclasses import dataclass from typing import Optional from langchain.chat_models import init_chat_model from langchain_core.messages import SystemMessage - from langchain_core.runnables import RunnableConfig from langgraph.graph import END, MessagesState, StateGraph, START + from langgraph.runtime import Runtime from typing_extensions import TypedDict - class ConfigSchema(TypedDict): - model: Optional[str] - system_message: Optional[str] + @dataclass + class ContextSchema: + model_provider: str = "anthropic" + system_message: str | None = None MODELS = { "anthropic": init_chat_model("anthropic:claude-3-5-haiku-latest"), "openai": init_chat_model("openai:gpt-4.1-mini"), } - def call_model(state: MessagesState, config: RunnableConfig): - model = config["configurable"].get("model", "anthropic") - model = MODELS[model] + def call_model(state: MessagesState, runtime: Runtime[ContextSchema]): + model = MODELS[runtime.context.model_provider] messages = state["messages"] - if system_message := config["configurable"].get("system_message"): + if (system_message := runtime.context.system_message): messages = [SystemMessage(system_message)] + messages response = model.invoke(messages) return {"messages": [response]} - builder = StateGraph(MessagesState, config_schema=ConfigSchema) + builder = StateGraph(MessagesState, context_schema=ContextSchema) builder.add_node("model", call_model) builder.add_edge(START, "model") builder.add_edge("model", END) @@ -640,8 +641,7 @@ print(graph.invoke({}, {"configurable": {"my_runtime_value": "b"}})) # Usage input_message = {"role": "user", "content": "hi"} - config = {"configurable": {"model": "openai", "system_message": "Respond in Italian."}} - response = graph.invoke({"messages": [input_message]}, config) + response = graph.invoke({"messages": [input_message]}, context={"model_provider": "openai", "system_message": "Respond in Italian."}) for message in response["messages"]: message.pretty_print() ``` diff --git a/docs/docs/tutorials/tot/tot.ipynb b/docs/docs/tutorials/tot/tot.ipynb index cb6e967a0..bcb8502aa 100644 --- a/docs/docs/tutorials/tot/tot.ipynb +++ b/docs/docs/tutorials/tot/tot.ipynb @@ -280,8 +280,8 @@ "from typing import Optional, Dict, Any\n", "from typing_extensions import Annotated, TypedDict\n", "from langgraph.graph import StateGraph\n", + "from langgraph.types import Runtime\n", "\n", - "from langchain_core.runnables import RunnableConfig\n", "from langgraph.checkpoint.memory import MemorySaver\n", "from langgraph.types import Send\n", "\n", @@ -307,22 +307,25 @@ " depth: Annotated[int, operator.add]\n", "\n", "\n", - "class Configuration(TypedDict, total=False):\n", + "class Context(TypedDict, total=False):\n", " max_depth: int\n", " threshold: float\n", " k: int\n", " beam_size: int\n", "\n", + "class EnsuredContext(TypedDict):\n", + " max_depth: int\n", + " threshold: float\n", + " k: int\n", + " beam_size: int\n", "\n", - "def _ensure_configurable(config: RunnableConfig) -> Configuration:\n", + "def _ensure_context(ctx: Context) -> EnsuredContext:\n", " \"\"\"Get params that configure the search algorithm.\"\"\"\n", - " configurable = config.get(\"configurable\", {})\n", " return {\n", - " **configurable,\n", - " \"max_depth\": configurable.get(\"max_depth\", 10),\n", - " \"threshold\": config.get(\"threshold\", 0.9),\n", - " \"k\": configurable.get(\"k\", 5),\n", - " \"beam_size\": configurable.get(\"beam_size\", 3),\n", + " \"max_depth\": ctx.get(\"max_depth\", 10),\n", + " \"threshold\": ctx.get(\"threshold\", 0.9),\n", + " \"k\": ctx.get(\"k\", 5),\n", + " \"beam_size\": ctx.get(\"beam_size\", 3)\n", " }\n", "\n", "\n", @@ -330,9 +333,9 @@ " seed: Optional[Candidate]\n", "\n", "\n", - "def expand(state: ExpansionState, *, config: RunnableConfig) -> Dict[str, List[str]]:\n", + "def expand(state: ExpansionState, *, runtime: Runtime[Context]) -> Dict[str, List[Candidate]]:\n", " \"\"\"Generate the next state.\"\"\"\n", - " configurable = _ensure_configurable(config)\n", + " ctx = _ensure_context(runtime.context)\n", " if not state.get(\"seed\"):\n", " candidate_str = \"\"\n", " else:\n", @@ -342,9 +345,8 @@ " {\n", " \"problem\": state[\"problem\"],\n", " \"candidate\": candidate_str,\n", - " \"k\": configurable[\"k\"],\n", + " \"k\": ctx[\"k\"],\n", " },\n", - " config=config,\n", " )\n", " except Exception:\n", " return {\"candidates\": []}\n", @@ -354,7 +356,7 @@ " return {\"candidates\": new_candidates}\n", "\n", "\n", - "def score(state: ToTState) -> Dict[str, List[float]]:\n", + "def score(state: ToTState) -> Dict[str, Any]:\n", " \"\"\"Evaluate the candidate generations.\"\"\"\n", " candidates = state[\"candidates\"]\n", " scored = []\n", @@ -364,10 +366,10 @@ "\n", "\n", "def prune(\n", - " state: ToTState, *, config: RunnableConfig\n", - ") -> Dict[str, List[Dict[str, Any]]]:\n", + " state: ToTState, *, runtime: Runtime[Context]\n", + ") -> Dict[str, Any]:\n", " scored_candidates = state[\"scored_candidates\"]\n", - " beam_size = _ensure_configurable(config)[\"beam_size\"]\n", + " beam_size = _ensure_context(runtime.context)[\"beam_size\"]\n", " organized = sorted(\n", " scored_candidates, key=lambda candidate: candidate[1], reverse=True\n", " )\n", @@ -383,11 +385,11 @@ "\n", "\n", "def should_terminate(\n", - " state: ToTState, config: RunnableConfig\n", + " state: ToTState, runtime: Runtime[Context]\n", ") -> Union[Literal[\"__end__\"], Send]:\n", - " configurable = _ensure_configurable(config)\n", - " solved = state[\"candidates\"][0].score >= configurable[\"threshold\"]\n", - " if solved or state[\"depth\"] >= configurable[\"max_depth\"]:\n", + " ctx = _ensure_context(runtime.context)\n", + " solved = state[\"candidates\"][0].score >= ctx[\"threshold\"]\n", + " if solved or state[\"depth\"] >= ctx[\"max_depth\"]:\n", " return \"__end__\"\n", " return [\n", " Send(\"expand\", {**state, \"somevalseed\": candidate})\n", @@ -396,7 +398,7 @@ "\n", "\n", "# Create the graph\n", - "builder = StateGraph(state_schema=ToTState, config_schema=Configuration)\n", + "builder = StateGraph(state_schema=ToTState, context_schema=Context)\n", "\n", "# Add nodes\n", "builder.add_node(expand)\n", @@ -467,13 +469,7 @@ } ], "source": [ - "config = {\n", - " \"configurable\": {\n", - " \"thread_id\": \"test_1\",\n", - " \"depth\": 10,\n", - " }\n", - "}\n", - "for step in graph.stream({\"problem\": puzzles[42]}, config):\n", + "for step in graph.stream({\"problem\": puzzles[42]}, config={\"configurable\": {\"thread_id\": \"test_1\"}}, context={\"depth\": 10}):\n", " print(step)" ] }, @@ -491,7 +487,7 @@ } ], "source": [ - "final_state = graph.get_state(config)\n", + "final_state = graph.get_state({'configurable': {'thread_id': 'test_1'}})\n", "winning_solution = final_state.values[\"candidates\"][0]\n", "search_depth = final_state.values[\"depth\"]\n", "if winning_solution[1] == 1:\n", diff --git a/libs/cli/examples/graphs/agent.py b/libs/cli/examples/graphs/agent.py index cf874a2ae..f39df4cae 100644 --- a/libs/cli/examples/graphs/agent.py +++ b/libs/cli/examples/graphs/agent.py @@ -49,12 +49,12 @@ def call_model(state, config): tool_node = ToolNode(tools) -class ConfigSchema(TypedDict): +class ContextSchema(TypedDict): model: Literal["anthropic", "openai"] # Define a new graph -workflow = StateGraph(AgentState, config_schema=ConfigSchema) +workflow = StateGraph(AgentState, context_schema=ContextSchema) # Define the two nodes we will cycle between workflow.add_node("agent", call_model) diff --git a/libs/langgraph/langgraph/_internal/_runnable.py b/libs/langgraph/langgraph/_internal/_runnable.py index 37ac97fbf..9a4b76ea0 100644 --- a/libs/langgraph/langgraph/_internal/_runnable.py +++ b/libs/langgraph/langgraph/_internal/_runnable.py @@ -48,11 +48,10 @@ from langgraph._internal._config import ( get_callback_manager_for_config, patch_config, ) +from langgraph._internal._typing import UNSET from langgraph.constants import ( CONF, - CONFIG_KEY_PREVIOUS, - CONFIG_KEY_STORE, - CONFIG_KEY_STREAM_WRITER, + CONFIG_KEY_RUNTIME, ) from langgraph.store.base import BaseStore from langgraph.types import StreamWriter @@ -128,45 +127,52 @@ ANY_TYPE = object() ASYNCIO_ACCEPTS_CONTEXT = sys.version_info >= (3, 11) -# List of keyword arguments that can be injected at runtime from the config object. +# 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. KWARGS_CONFIG_KEYS: tuple[tuple[str, tuple[Any, ...], str, Any], ...] = ( ( - sys.intern("writer"), + "config", + (RunnableConfig, "RunnableConfig", inspect.Parameter.empty), + # for now, use config directly, eventually, will pop off of Runtime + "N/A", + inspect.Parameter.empty, + ), + ( + "writer", (StreamWriter, "StreamWriter", inspect.Parameter.empty), - CONFIG_KEY_STREAM_WRITER, + "stream_writer", lambda _: None, ), ( - # Covers store that is not optional (will raise an error if a store - # cannot be injected). - sys.intern("store"), + "store", ( BaseStore, "BaseStore", inspect.Parameter.empty, ), - CONFIG_KEY_STORE, + "store", inspect.Parameter.empty, ), ( - # Covers store that is optional. Will set to None if not found in config. - sys.intern("store"), + "store", ( Optional[BaseStore], - # Best effort to catch some forward references. - # This will not work for cases like `"Union[None, BaseStore]"`, - # we'll need to re-write logic to use get_type_hints() - # to resolve forward references. "Optional[BaseStore]", ), - CONFIG_KEY_STORE, + "store", None, ), ( - sys.intern("previous"), + "previous", (ANY_TYPE,), - CONFIG_KEY_PREVIOUS, + "previous", + inspect.Parameter.empty, + ), + ( + "runtime", + (ANY_TYPE,), + # we never hit this block, we just inject runtime directly + "N/A", inspect.Parameter.empty, ), ) @@ -174,7 +180,7 @@ KWARGS_CONFIG_KEYS: tuple[tuple[str, tuple[Any, ...], str, Any], ...] = ( config keys, default values and type annotations. Used to configure keyword arguments that can be injected at runtime -from the config object as kwargs to `invoke`, `ainvoke`, `stream` and `astream`. +from the `Runtime` object as kwargs to `invoke`, `ainvoke`, `stream` and `astream`. For a keyword to be injected from the config object, the function signature must contain a kwarg with the same name and a matching type annotation. @@ -182,8 +188,10 @@ must contain a kwarg with the same name and a matching type annotation. Each tuple contains: - the name of the kwarg in the function signature - the type annotation(s) for the kwarg -- the config key to look for the value in -- the default value for the kwarg +- the `Runtime` attribute for fetching the value (N/A if not applicable) + +This is fully internal and should be further refactored to use `get_type_hints` +to resolve forward references and optional types formatted like BaseStore | None. """ VALID_KINDS = (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) @@ -250,7 +258,6 @@ class RunnableCallable(Runnable): trace: bool = True, recurse: bool = True, explode_args: bool = False, - func_accepts_config: bool | None = None, **kwargs: Any, ) -> None: self.name = name @@ -277,31 +284,23 @@ class RunnableCallable(Runnable): if func is None and afunc is None: raise ValueError("At least one of func or afunc must be provided.") - if func_accepts_config is not None: - self.func_accepts_config = func_accepts_config - self.func_accepts: dict[str, tuple[str, Any]] = {} - else: - params = inspect.signature(cast(Callable, func or afunc)).parameters + self.func_accepts: dict[str, tuple[str, Any]] = {} + params = inspect.signature(cast(Callable, func or afunc)).parameters - self.func_accepts_config = "config" in params - # Mapping from kwarg name to (config key, default value) to be used. - # The default value is used if the config key is not found in the config. - self.func_accepts = {} + for kw, typ, runtime_key, default in KWARGS_CONFIG_KEYS: + p = params.get(kw) - for kw, typ, config_key, default in KWARGS_CONFIG_KEYS: - p = params.get(kw) + if p is None or p.kind not in VALID_KINDS: + # If parameter is not found or is not a valid kind, skip + continue - if p is None or p.kind not in VALID_KINDS: - # If parameter is not found or is not a valid kind, skip - continue + if typ != (ANY_TYPE,) and p.annotation not in typ: + # A specific type is required, but the function annotation does + # not match the expected type. + continue - if typ != (ANY_TYPE,) and p.annotation not in typ: - # A specific type is required, but the function annotation does - # not match the expected type. - continue - - # If the kwarg is accepted by the function, store the default value - self.func_accepts[kw] = (config_key, default) + # If the kwarg is accepted by the function, store the key / runtime attribute to inject + self.func_accepts[kw] = (runtime_key, default) def __repr__(self) -> str: repr_args = { @@ -328,25 +327,33 @@ class RunnableCallable(Runnable): else: args = (input,) kwargs = {**self.kwargs, **kwargs} - if self.func_accepts_config: - kwargs["config"] = config - _conf = config[CONF] - for kw, (config_key, default_value) in self.func_accepts.items(): + runtime = config[CONF].get(CONFIG_KEY_RUNTIME) + + for kw, (runtime_key, default) in self.func_accepts.items(): # If the kwarg is already set, use the set value if kw in kwargs: continue - if ( - # If the kwarg is requested, but isn't in the config AND has no - # default value, raise an error - config_key not in _conf and default_value is inspect.Parameter.empty - ): - raise ValueError( - f"Missing required config key '{config_key}' for '{self.name}'." - ) + kw_value: Any = UNSET + if kw == "config": + kw_value = config + elif runtime: + if kw == "runtime": + kw_value = runtime + else: + try: + kw_value = getattr(runtime, runtime_key) + except AttributeError: + pass - kwargs[kw] = _conf.get(config_key, default_value) + if kw_value is UNSET: + if default is inspect.Parameter.empty: + raise ValueError( + f"Missing required config key '{runtime_key}' for '{self.name}'." + ) + kw_value = default + kwargs[kw] = kw_value if self.trace: callback_manager = get_callback_manager_for_config(config, self.tags) @@ -392,23 +399,33 @@ class RunnableCallable(Runnable): else: args = (input,) kwargs = {**self.kwargs, **kwargs} - if self.func_accepts_config: - kwargs["config"] = config - _conf = config[CONF] - for kw, (config_key, default_value) in self.func_accepts.items(): + + runtime = config[CONF].get(CONFIG_KEY_RUNTIME) + + for kw, (runtime_key, default) in self.func_accepts.items(): # If the kwarg has already been set, use the set value if kw in kwargs: continue - if ( - # If the kwarg is requested, but isn't in the config AND has no - # default value, raise an error - config_key not in _conf and default_value is inspect.Parameter.empty - ): - raise ValueError( - f"Missing required config key '{config_key}' for '{self.name}'." - ) - kwargs[kw] = _conf.get(config_key, default_value) + kw_value: Any = UNSET + if kw == "config": + kw_value = config + elif runtime: + if kw == "runtime": + kw_value = runtime + else: + try: + kw_value = getattr(runtime, runtime_key) + except AttributeError: + pass + if kw_value is UNSET: + if default is inspect.Parameter.empty: + raise ValueError( + f"Missing required config key '{runtime_key}' for '{self.name}'." + ) + kw_value = default + kwargs[kw] = kw_value + if self.trace: callback_manager = get_async_callback_manager_for_config(config, self.tags) run_manager = await callback_manager.on_chain_start( diff --git a/libs/langgraph/langgraph/_internal/_runtime.py b/libs/langgraph/langgraph/_internal/_runtime.py new file mode 100644 index 000000000..d3296e523 --- /dev/null +++ b/libs/langgraph/langgraph/_internal/_runtime.py @@ -0,0 +1,39 @@ +"""Internal utilities for the Runtime class.""" + +from __future__ import annotations + +from dataclasses import replace +from typing import Any, cast + +from typing_extensions import TypedDict, Unpack + +from langgraph.runtime import Runtime +from langgraph.store.base import BaseStore +from langgraph.types import StreamWriter + + +class RuntimePatch(TypedDict, total=False): + """Patch structure for the Runtime class.""" + + context: Any + store: BaseStore | None + stream_writer: StreamWriter + previous: Any + + +def patch_runtime(runtime: Runtime, **overrides: Unpack[RuntimePatch]) -> Runtime: + """Patch the runtime with the given overrides, returning a new instance.""" + return replace(runtime, **overrides) + + +def patch_runtime_non_null( + runtime: Runtime, **overrides: Unpack[RuntimePatch] +) -> Runtime: + """Patch the runtime with the given overrides, returning a new instance. + + Only patch fields with overrides that are not None. + """ + return replace( + runtime, + **cast(dict[str, Any], {k: v for k, v in overrides.items() if v is not None}), + ) diff --git a/libs/langgraph/langgraph/config.py b/libs/langgraph/langgraph/config.py index b2ef57cfb..d5f9db8fb 100644 --- a/libs/langgraph/langgraph/config.py +++ b/libs/langgraph/langgraph/config.py @@ -5,7 +5,7 @@ from typing import Any from langchain_core.runnables import RunnableConfig from langchain_core.runnables.config import var_child_runnable_config -from langgraph.constants import CONF, CONFIG_KEY_STORE, CONFIG_KEY_STREAM_WRITER +from langgraph.constants import CONF, CONFIG_KEY_RUNTIME from langgraph.store.base import BaseStore from langgraph.types import StreamWriter @@ -114,8 +114,7 @@ def get_store() -> BaseStore: 3 ``` """ - config = get_config() - return config[CONF][CONFIG_KEY_STORE] + return get_config()[CONF][CONFIG_KEY_RUNTIME].store def get_stream_writer() -> StreamWriter: @@ -181,5 +180,5 @@ def get_stream_writer() -> StreamWriter: {'custom_data': 'Hello!'} ``` """ - config = get_config() - return config[CONF].get(CONFIG_KEY_STREAM_WRITER, _no_op_stream_writer) + runtime = get_config()[CONF][CONFIG_KEY_RUNTIME] + return runtime.stream_writer diff --git a/libs/langgraph/langgraph/constants.py b/libs/langgraph/langgraph/constants.py index 7ba6f24f3..94ed8646e 100644 --- a/libs/langgraph/langgraph/constants.py +++ b/libs/langgraph/langgraph/constants.py @@ -79,10 +79,6 @@ CONFIG_KEY_CHECKPOINTER = sys.intern("__pregel_checkpointer") # holds a `BaseCheckpointSaver` passed from parent graph to child graphs CONFIG_KEY_STREAM = sys.intern("__pregel_stream") # holds a `StreamProtocol` passed from parent graph to child graphs -CONFIG_KEY_STREAM_WRITER = sys.intern("__pregel_stream_writer") -# holds a `StreamWriter` for stream_mode=custom -CONFIG_KEY_STORE = sys.intern("__pregel_store") -# holds a `BaseStore` made available to managed values CONFIG_KEY_CACHE = sys.intern("__pregel_cache") # holds a `BaseCache` made available to subgraphs CONFIG_KEY_RESUMING = sys.intern("__pregel_resuming") @@ -101,12 +97,12 @@ CONFIG_KEY_NODE_FINISHED = sys.intern("__pregel_node_finished") # holds a callback to be called when a node is finished CONFIG_KEY_SCRATCHPAD = sys.intern("__pregel_scratchpad") # holds a mutable dict for temporary storage scoped to the current task -CONFIG_KEY_PREVIOUS = sys.intern("__pregel_previous") -# holds the previous return value from a stateful Pregel graph. CONFIG_KEY_RUNNER_SUBMIT = sys.intern("__pregel_runner_submit") # holds a function that receives tasks from runner, executes them and returns results CONFIG_KEY_CHECKPOINT_DURING = sys.intern("__pregel_checkpoint_during") # holds a boolean indicating whether to checkpoint during the run (or only at the end) +CONFIG_KEY_RUNTIME = sys.intern("__pregel_runtime") +# holds a `Runtime` instance with context, store, stream writer, etc. # --- Other constants --- PUSH = sys.intern("__pregel_push") @@ -137,8 +133,7 @@ RESERVED = { CONFIG_KEY_READ, CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_STREAM, - CONFIG_KEY_STREAM_WRITER, - CONFIG_KEY_STORE, + CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_RESUMING, CONFIG_KEY_TASK_ID, CONFIG_KEY_CHECKPOINT_MAP, diff --git a/libs/langgraph/langgraph/func/__init__.py b/libs/langgraph/langgraph/func/__init__.py index 90d43c191..f31b82948 100644 --- a/libs/langgraph/langgraph/func/__init__.py +++ b/libs/langgraph/langgraph/func/__init__.py @@ -12,6 +12,7 @@ from typing import ( Callable, Generic, TypeVar, + cast, get_args, get_origin, overload, @@ -38,7 +39,8 @@ from langgraph.pregel._read import PregelNode from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry from langgraph.store.base import BaseStore from langgraph.types import _DC_KWARGS, CachePolicy, RetryPolicy, StreamMode -from langgraph.warnings import LangGraphDeprecatedSinceV05 +from langgraph.typing import ContextT +from langgraph.warnings import LangGraphDeprecatedSinceV05, LangGraphDeprecatedSinceV10 __all__ = ("task", "entrypoint") @@ -215,7 +217,7 @@ S = TypeVar("S") # In this form, the `final` attribute should play nicely with IDE autocompletion, # and type checking tools. # In addition, we'll be able to surface this information in the API Reference. -class entrypoint: +class entrypoint(Generic[ContextT]): """Define a LangGraph workflow using the `entrypoint` decorator. ### Function signature @@ -231,10 +233,9 @@ class entrypoint: | Parameter | Description | |------------------|----------------------------------------------------------------------------------------------------| - | **`store`** | An instance of [BaseStore][langgraph.store.base.BaseStore]. Useful for long-term memory. | - | **`writer`** | A [StreamWriter][langgraph.types.StreamWriter] instance for writing custom data to a stream. | | **`config`** | A configuration object (aka RunnableConfig) that holds run-time configuration values. | | **`previous`** | The previous return value for the given thread (available only when a checkpointer is provided). | + | **`runtime`** | A Runtime object that contains information about the current run, including context, store, writer | | The entrypoint decorator can be applied to sync functions or async functions. @@ -254,7 +255,7 @@ class entrypoint: store: A generalized key-value store. Some implementations may support semantic search capabilities through an optional `index` configuration. cache: A cache to use for caching the results of the workflow. - config_schema: Specifies the schema for the configuration object that will be + context_schema: Specifies the schema for the context object that will be passed to the workflow. cache_policy: A cache policy to use for caching the results of the workflow. retry_policy: A retry policy (or list of policies) to use for the workflow in case of a failure. @@ -376,26 +377,35 @@ class entrypoint: checkpointer: BaseCheckpointSaver | None = None, store: BaseStore | None = None, cache: BaseCache | None = None, - config_schema: type[Any] | None = None, + context_schema: type[ContextT] | None = None, cache_policy: CachePolicy | None = None, retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None, **kwargs: Unpack[DeprecatedKwargs], ) -> None: """Initialize the entrypoint decorator.""" + if (config_schema := kwargs.get("config_schema", UNSET)) is not UNSET: + warnings.warn( + "`config_schema` is deprecated and will be removed. Please use `context_schema` instead.", + category=LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + if context_schema is None: + context_schema = cast(type[ContextT], config_schema) + if (retry := kwargs.get("retry", UNSET)) is not UNSET: warnings.warn( "`retry` is deprecated and will be removed. Please use `retry_policy` instead.", category=LangGraphDeprecatedSinceV05, ) if retry_policy is None: - retry_policy = retry # type: ignore[assignment] + retry_policy = cast("RetryPolicy | Sequence[RetryPolicy]", retry) self.checkpointer = checkpointer self.store = store self.cache = cache self.cache_policy = cache_policy self.retry_policy = retry_policy - self.config_schema = config_schema + self.context_schema = context_schema @dataclass(**_DC_KWARGS) class final(Generic[R, S]): @@ -527,5 +537,5 @@ class entrypoint: cache=self.cache, cache_policy=self.cache_policy, retry_policy=self.retry_policy or (), - config_type=self.config_schema, + context_schema=self.context_schema, # type: ignore[arg-type] ) diff --git a/libs/langgraph/langgraph/graph/_branch.py b/libs/langgraph/langgraph/graph/_branch.py index 90bb68b88..34ff58a61 100644 --- a/libs/langgraph/langgraph/graph/_branch.py +++ b/libs/langgraph/langgraph/graph/_branch.py @@ -134,7 +134,6 @@ class BranchSpec(NamedTuple): reader=reader, name=None, trace=False, - func_accepts_config=True, ), list( zip_longest( diff --git a/libs/langgraph/langgraph/graph/_node.py b/libs/langgraph/langgraph/graph/_node.py index 54f9a1fab..48ecf683b 100644 --- a/libs/langgraph/langgraph/graph/_node.py +++ b/libs/langgraph/langgraph/graph/_node.py @@ -9,9 +9,10 @@ from langchain_core.runnables import Runnable, RunnableConfig from typing_extensions import TypeAlias from langgraph.constants import EMPTY_SEQ +from langgraph.runtime import Runtime from langgraph.store.base import BaseStore from langgraph.types import CachePolicy, RetryPolicy, StreamWriter -from langgraph.typing import NodeInputT, NodeInputT_contra +from langgraph.typing import ContextT, NodeInputT, NodeInputT_contra _DC_SLOTS = {"slots": True} if sys.version_info >= (3, 10) else {} @@ -61,6 +62,12 @@ class _NodeWithConfigWriterStore(Protocol[NodeInputT_contra]): ) -> Any: ... +class _NodeWithRuntime(Protocol[NodeInputT_contra, ContextT]): + def __call__( + self, state: NodeInputT_contra, *, runtime: Runtime[ContextT] + ) -> Any: ... + + # TODO: we probably don't want to explicitly support the config / store signatures once # we move to adding a context arg. Maybe what we do is we add support for kwargs with param spec # this is purely for typing purposes though, so can easily change in the coming weeks. @@ -73,13 +80,14 @@ StateNode: TypeAlias = Union[ _NodeWithConfigWriter[NodeInputT], _NodeWithConfigStore[NodeInputT], _NodeWithConfigWriterStore[NodeInputT], + _NodeWithRuntime[NodeInputT, ContextT], Runnable[NodeInputT, Any], ] @dataclass(**_DC_SLOTS) -class StateNodeSpec(Generic[NodeInputT]): - runnable: StateNode[NodeInputT] +class StateNodeSpec(Generic[NodeInputT, ContextT]): + runnable: StateNode[NodeInputT, ContextT] metadata: dict[str, Any] | None input_schema: type[NodeInputT] retry_policy: RetryPolicy | Sequence[RetryPolicy] | None diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index f6bda0dd6..78d33a804 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -2,6 +2,7 @@ from __future__ import annotations import inspect import logging +import sys import typing import warnings from collections import defaultdict @@ -83,8 +84,13 @@ from langgraph.types import ( RetryPolicy, Send, ) -from langgraph.typing import InputT, NodeInputT, OutputT, StateT -from langgraph.warnings import LangGraphDeprecatedSinceV05 +from langgraph.typing import ContextT, InputT, NodeInputT, OutputT, StateT +from langgraph.warnings import LangGraphDeprecatedSinceV05, LangGraphDeprecatedSinceV10 + +if sys.version_info < (3, 10): + NoneType = type(None) +else: + from types import NoneType as NoneType __all__ = ("StateGraph", "CompiledStateGraph") @@ -105,14 +111,14 @@ def _warn_invalid_state_schema(schema: type[Any] | Any) -> None: ) -def _get_node_name(node: StateNode) -> str: +def _get_node_name(node: StateNode[Any, ContextT]) -> str: try: return getattr(node, "__name__", node.__class__.__name__) except AttributeError: raise TypeError(f"Unsupported node type: {type(node)}") -class StateGraph(Generic[StateT, InputT, OutputT]): +class StateGraph(Generic[StateT, ContextT, InputT, OutputT]): """A graph whose nodes communicate by reading and writing to a shared state. The signature of each node is State -> Partial. @@ -122,8 +128,10 @@ class StateGraph(Generic[StateT, InputT, OutputT]): Args: state_schema: The schema class that defines the state. - config_schema: The schema class that defines the configuration. - Use this to expose configurable parameters in your API. + context_schema: The schema class that defines the runtime context. + Use this to expose immutable context data to your nodes, like user_id, db_conn, etc. + input_schema: The schema class that defines the input to the graph. + output_schema: The schema class that defines the output from the graph. Example: ```python @@ -131,6 +139,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): from typing_extensions import Annotated, TypedDict from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import StateGraph + from langgraph.rumtime import Runtime def reducer(a: list, b: int | None) -> list: if b is not None: @@ -140,13 +149,13 @@ class StateGraph(Generic[StateT, InputT, OutputT]): class State(TypedDict): x: Annotated[list, reducer] - class ConfigSchema(TypedDict): + class Context(TypedDict): r: float - graph = StateGraph(State, config_schema=ConfigSchema) + graph = StateGraph(state_schema=State, context_schema=Context) - def node(state: State, config: RunnableConfig) -> dict: - r = config["configurable"].get("r", 1.0) + def node(state: State, runtime: Runtime[Context]) -> dict: + r = runtie.context.get("r", 1.0) x = state["x"][-1] next_value = x * r * (1 - x) return {"x": next_value} @@ -156,16 +165,13 @@ class StateGraph(Generic[StateT, InputT, OutputT]): graph.set_finish_point("A") compiled = graph.compile() - print(compiled.config_specs) - # [ConfigurableFieldSpec(id='r', annotation=, name=None, description=None, default=None, is_shared=False, dependencies=None)] - - step1 = compiled.invoke({"x": 0.5}, {"configurable": {"r": 3.0}}) + step1 = compiled.invoke({"x": 0.5}, context={"r": 3.0}) # {'x': [0.5, 0.75]} ``` """ edges: set[tuple[str, str]] - nodes: dict[str, StateNodeSpec] + nodes: dict[str, StateNodeSpec[Any, ContextT]] branches: defaultdict[str, dict[str, BranchSpec]] channels: dict[str, BaseChannel] managed: dict[str, ManagedValueSpec] @@ -174,18 +180,28 @@ class StateGraph(Generic[StateT, InputT, OutputT]): compiled: bool state_schema: type[StateT] + context_schema: type[ContextT] | None input_schema: type[InputT] output_schema: type[OutputT] def __init__( self, state_schema: type[StateT], - config_schema: type[Any] | None = None, + context_schema: type[ContextT] | None = None, *, input_schema: type[InputT] | None = None, output_schema: type[OutputT] | None = None, **kwargs: Unpack[DeprecatedKwargs], ) -> None: + if (config_schema := kwargs.get("config_schema", UNSET)) is not UNSET: + warnings.warn( + "`config_schema` is deprecated and will be removed. Please use `context_schema` instead.", + category=LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + if context_schema is None: + context_schema = cast(type[ContextT], config_schema) + if (input_ := kwargs.get("input", UNSET)) is not UNSET: warnings.warn( "`input` is deprecated and will be removed. Please use `input_schema` instead.", @@ -193,7 +209,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): stacklevel=2, ) if input_schema is None: - input_schema = cast(Union[type[InputT], None], input_) + input_schema = cast(type[InputT], input_) if (output := kwargs.get("output", UNSET)) is not UNSET: warnings.warn( @@ -202,7 +218,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): stacklevel=2, ) if output_schema is None: - output_schema = cast(Union[type[OutputT], None], output) + output_schema = cast(type[OutputT], output) self.nodes = {} self.edges = set() @@ -216,7 +232,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): self.state_schema = state_schema self.input_schema = cast(type[InputT], input_schema or state_schema) self.output_schema = cast(type[OutputT], output_schema or state_schema) - self.config_schema = config_schema + self.context_schema = context_schema self._add_schema(self.state_schema) self._add_schema(self.input_schema, allow_managed=False) @@ -263,7 +279,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): @overload def add_node( self, - node: StateNode[StateT], + node: StateNode[NodeInputT, ContextT], *, defer: bool = False, metadata: dict[str, Any] | None = None, @@ -281,7 +297,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): @overload def add_node( self, - node: StateNode[NodeInputT], + node: StateNode[NodeInputT, ContextT], *, defer: bool = False, metadata: dict[str, Any] | None = None, @@ -300,7 +316,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): def add_node( self, node: str, - action: StateNode[StateT], + action: StateNode[NodeInputT, ContextT], *, defer: bool = False, metadata: dict[str, Any] | None = None, @@ -316,8 +332,8 @@ class StateGraph(Generic[StateT, InputT, OutputT]): @overload def add_node( self, - node: str, - action: StateNode[NodeInputT], + node: str | StateNode[NodeInputT, ContextT], + action: StateNode[NodeInputT, ContextT] | None = None, *, defer: bool = False, metadata: dict[str, Any] | None = None, @@ -332,8 +348,8 @@ class StateGraph(Generic[StateT, InputT, OutputT]): def add_node( self, - node: str | StateNode[StateT] | StateNode[NodeInputT], - action: StateNode[StateT] | StateNode[NodeInputT] | None = None, + node: str | StateNode[NodeInputT, ContextT], + action: StateNode[NodeInputT, ContextT] | None = None, *, defer: bool = False, metadata: dict[str, Any] | None = None, @@ -498,8 +514,8 @@ class StateGraph(Generic[StateT, InputT, OutputT]): ends = destinations if input_schema is not None: - self.nodes[node] = StateNodeSpec[NodeInputT]( - coerce_to_runnable(action, name=node, trace=False), + self.nodes[node] = StateNodeSpec[NodeInputT, ContextT]( + coerce_to_runnable(action, name=node, trace=False), # type: ignore[arg-type] metadata, input_schema=input_schema, retry_policy=retry_policy, @@ -509,7 +525,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): ) elif inferred_input_schema is not None: self.nodes[node] = StateNodeSpec( - coerce_to_runnable(action, name=node, trace=False), + coerce_to_runnable(action, name=node, trace=False), # type: ignore[arg-type] metadata, input_schema=inferred_input_schema, retry_policy=retry_policy, @@ -518,8 +534,8 @@ class StateGraph(Generic[StateT, InputT, OutputT]): defer=defer, ) else: - self.nodes[node] = StateNodeSpec[StateT]( - coerce_to_runnable(action, name=node, trace=False), + self.nodes[node] = StateNodeSpec[StateT, ContextT]( + coerce_to_runnable(action, name=node, trace=False), # type: ignore[arg-type] metadata, input_schema=self.state_schema, retry_policy=retry_policy, @@ -636,7 +652,10 @@ class StateGraph(Generic[StateT, InputT, OutputT]): def add_sequence( self, - nodes: Sequence[StateNode[StateT] | tuple[str, StateNode[StateT]]], + nodes: Sequence[ + StateNode[NodeInputT, ContextT] + | tuple[str, StateNode[NodeInputT, ContextT]] + ], ) -> Self: """Add a sequence of nodes that will be executed in the provided order. @@ -782,7 +801,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]): interrupt_after: All | list[str] | None = None, debug: bool = False, name: str | None = None, - ) -> CompiledStateGraph[StateT, InputT, OutputT]: + ) -> CompiledStateGraph[StateT, ContextT, InputT, OutputT]: """Compiles the state graph into a `CompiledStateGraph` object. The compiled graph implements the `Runnable` interface and can be invoked, @@ -834,10 +853,10 @@ class StateGraph(Generic[StateT, InputT, OutputT]): ] ) - compiled = CompiledStateGraph[StateT, InputT, OutputT]( + compiled = CompiledStateGraph[StateT, ContextT, InputT, OutputT]( builder=self, schema_to_mapper={}, - config_type=self.config_schema, + context_schema=self.context_schema, nodes={}, channels={ **self.channels, @@ -876,15 +895,16 @@ class StateGraph(Generic[StateT, InputT, OutputT]): class CompiledStateGraph( - Pregel[StateT, InputT, OutputT], Generic[StateT, InputT, OutputT] + Pregel[StateT, ContextT, InputT, OutputT], + Generic[StateT, ContextT, InputT, OutputT], ): - builder: StateGraph[StateT, InputT, OutputT] + builder: StateGraph[StateT, ContextT, InputT, OutputT] schema_to_mapper: dict[type[Any], Callable[[Any], Any] | None] def __init__( self, *, - builder: StateGraph[StateT, InputT, OutputT], + builder: StateGraph[StateT, ContextT, InputT, OutputT], schema_to_mapper: dict[type[Any], Callable[[Any], Any] | None], **kwargs: Any, ) -> None: @@ -912,7 +932,7 @@ class CompiledStateGraph( name=self.get_name("Output"), ) - def attach_node(self, key: str, node: StateNodeSpec | None) -> None: + def attach_node(self, key: str, node: StateNodeSpec[Any, ContextT] | None) -> None: if key == START: output_keys = [ k diff --git a/libs/langgraph/langgraph/pregel/_algo.py b/libs/langgraph/langgraph/pregel/_algo.py index 270dfebdb..14ff02fd4 100644 --- a/libs/langgraph/langgraph/pregel/_algo.py +++ b/libs/langgraph/langgraph/pregel/_algo.py @@ -26,6 +26,7 @@ from langchain_core.runnables.config import RunnableConfig from xxhash import xxh3_128_hexdigest from langgraph._internal._config import merge_configs, patch_config +from langgraph._internal._runtime import patch_runtime_non_null from langgraph.channels.base import BaseChannel from langgraph.channels.topic import Topic from langgraph.checkpoint.base import ( @@ -42,12 +43,11 @@ from langgraph.constants import ( CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_CHECKPOINTER, - CONFIG_KEY_PREVIOUS, CONFIG_KEY_READ, CONFIG_KEY_RESUME_MAP, + CONFIG_KEY_RUNTIME, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_SEND, - CONFIG_KEY_STORE, CONFIG_KEY_TASK_ID, EMPTY_SEQ, ERROR, @@ -72,6 +72,7 @@ from langgraph.pregel._io import read_channels from langgraph.pregel._log import logger from langgraph.pregel._read import INPUT_CACHE_KEY_TYPE, PregelNode from langgraph.pregel._scratchpad import PregelScratchpad +from langgraph.runtime import DEFAULT_RUNTIME from langgraph.store.base import BaseStore from langgraph.types import ( All, @@ -583,6 +584,10 @@ def prepare_single_task( step, stop, ) + runtime = patch_runtime_non_null( + configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME), + store=store, + ) return PregelExecutableTask( name, call.input, @@ -604,7 +609,6 @@ def prepare_single_task( managed, PregelTaskWrites(task_path, name, writes, triggers), ), - CONFIG_KEY_STORE: (store or configurable.get(CONFIG_KEY_STORE)), CONFIG_KEY_CHECKPOINTER: ( checkpointer or configurable.get(CONFIG_KEY_CHECKPOINTER) ), @@ -615,6 +619,7 @@ def prepare_single_task( CONFIG_KEY_CHECKPOINT_ID: None, CONFIG_KEY_CHECKPOINT_NS: task_checkpoint_ns, CONFIG_KEY_SCRATCHPAD: scratchpad, + CONFIG_KEY_RUNTIME: runtime, }, ), triggers, @@ -709,6 +714,11 @@ def prepare_single_task( step, stop, ) + runtime = patch_runtime_non_null( + configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME), + store=store, + previous=checkpoint["channel_values"].get(PREVIOUS, None), + ) return PregelExecutableTask( packet.node, packet.arg, @@ -731,7 +741,6 @@ def prepare_single_task( managed, PregelTaskWrites(task_path, packet.node, writes, triggers), ), - CONFIG_KEY_STORE: (store or configurable.get(CONFIG_KEY_STORE)), CONFIG_KEY_CHECKPOINTER: ( checkpointer or configurable.get(CONFIG_KEY_CHECKPOINTER) ), @@ -742,9 +751,7 @@ def prepare_single_task( CONFIG_KEY_CHECKPOINT_ID: None, CONFIG_KEY_CHECKPOINT_NS: task_checkpoint_ns, CONFIG_KEY_SCRATCHPAD: scratchpad, - CONFIG_KEY_PREVIOUS: checkpoint["channel_values"].get( - PREVIOUS, None - ), + CONFIG_KEY_RUNTIME: runtime, }, ), triggers, @@ -846,6 +853,11 @@ def prepare_single_task( ) else: cache_key = None + runtime = patch_runtime_non_null( + configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME), + previous=checkpoint["channel_values"].get(PREVIOUS, None), + store=store, + ) return PregelExecutableTask( name, val, @@ -877,9 +889,6 @@ def prepare_single_task( triggers, ), ), - CONFIG_KEY_STORE: ( - store or configurable.get(CONFIG_KEY_STORE) - ), CONFIG_KEY_CHECKPOINTER: ( checkpointer or configurable.get(CONFIG_KEY_CHECKPOINTER) @@ -891,9 +900,7 @@ def prepare_single_task( CONFIG_KEY_CHECKPOINT_ID: None, CONFIG_KEY_CHECKPOINT_NS: task_checkpoint_ns, CONFIG_KEY_SCRATCHPAD: scratchpad, - CONFIG_KEY_PREVIOUS: checkpoint["channel_values"].get( - PREVIOUS, None - ), + CONFIG_KEY_RUNTIME: runtime, }, ), triggers, diff --git a/libs/langgraph/langgraph/pregel/_read.py b/libs/langgraph/langgraph/pregel/_read.py index ea73d4d63..a3edf2c31 100644 --- a/libs/langgraph/langgraph/pregel/_read.py +++ b/libs/langgraph/langgraph/pregel/_read.py @@ -46,7 +46,6 @@ class ChannelRead(RunnableCallable): tags=tags, name=None, trace=False, - func_accepts_config=True, ) self.fresh = fresh self.mapper = mapper diff --git a/libs/langgraph/langgraph/pregel/_write.py b/libs/langgraph/langgraph/pregel/_write.py index 56dceb9d4..d16de35a2 100644 --- a/libs/langgraph/langgraph/pregel/_write.py +++ b/libs/langgraph/langgraph/pregel/_write.py @@ -64,7 +64,6 @@ class ChannelWrite(RunnableCallable): name=None, tags=tags, trace=False, - func_accepts_config=True, ) self.writes = cast( list[Union[ChannelWriteEntry, ChannelWriteTupleEntry, Send]], writes diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index b165c3318..1d7324f65 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -4,10 +4,13 @@ import asyncio import concurrent import concurrent.futures import queue +import warnings import weakref from collections import defaultdict, deque from collections.abc import AsyncIterator, Iterator, Mapping, Sequence +from dataclasses import is_dataclass from functools import partial +from inspect import isclass from typing import Any, Callable, Generic, Union, cast, get_type_hints from uuid import UUID, uuid5 @@ -22,8 +25,8 @@ from langchain_core.runnables.config import ( get_callback_manager_for_config, ) from langchain_core.runnables.graph import Graph -from pydantic import BaseModel -from typing_extensions import Self +from pydantic import BaseModel, TypeAdapter +from typing_extensions import Self, Unpack, deprecated, is_typeddict from langgraph._internal._config import ( ensure_config, @@ -44,6 +47,7 @@ from langgraph._internal._runnable import ( RunnableSeq, coerce_to_runnable, ) +from langgraph._internal._typing import DeprecatedKwargs from langgraph.cache.base import BaseCache from langgraph.channels.base import BaseChannel from langgraph.channels.topic import Topic @@ -64,10 +68,9 @@ from langgraph.constants import ( CONFIG_KEY_NODE_FINISHED, CONFIG_KEY_READ, CONFIG_KEY_RUNNER_SUBMIT, + CONFIG_KEY_RUNTIME, CONFIG_KEY_SEND, - CONFIG_KEY_STORE, CONFIG_KEY_STREAM, - CONFIG_KEY_STREAM_WRITER, CONFIG_KEY_TASK_ID, CONFIG_KEY_THREAD_ID, END, @@ -113,6 +116,7 @@ from langgraph.pregel._validate import validate_graph, validate_keys from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry from langgraph.pregel.debug import get_bolded_text, get_colored_text, tasks_w_writes from langgraph.pregel.protocol import PregelProtocol, StreamChunk, StreamProtocol +from langgraph.runtime import Runtime from langgraph.store.base import BaseStore from langgraph.types import ( All, @@ -125,7 +129,8 @@ from langgraph.types import ( StateUpdate, StreamMode, ) -from langgraph.typing import InputT, OutputT, StateT +from langgraph.typing import ContextT, InputT, OutputT, StateT +from langgraph.warnings import LangGraphDeprecatedSinceV10 try: from langchain_core.tracers._streaming import _StreamingCallbackHandler @@ -299,7 +304,10 @@ class NodeBuilder: ) -class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, OutputT]): +class Pregel( + PregelProtocol[StateT, ContextT, InputT, OutputT], + Generic[StateT, ContextT, InputT, OutputT], +): """Pregel manages the runtime behavior for LangGraph applications. ## Overview @@ -592,7 +600,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou """Cache policy to use for all nodes. Can be overridden by individual nodes. Defaults to None.""" - config_type: type[Any] | None = None + context_schema: type[ContextT] | None = None config: RunnableConfig | None = None @@ -620,11 +628,22 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou cache: BaseCache | None = None, retry_policy: RetryPolicy | Sequence[RetryPolicy] = (), cache_policy: CachePolicy | None = None, - config_type: type[Any] | None = None, + context_schema: type[ContextT] | None = None, config: RunnableConfig | None = None, trigger_to_nodes: Mapping[str, Sequence[str]] | None = None, name: str = "LangGraph", + **deprecated_kwargs: Unpack[DeprecatedKwargs], ) -> None: + if config_type := deprecated_kwargs.get("config_type"): + warnings.warn( + "`config_type` is deprecated and will be removed. Please use `context_schema` instead.", + category=LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + + if context_schema is None: + context_schema = cast(type[ContextT], config_type) + self.nodes = { k: v.build() if isinstance(v, NodeBuilder) else v for k, v in nodes.items() } @@ -651,7 +670,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou (retry_policy,) if isinstance(retry_policy, RetryPolicy) else retry_policy ) self.cache_policy = cache_policy - self.config_type = config_type + self.context_schema = context_schema self.config = config self.trigger_to_nodes = trigger_to_nodes or {} self.name = name @@ -760,10 +779,23 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou self.trigger_to_nodes = _trigger_to_nodes(self.nodes) return self + @deprecated( + "`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead." + ) def config_schema(self, *, include: Sequence[str] | None = None) -> type[BaseModel]: + warnings.warn( + "`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead.", + category=LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + include = include or [] fields = { - **({"configurable": (self.config_type, None)} if self.config_type else {}), + **( + {"configurable": (self.context_schema, None)} + if self.context_schema + else {} + ), **{ field_name: (field_type, None) for field_name, field_type in get_type_hints(RunnableConfig).items() @@ -772,12 +804,36 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou } return create_model(self.get_name("Config"), field_definitions=fields) + @deprecated( + "`get_config_jsonschema` is deprecated. Use `get_context_jsonschema` instead." + ) def get_config_jsonschema( self, *, include: Sequence[str] | None = None ) -> dict[str, Any]: - schema = self.config_schema(include=include) + warnings.warn( + "`get_config_jsonschema` is deprecated. Use `get_context_jsonschema` instead.", + category=LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=LangGraphDeprecatedSinceV10) + schema = self.config_schema(include=include) return schema.model_json_schema() + def get_context_jsonschema(self) -> dict[str, Any] | None: + if (context_schema := self.context_schema) is None: + return None + + if isclass(context_schema) and issubclass(context_schema, BaseModel): + return context_schema.model_json_schema() + elif is_typeddict(context_schema) or is_dataclass(context_schema): + return TypeAdapter(context_schema).json_schema() + else: + raise ValueError( + f"Invalid context schema type: {context_schema}. Must be a BaseModel, TypedDict or dataclass." + ) + @property def InputType(self) -> Any: if isinstance(self.input_channels, str): @@ -2327,8 +2383,8 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou "Checkpointer requires one or more of the following 'configurable' " "keys: thread_id, checkpoint_ns, checkpoint_id" ) - if CONFIG_KEY_STORE in config.get(CONF, {}): - store: BaseStore | None = config[CONF][CONFIG_KEY_STORE] + if CONFIG_KEY_RUNTIME in config.get(CONF, {}): + store: BaseStore | None = config[CONF][CONFIG_KEY_RUNTIME].store else: store = self.store if CONFIG_KEY_CACHE in config.get(CONF, {}): @@ -2350,6 +2406,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, stream_mode: StreamMode | Sequence[StreamMode] | None = None, print_mode: StreamMode | Sequence[StreamMode] = (), output_keys: str | Sequence[str] | None = None, @@ -2446,28 +2503,39 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou run_manager.inheritable_handlers.append( StreamMessagesHandler(stream.put, subgraphs) ) + # set up custom stream mode if "custom" in stream_modes: - config[CONF][CONFIG_KEY_STREAM_WRITER] = lambda c: stream.put( - ( - tuple( - get_config()[CONF][CONFIG_KEY_CHECKPOINT_NS].split(NS_SEP)[ - :-1 - ] - ), - "custom", - c, + + def stream_writer(c: Any) -> None: + stream.put( + ( + tuple( + get_config()[CONF][CONFIG_KEY_CHECKPOINT_NS].split( + NS_SEP + )[:-1] + ), + "custom", + c, + ) ) - ) - elif ( - CONFIG_KEY_STREAM not in config[CONF] - and CONFIG_KEY_STREAM_WRITER in config[CONF] - ): - # remove parent graph stream writer if subgraph streaming not requested - del config[CONF][CONFIG_KEY_STREAM_WRITER] + elif CONFIG_KEY_STREAM in config[CONF]: + stream_writer = config[CONF][CONFIG_KEY_RUNTIME].stream_writer + else: + + def stream_writer(c: Any) -> None: + pass + # set checkpointing mode for subgraphs if checkpoint_during is not None: config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during + + config[CONF][CONFIG_KEY_RUNTIME] = Runtime( + context=context, + store=store, + stream_writer=stream_writer, + previous=None, + ) with SyncPregelLoop( input, stream=StreamProtocol(stream.put, stream_modes), @@ -2572,6 +2640,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, stream_mode: StreamMode | Sequence[StreamMode] | None = None, print_mode: StreamMode | Sequence[StreamMode] = (), output_keys: str | Sequence[str] | None = None, @@ -2686,10 +2755,26 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou run_manager.inheritable_handlers.append( StreamMessagesHandler(stream_put, subgraphs) ) + # set up custom stream mode + def stream_writer(c: Any) -> None: + aioloop.call_soon_threadsafe( + stream.put_nowait, + ( + tuple( + get_config()[CONF][CONFIG_KEY_CHECKPOINT_NS].split(NS_SEP)[ + :-1 + ] + ), + "custom", + c, + ), + ) + if "custom" in stream_modes: - config[CONF][CONFIG_KEY_STREAM_WRITER] = ( - lambda c: aioloop.call_soon_threadsafe( + + def stream_writer(c: Any) -> None: + aioloop.call_soon_threadsafe( stream.put_nowait, ( tuple( @@ -2701,16 +2786,23 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou c, ), ) - ) - elif ( - CONFIG_KEY_STREAM not in config[CONF] - and CONFIG_KEY_STREAM_WRITER in config[CONF] - ): - # remove parent graph stream writer if subgraph streaming not requested - del config[CONF][CONFIG_KEY_STREAM_WRITER] + elif CONFIG_KEY_STREAM in config[CONF]: + stream_writer = config[CONF][CONFIG_KEY_RUNTIME].stream_writer + else: + + def stream_writer(c: Any) -> None: + pass + # set checkpointing mode for subgraphs if checkpoint_during is not None: config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during + + config[CONF][CONFIG_KEY_RUNTIME] = Runtime( + context=context, + store=store, + stream_writer=stream_writer, + previous=None, + ) async with AsyncPregelLoop( input, stream=StreamProtocol(stream.put_nowait, stream_modes), @@ -2816,6 +2908,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, stream_mode: StreamMode = "values", print_mode: StreamMode | Sequence[StreamMode] = (), output_keys: str | Sequence[str] | None = None, @@ -2848,6 +2941,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou for chunk in self.stream( input, config, + context=context, stream_mode=["updates", "values"] if stream_mode == "values" else stream_mode, @@ -2891,6 +2985,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, stream_mode: StreamMode = "values", print_mode: StreamMode | Sequence[StreamMode] = (), output_keys: str | Sequence[str] | None = None, @@ -2924,6 +3019,7 @@ class Pregel(PregelProtocol[StateT, InputT, OutputT], Generic[StateT, InputT, Ou async for chunk in self.astream( input, config, + context=context, stream_mode=["updates", "values"] if stream_mode == "values" else stream_mode, diff --git a/libs/langgraph/langgraph/pregel/protocol.py b/libs/langgraph/langgraph/pregel/protocol.py index 2dba937c9..5b5f83c70 100644 --- a/libs/langgraph/langgraph/pregel/protocol.py +++ b/libs/langgraph/langgraph/pregel/protocol.py @@ -9,12 +9,12 @@ from langchain_core.runnables.graph import Graph as DrawableGraph from typing_extensions import Self from langgraph.types import All, Command, StateSnapshot, StateUpdate, StreamMode -from langgraph.typing import InputT, OutputT, StateT +from langgraph.typing import ContextT, InputT, OutputT, StateT __all__ = ("PregelProtocol", "StreamProtocol") -class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT]): +class PregelProtocol(Runnable[InputT, Any], Generic[StateT, ContextT, InputT, OutputT]): @abstractmethod def with_config( self, config: RunnableConfig | None = None, **kwargs: Any @@ -102,6 +102,7 @@ class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT]): input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, stream_mode: StreamMode | list[StreamMode] | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, @@ -114,6 +115,7 @@ class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT]): input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, stream_mode: StreamMode | list[StreamMode] | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, @@ -126,6 +128,7 @@ class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT]): input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, ) -> dict[str, Any] | Any: ... @@ -136,6 +139,7 @@ class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT]): input: InputT | Command | None, config: RunnableConfig | None = None, *, + context: ContextT | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, ) -> dict[str, Any] | Any: ... diff --git a/libs/langgraph/langgraph/runtime.py b/libs/langgraph/langgraph/runtime.py new file mode 100644 index 000000000..cfaa8dbe9 --- /dev/null +++ b/libs/langgraph/langgraph/runtime.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Generic, cast + +from langgraph.config import get_config +from langgraph.constants import CONF, CONFIG_KEY_RUNTIME +from langgraph.store.base import BaseStore +from langgraph.types import _DC_KWARGS, StreamWriter +from langgraph.typing import ContextT + + +def _no_op_stream_writer(_: Any) -> None: ... + + +@dataclass(**_DC_KWARGS) +class Runtime(Generic[ContextT]): + """Convenience class that bundles run-scoped context and graph configuration. + + !!! version-added "Added in version 1.0.0." + """ + + context: ContextT + """Static context for the graph run, like user_id, db_conn, etc. + + Can also be thought of as 'run dependencies'.""" + + store: BaseStore | None + """Store for the graph run, enabling persistence and memory.""" + + stream_writer: StreamWriter + """Function that writes to the custom stream.""" + + previous: Any | None + """The previous return value for the given thread. + + Only available with the functional API when a checkpointer is provided.""" + + +DEFAULT_RUNTIME = Runtime( + context=None, + store=None, + stream_writer=_no_op_stream_writer, + previous=None, +) + + +def get_runtime(context_schema: type[ContextT] | None = None) -> Runtime[ContextT]: + """Get the runtime for the current graph run.""" + + # TODO: in an ideal world, we would have a context manager for + # the runtime that's independent of the config. this will follow + # from the removal of the configurable packing + runtime = cast(Runtime[ContextT], get_config()[CONF].get(CONFIG_KEY_RUNTIME)) + return runtime diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 11016c536..69489bd86 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -1,9 +1,9 @@ from __future__ import annotations -import dataclasses import sys from collections import deque from collections.abc import Hashable, Sequence +from dataclasses import asdict, dataclass from typing import ( TYPE_CHECKING, Any, @@ -122,7 +122,7 @@ class RetryPolicy(NamedTuple): KeyFuncT = TypeVar("KeyFuncT", bound=Callable[..., Union[str, bytes]]) -@dataclasses.dataclass(**_DC_KWARGS) +@dataclass(**_DC_KWARGS) class CachePolicy(Generic[KeyFuncT]): """Configuration for caching nodes.""" @@ -138,7 +138,7 @@ _DEFAULT_INTERRUPT_ID = "placeholder-id" @final -@dataclasses.dataclass(init=False, **_DC_SLOTS) +@dataclass(init=False, **_DC_SLOTS) class Interrupt: """Information about an interrupt that occurred in a node. @@ -218,7 +218,7 @@ class CacheKey(NamedTuple): """Time to live for the cache entry in seconds.""" -@dataclasses.dataclass(**_T_DC_KWARGS) +@dataclass(**_T_DC_KWARGS) class PregelExecutableTask: name: str input: Any @@ -329,7 +329,7 @@ class Send: N = TypeVar("N", bound=Hashable) -@dataclasses.dataclass(**_DC_KWARGS) +@dataclass(**_DC_KWARGS) class Command(Generic[N], ToolOutputMixin): """One or more commands to update the graph's state and send messages to nodes. @@ -362,9 +362,7 @@ class Command(Generic[N], ToolOutputMixin): def __repr__(self) -> str: # get all non-None values contents = ", ".join( - f"{key}={value!r}" - for key, value in dataclasses.asdict(self).items() - if value + f"{key}={value!r}" for key, value in asdict(self).items() if value ) return f"Command({contents})" diff --git a/libs/langgraph/langgraph/typing.py b/libs/langgraph/langgraph/typing.py index bfddc9ae5..c3ba65939 100644 --- a/libs/langgraph/langgraph/typing.py +++ b/libs/langgraph/langgraph/typing.py @@ -12,6 +12,7 @@ __all__ = ( "StateT_contra", "InputT", "OutputT", + "ContextT", ) StateT = TypeVar("StateT", bound=StateLike) @@ -21,15 +22,29 @@ StateT_co = TypeVar("StateT_co", bound=StateLike, covariant=True) StateT_contra = TypeVar("StateT_contra", bound=StateLike, contravariant=True) +ContextT = TypeVar("ContextT", bound=Union[StateLike, None], default=None) +"""Type variable used to represent graph run scoped context. + +Defaults to `None`. +""" + +ContextT_contra = TypeVar( + "ContextT_contra", bound=Union[StateLike, None], contravariant=True, default=None +) + InputT = TypeVar("InputT", bound=StateLike, default=StateT) """Type variable used to represent the input to a state graph. Defaults to `StateT`. """ -OutputT = TypeVar("OutputT", bound=Union[StateLike, None], default=StateT) -"""Type variable used to represent the output of a state graph.""" +OutputT = TypeVar("OutputT", bound=StateLike, default=StateT) +"""Type variable used to represent the output of a state graph. + +Defaults to `StateT`. +""" NodeInputT = TypeVar("NodeInputT", bound=StateLike) +"""Type variable used to represent the input to a node.""" NodeInputT_contra = TypeVar("NodeInputT_contra", bound=StateLike, contravariant=True) diff --git a/libs/langgraph/tests/__snapshots__/test_pregel.ambr b/libs/langgraph/tests/__snapshots__/test_pregel.ambr index c1c17b29d..5167d8924 100644 --- a/libs/langgraph/tests/__snapshots__/test_pregel.ambr +++ b/libs/langgraph/tests/__snapshots__/test_pregel.ambr @@ -825,7 +825,7 @@ ''' # --- # name: test_state_graph_w_config_inherited_state_keys - '{"$defs": {"Config": {"properties": {"tools": {"items": {"type": "string"}, "title": "Tools", "type": "array"}}, "title": "Config", "type": "object"}}, "properties": {"configurable": {"$ref": "#/$defs/Config", "default": null}}, "title": "LangGraphConfig", "type": "object"}' + '{"properties": {"tools": {"items": {"type": "string"}, "title": "Tools", "type": "array"}}, "title": "Context", "type": "object"}' # --- # name: test_state_graph_w_config_inherited_state_keys.1 '{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"additionalProperties": true, "title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "title": "Agent Outcome"}, "intermediate_steps": {"items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "required": ["input", "agent_outcome"], "title": "AgentState", "type": "object"}' diff --git a/libs/langgraph/tests/test_deprecation.py b/libs/langgraph/tests/test_deprecation.py index bd592f922..dee208c4b 100644 --- a/libs/langgraph/tests/test_deprecation.py +++ b/libs/langgraph/tests/test_deprecation.py @@ -1,9 +1,12 @@ import pytest +from pytest_mock import MockerFixture from typing_extensions import TypedDict +from langgraph.channels.last_value import LastValue from langgraph.errors import NodeInterrupt from langgraph.func import entrypoint, task from langgraph.graph import StateGraph +from langgraph.pregel import NodeBuilder, Pregel from langgraph.types import Interrupt, RetryPolicy from langgraph.warnings import LangGraphDeprecatedSinceV05, LangGraphDeprecatedSinceV10 @@ -83,7 +86,7 @@ def test_constants_deprecation() -> None: from langgraph.constants import Interrupt # noqa: F401 -def test_pregel_deprecation() -> None: +def test_pregel_types_deprecation() -> None: with pytest.warns( LangGraphDeprecatedSinceV10, match="Importing from langgraph.pregel.types is deprecated. Please use 'from langgraph.types import ...' instead.", @@ -91,6 +94,54 @@ def test_pregel_deprecation() -> None: from langgraph.pregel.types import StateSnapshot # noqa: F401 +@pytest.mark.filterwarnings("ignore:`config_schema` is deprecated") +@pytest.mark.filterwarnings("ignore:`get_config_jsonschema` is deprecated") +def test_config_schema_deprecation() -> None: + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="`config_schema` is deprecated and will be removed. Please use `context_schema` instead.", + ): + builder = StateGraph(PlainState, config_schema=PlainState) + + builder.add_node("test_node", lambda state: state) + builder.set_entry_point("test_node") + graph = builder.compile() + + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead.", + ): + graph.config_schema() + + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="`get_config_jsonschema` is deprecated. Use `get_context_jsonschema` instead.", + ): + graph.get_config_jsonschema() + + +def test_config_type_deprecation_pregel(mocker: MockerFixture) -> None: + add_one = mocker.Mock(side_effect=lambda x: x + 1) + chain = NodeBuilder().subscribe_only("input").do(add_one).write_to("output") + + with pytest.warns( + LangGraphDeprecatedSinceV10, + match="`config_type` is deprecated and will be removed. Please use `context_schema` instead.", + ): + Pregel( + nodes={ + "one": chain, + }, + channels={ + "input": LastValue(int), + "output": LastValue(int), + }, + input_channels="input", + output_channels="output", + config_type=PlainState, + ) + + def test_interrupt_attributes_deprecation() -> None: interrupt = Interrupt(value="question", id="abc") diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index f89c727ae..a64f843da 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -7,7 +7,6 @@ import operator import threading import time import uuid -import warnings from collections import Counter, deque from collections.abc import Sequence from concurrent.futures import ThreadPoolExecutor @@ -190,7 +189,7 @@ def test_checkpoint_errors() -> None: ) -def test_config_json_schema() -> None: +def test_context_json_schema() -> None: """Test that config json schema is generated properly.""" chain = NodeBuilder().subscribe_only("input").write_to("output") @@ -210,37 +209,25 @@ def test_config_json_schema() -> None: }, input_channels=["input", "ephemeral"], output_channels="output", - config_type=Foo, + context_schema=Foo, ) - assert app.get_config_jsonschema() == { - "$defs": { - "Foo": { - "properties": { - "x": { - "title": "X", - "type": "integer", - }, - "y": { - "default": "foo", - "title": "Y", - "type": "string", - }, - }, - "required": [ - "x", - ], - "title": "Foo", - "type": "object", - }, - }, + assert app.get_context_jsonschema() == { "properties": { - "configurable": { - "$ref": "#/$defs/Foo", - "default": None, + "x": { + "title": "X", + "type": "integer", + }, + "y": { + "default": "foo", + "title": "Y", + "type": "string", }, }, - "title": "LangGraphConfig", + "required": [ + "x", + ], + "title": "Foo", "type": "object", } @@ -423,13 +410,7 @@ def test_invoke_single_process_in_out(mocker: MockerFixture) -> None: "title": "LangGraphOutput", "type": "integer", } - with warnings.catch_warnings(): - warnings.simplefilter("error") # raise warnings as errors - assert app.config_schema().model_json_schema() == { - "properties": {}, - "title": "LangGraphConfig", - "type": "object", - } + assert app.get_context_jsonschema() is None assert app.invoke(2) == 3 assert app.invoke(2, output_keys=["output"]) == {"output": 3} @@ -1227,7 +1208,7 @@ def test_imp_task( ) -> None: mapper_calls = 0 - class Configurable(TypedDict): + class Context(TypedDict): model: str @task() @@ -1237,7 +1218,7 @@ def test_imp_task( time.sleep(input / 100) return str(input) * 2 - @entrypoint(checkpointer=sync_checkpointer, config_schema=Configurable) + @entrypoint(checkpointer=sync_checkpointer, context_schema=Context) def graph(input: list[int]) -> list[str]: futures = [mapper(i) for i in input] mapped = [f.result() for f in futures] @@ -1254,21 +1235,10 @@ def test_imp_task( "items": {"type": "string"}, "title": "LangGraphOutput", } - assert graph.get_config_jsonschema() == { - "$defs": { - "Configurable": { - "properties": { - "model": {"title": "Model", "type": "string"}, - }, - "required": ["model"], - "title": "Configurable", - "type": "object", - } - }, - "properties": { - "configurable": {"$ref": "#/$defs/Configurable", "default": None} - }, - "title": "LangGraphConfig", + assert graph.get_context_jsonschema() == { + "properties": {"model": {"title": "Model", "type": "string"}}, + "required": ["model"], + "title": "Context", "type": "object", } @@ -1770,7 +1740,7 @@ def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion) "intermediate_steps", } - class Config(TypedDict, total=False): + class Context(TypedDict, total=False): tools: list[str] # Assemble the tools @@ -1827,7 +1797,7 @@ def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion) return "continue" # Define a new graph - builder = StateGraph(AgentState, Config) + builder = StateGraph(AgentState, Context) builder.add_node("agent", agent) builder.add_node("tools", execute_tools) @@ -1842,7 +1812,7 @@ def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion) app = builder.compile() - assert json.dumps(app.config_schema().model_json_schema()) == snapshot + assert json.dumps(app.get_context_jsonschema()) == snapshot assert json.dumps(app.get_input_jsonschema()) == snapshot assert json.dumps(app.get_output_jsonschema()) == snapshot diff --git a/libs/langgraph/tests/test_runnable.py b/libs/langgraph/tests/test_runnable.py index 5fb390919..b27f6f625 100644 --- a/libs/langgraph/tests/test_runnable.py +++ b/libs/langgraph/tests/test_runnable.py @@ -5,6 +5,7 @@ from typing import Any, Optional import pytest from langgraph._internal._runnable import RunnableCallable +from langgraph.runtime import Runtime from langgraph.store.base import BaseStore from langgraph.types import StreamWriter @@ -90,7 +91,22 @@ def test_runnable_callable_injectable_arguments() -> None: assert store is None return "success" - assert RunnableCallable(func_optional_store).invoke({"x": "1"}) == "success" + assert ( + RunnableCallable(func_optional_store).invoke( + {"x": "1"}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store=None, + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, + ) + == "success" + ) # Test BaseStore annotation def func_required_store(inputs: Any, store: BaseStore) -> str: @@ -108,7 +124,17 @@ def test_runnable_callable_injectable_arguments() -> None: # Specify a value for store in the config assert ( RunnableCallable(func_required_store).invoke( - {}, config={"configurable": {"__pregel_store": None}} + {}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store=None, + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, ) == "success" ) @@ -118,7 +144,16 @@ def test_runnable_callable_injectable_arguments() -> None: RunnableCallable(func_optional_store).invoke( {"x": "1"}, store=None, - config={"configurable": {"__pregel_store": "foobar"}}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store="foobar", # type: ignore[assignment] + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, ) == "success" ) @@ -134,7 +169,17 @@ def test_runnable_callable_injectable_arguments() -> None: assert ( RunnableCallable(func_required_store_v2).invoke( - {}, config={"configurable": {"__pregel_store": "foobar"}} + {}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store="foobar", # type: ignore[assignment] + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, ) == "success" ) @@ -143,7 +188,16 @@ def test_runnable_callable_injectable_arguments() -> None: # And manual override takes precedence. {}, store="foobar", - config={"configurable": {"__pregel_store": "barbar"}}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store="foobar", # type: ignore[assignment] + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, ) @@ -192,7 +246,9 @@ async def test_runnable_callable_injectable_arguments_async() -> None: assert ( await RunnableCallable( func=func_required_store, afunc=afunc_required_store - ).ainvoke({}) + ).ainvoke( + {}, + ) == "success" ) @@ -200,7 +256,20 @@ async def test_runnable_callable_injectable_arguments_async() -> None: assert ( await RunnableCallable( func=func_required_store, afunc=afunc_required_store - ).ainvoke({}, store=None) + ).ainvoke( + {}, + store=None, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store=None, + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, + ) == "success" ) @@ -208,7 +277,19 @@ async def test_runnable_callable_injectable_arguments_async() -> None: assert ( await RunnableCallable( func=func_required_store, afunc=afunc_required_store - ).ainvoke({}, config={"configurable": {"__pregel_store": None}}) + ).ainvoke( + {}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store=None, + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, + ) == "success" ) @@ -219,7 +300,16 @@ async def test_runnable_callable_injectable_arguments_async() -> None: ).ainvoke( {"x": "1"}, store=None, - config={"configurable": {"__pregel_store": "foobar"}}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store="foobar", + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, ) == "success" ) @@ -244,7 +334,19 @@ async def test_runnable_callable_injectable_arguments_async() -> None: assert ( await RunnableCallable( func=func_required_store_v2, afunc=afunc_required_store_v2 - ).ainvoke({}, config={"configurable": {"__pregel_store": "foobar"}}) + ).ainvoke( + {}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store="foobar", + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, + ) == "success" ) @@ -255,7 +357,16 @@ async def test_runnable_callable_injectable_arguments_async() -> None: # And manual override takes precedence. {}, store="foobar", - config={"configurable": {"__pregel_store": "barbar"}}, + config={ + "configurable": { + "__pregel_runtime": Runtime( + store="foobar", + context=None, + stream_writer=lambda _: None, + previous=None, + ) + } + }, ) == "success" ) diff --git a/libs/langgraph/tests/test_runtime.py b/libs/langgraph/tests/test_runtime.py new file mode 100644 index 000000000..cfbf361b7 --- /dev/null +++ b/libs/langgraph/tests/test_runtime.py @@ -0,0 +1,47 @@ +from dataclasses import dataclass +from typing import Any + +from typing_extensions import TypedDict + +from langgraph.graph import END, START, StateGraph +from langgraph.runtime import Runtime, get_runtime + + +@dataclass +class Context: + api_key: str + + +class State(TypedDict): + message: str + + +def test_injected_runtime() -> None: + def injected_runtime(state: State, runtime: Runtime[Context]) -> dict[str, Any]: + return {"message": f"api key: {runtime.context.api_key}"} + + graph = StateGraph(state_schema=State, context_schema=Context) + graph.add_node("injected_runtime", injected_runtime) + graph.add_edge(START, "injected_runtime") + graph.add_edge("injected_runtime", END) + compiled = graph.compile() + result = compiled.invoke( + {"message": "hello world"}, context=Context(api_key="sk_123456") + ) + assert result == {"message": "api key: sk_123456"} + + +def test_context_runtime() -> None: + def context_runtime(state: State) -> dict[str, Any]: + runtime = get_runtime(Context) + return {"message": f"api key: {runtime.context.api_key}"} + + graph = StateGraph(state_schema=State, context_schema=Context) + graph.add_node("context_runtime", context_runtime) + graph.add_edge(START, "context_runtime") + graph.add_edge("context_runtime", END) + compiled = graph.compile() + result = compiled.invoke( + {"message": "hello world"}, context=Context(api_key="sk_123456") + ) + assert result == {"message": "api key: sk_123456"} diff --git a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py index a6ac2f9d4..06efc8834 100644 --- a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py +++ b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py @@ -11,6 +11,7 @@ from typing import ( cast, get_type_hints, ) +from warnings import warn from langchain_core.language_models import ( BaseChatModel, @@ -35,6 +36,7 @@ from pydantic import BaseModel from typing_extensions import Annotated, TypedDict from langgraph._internal._runnable import RunnableCallable, RunnableLike +from langgraph._internal._typing import UNSET from langgraph.errors import ErrorCode, create_error_message from langgraph.graph import END, StateGraph from langgraph.graph.message import add_messages @@ -43,6 +45,7 @@ from langgraph.managed import IsLastStep, RemainingSteps from langgraph.prebuilt.tool_node import ToolNode from langgraph.store.base import BaseStore from langgraph.types import Checkpointer, Send +from langgraph.warnings import LangGraphDeprecatedSinceV10 StructuredResponse = Union[dict, BaseModel] StructuredResponseSchema = Union[dict, type[BaseModel]] @@ -252,7 +255,7 @@ def create_react_agent( pre_model_hook: Optional[RunnableLike] = None, post_model_hook: Optional[RunnableLike] = None, state_schema: Optional[StateSchemaType] = None, - config_schema: Optional[Type[Any]] = None, + context_schema: Optional[Type[Any]] = None, checkpointer: Optional[Checkpointer] = None, store: Optional[BaseStore] = None, interrupt_before: Optional[list[str]] = None, @@ -260,6 +263,7 @@ def create_react_agent( debug: bool = False, version: Literal["v1", "v2"] = "v2", name: Optional[str] = None, + **deprecated_kwargs: Any, ) -> CompiledStateGraph: """Creates an agent graph that calls tools in a loop until a stopping condition is met. @@ -334,8 +338,7 @@ def create_react_agent( state_schema: An optional state schema that defines graph state. Must have `messages` and `remaining_steps` keys. Defaults to `AgentState` that defines those two keys. - config_schema: An optional schema for configuration. - Use this to expose configurable parameters via agent.config_specs. + context_schema: An optional schema for runtime context. checkpointer: An optional checkpoint saver object. This is used for persisting the state of the graph (e.g., as chat memory) for a single thread (e.g., a single conversation). store: An optional store object. This is used for persisting data @@ -402,6 +405,15 @@ def create_react_agent( print(chunk) ``` """ + if (config_schema := deprecated_kwargs.pop("config_schema", UNSET)) is not UNSET: + warn( + "`config_schema` is no longer supported. Use `context_schema` instead.", + category=LangGraphDeprecatedSinceV10, + ) + + if context_schema is not None: + context_schema = config_schema + if version not in ("v1", "v2"): raise ValueError( f"Invalid version {version}. Supported versions are 'v1' and 'v2'." @@ -590,7 +602,7 @@ def create_react_agent( if not tool_calling_enabled: # Define a new graph - workflow = StateGraph(state_schema, config_schema=config_schema) + workflow = StateGraph(state_schema=state_schema, context_schema=context_schema) workflow.add_node( "agent", RunnableCallable(call_model, acall_model), @@ -657,7 +669,9 @@ def create_react_agent( return [Send("tools", [tool_call]) for tool_call in tool_calls] # Define a new graph - workflow = StateGraph(state_schema or AgentState, config_schema=config_schema) + workflow = StateGraph( + state_schema=state_schema or AgentState, context_schema=context_schema + ) # Define the two nodes we will cycle between workflow.add_node( From d935a2d110b5d4de61be4aa9a9448542ac35755b Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:13:34 -0400 Subject: [PATCH 09/22] refactor(langgraph): move typing constructs in `constants.py` -> `_internal/_typing.py` (#5518) --- libs/langgraph/langgraph/_internal/_fields.py | 4 +--- libs/langgraph/langgraph/_internal/_runnable.py | 10 +++++----- libs/langgraph/langgraph/_internal/_typing.py | 12 ++++++------ libs/langgraph/langgraph/channels/any_value.py | 6 +++++- libs/langgraph/langgraph/channels/base.py | 12 +++++++----- libs/langgraph/langgraph/channels/binop.py | 2 +- .../langgraph/channels/ephemeral_value.py | 7 ++++++- libs/langgraph/langgraph/channels/last_value.py | 15 +++++++++++---- .../langgraph/channels/named_barrier_value.py | 2 +- libs/langgraph/langgraph/channels/topic.py | 2 +- .../langgraph/channels/untracked_value.py | 11 ++++++++--- libs/langgraph/langgraph/constants.py | 4 ---- libs/langgraph/langgraph/func/__init__.py | 8 ++++---- libs/langgraph/langgraph/graph/_node.py | 2 +- libs/langgraph/langgraph/graph/state.py | 14 ++++++-------- libs/langgraph/langgraph/pregel/_algo.py | 3 +-- libs/langgraph/langgraph/pregel/_checkpoint.py | 2 +- libs/langgraph/langgraph/pregel/_io.py | 3 +-- libs/langgraph/langgraph/pregel/_loop.py | 3 +-- libs/langgraph/langgraph/pregel/_runner.py | 2 +- libs/langgraph/langgraph/pregel/_write.py | 3 ++- libs/langgraph/langgraph/pregel/debug.py | 2 +- libs/langgraph/langgraph/types.py | 4 ++-- libs/langgraph/tests/test_channels.py | 2 +- .../langgraph/prebuilt/chat_agent_executor.py | 6 ++++-- 25 files changed, 78 insertions(+), 63 deletions(-) diff --git a/libs/langgraph/langgraph/_internal/_fields.py b/libs/langgraph/langgraph/_internal/_fields.py index 5b9c8dca6..6979678d1 100644 --- a/libs/langgraph/langgraph/_internal/_fields.py +++ b/libs/langgraph/langgraph/_internal/_fields.py @@ -9,9 +9,7 @@ from typing import Annotated, Any, Optional, Union, get_type_hints from pydantic import BaseModel from typing_extensions import NotRequired, ReadOnly, Required, get_origin -# NOTE: this is redefined here separately from langgraph.constants -# to avoid a circular import -MISSING = object() +from langgraph._internal._typing import MISSING def _is_optional_type(type_: Any) -> bool: diff --git a/libs/langgraph/langgraph/_internal/_runnable.py b/libs/langgraph/langgraph/_internal/_runnable.py index 9a4b76ea0..22d1b0515 100644 --- a/libs/langgraph/langgraph/_internal/_runnable.py +++ b/libs/langgraph/langgraph/_internal/_runnable.py @@ -48,7 +48,7 @@ from langgraph._internal._config import ( get_callback_manager_for_config, patch_config, ) -from langgraph._internal._typing import UNSET +from langgraph._internal._typing import MISSING from langgraph.constants import ( CONF, CONFIG_KEY_RUNTIME, @@ -335,7 +335,7 @@ class RunnableCallable(Runnable): if kw in kwargs: continue - kw_value: Any = UNSET + kw_value: Any = MISSING if kw == "config": kw_value = config elif runtime: @@ -347,7 +347,7 @@ class RunnableCallable(Runnable): except AttributeError: pass - if kw_value is UNSET: + if kw_value is MISSING: if default is inspect.Parameter.empty: raise ValueError( f"Missing required config key '{runtime_key}' for '{self.name}'." @@ -407,7 +407,7 @@ class RunnableCallable(Runnable): if kw in kwargs: continue - kw_value: Any = UNSET + kw_value: Any = MISSING if kw == "config": kw_value = config elif runtime: @@ -418,7 +418,7 @@ class RunnableCallable(Runnable): kw_value = getattr(runtime, runtime_key) except AttributeError: pass - if kw_value is UNSET: + if kw_value is MISSING: if default is inspect.Parameter.empty: raise ValueError( f"Missing required config key '{runtime_key}' for '{self.name}'." diff --git a/libs/langgraph/langgraph/_internal/_typing.py b/libs/langgraph/langgraph/_internal/_typing.py index 79b5478d0..02adb3364 100644 --- a/libs/langgraph/langgraph/_internal/_typing.py +++ b/libs/langgraph/langgraph/_internal/_typing.py @@ -42,13 +42,13 @@ It can either be a `TypedDict`, `dataclass`, or Pydantic `BaseModel`. Note: we cannot use either `TypedDict` or `dataclass` directly due to limitations in type checking. """ - -class Unset: - """A sentinel value to represent an unset type.""" - - -UNSET: Unset = Unset() +MISSING = object() +"""Unset sentinel value.""" class DeprecatedKwargs(TypedDict): """TypedDict to use for extra keyword arguments, enabling type checking warnings for deprecated arguments.""" + + +EMPTY_SEQ: tuple[str, ...] = tuple() +"""An empty sequence of strings.""" diff --git a/libs/langgraph/langgraph/channels/any_value.py b/libs/langgraph/langgraph/channels/any_value.py index 18b008a34..9ba255574 100644 --- a/libs/langgraph/langgraph/channels/any_value.py +++ b/libs/langgraph/langgraph/channels/any_value.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from collections.abc import Sequence from typing import Any, Generic from typing_extensions import Self +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value -from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError __all__ = ("AnyValue",) @@ -16,6 +18,8 @@ class AnyValue(Generic[Value], BaseChannel[Value, Value, Value]): __slots__ = ("typ", "value") + value: Value | Any + def __init__(self, typ: Any, key: str = "") -> None: super().__init__(typ, key) self.value = MISSING diff --git a/libs/langgraph/langgraph/channels/base.py b/libs/langgraph/langgraph/channels/base.py index 5f6ae3f1a..2d00da64f 100644 --- a/libs/langgraph/langgraph/channels/base.py +++ b/libs/langgraph/langgraph/channels/base.py @@ -1,20 +1,22 @@ +from __future__ import annotations + from abc import ABC, abstractmethod from collections.abc import Sequence from typing import Any, Generic, TypeVar from typing_extensions import Self -from langgraph.constants import MISSING +from langgraph._internal._typing import MISSING from langgraph.errors import EmptyChannelError Value = TypeVar("Value") Update = TypeVar("Update") -C = TypeVar("C") +Checkpoint = TypeVar("Checkpoint") __all__ = ("BaseChannel",) -class BaseChannel(Generic[Value, Update, C], ABC): +class BaseChannel(Generic[Value, Update, Checkpoint], ABC): """Base class for all channels.""" __slots__ = ("key", "typ") @@ -41,7 +43,7 @@ class BaseChannel(Generic[Value, Update, C], ABC): Subclasses can override this method with a more efficient implementation.""" return self.from_checkpoint(self.checkpoint()) - def checkpoint(self) -> C: + def checkpoint(self) -> Checkpoint | Any: """Return a serializable representation of the channel's current state. Raises EmptyChannelError if the channel is empty (never updated yet), or doesn't support checkpoints.""" @@ -51,7 +53,7 @@ class BaseChannel(Generic[Value, Update, C], ABC): return MISSING @abstractmethod - def from_checkpoint(self, checkpoint: C) -> Self: + def from_checkpoint(self, checkpoint: Checkpoint | Any) -> Self: """Return a new identical channel, optionally initialized from a checkpoint. If the checkpoint contains complex data structures, they should be copied.""" diff --git a/libs/langgraph/langgraph/channels/binop.py b/libs/langgraph/langgraph/channels/binop.py index 6b34b5533..d47c4e049 100644 --- a/libs/langgraph/langgraph/channels/binop.py +++ b/libs/langgraph/langgraph/channels/binop.py @@ -4,8 +4,8 @@ from typing import Callable, Generic from typing_extensions import NotRequired, Required, Self +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value -from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError __all__ = ("BinaryOperatorAggregate",) diff --git a/libs/langgraph/langgraph/channels/ephemeral_value.py b/libs/langgraph/langgraph/channels/ephemeral_value.py index 98d4f41dd..108588d0b 100644 --- a/libs/langgraph/langgraph/channels/ephemeral_value.py +++ b/libs/langgraph/langgraph/channels/ephemeral_value.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from collections.abc import Sequence from typing import Any, Generic from typing_extensions import Self +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value -from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError, InvalidUpdateError __all__ = ("EphemeralValue",) @@ -15,6 +17,9 @@ class EphemeralValue(Generic[Value], BaseChannel[Value, Value, Value]): __slots__ = ("value", "guard") + value: Value | Any + guard: bool + def __init__(self, typ: Any, guard: bool = True) -> None: super().__init__(typ) self.guard = guard diff --git a/libs/langgraph/langgraph/channels/last_value.py b/libs/langgraph/langgraph/channels/last_value.py index 1c07fc7ab..54caac758 100644 --- a/libs/langgraph/langgraph/channels/last_value.py +++ b/libs/langgraph/langgraph/channels/last_value.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from collections.abc import Sequence from typing import Any, Generic from typing_extensions import Self +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value -from langgraph.constants import MISSING from langgraph.errors import ( EmptyChannelError, ErrorCode, @@ -20,6 +22,8 @@ class LastValue(Generic[Value], BaseChannel[Value, Value, Value]): __slots__ = ("value",) + value: Value | Any + def __init__(self, typ: Any, key: str = "") -> None: super().__init__(typ, key) self.value = MISSING @@ -82,6 +86,9 @@ class LastValueAfterFinish( __slots__ = ("value", "finished") + value: Value | Any + finished: bool + def __init__(self, typ: Any, key: str = "") -> None: super().__init__(typ, key) self.value = MISSING @@ -100,19 +107,19 @@ class LastValueAfterFinish( """The type of the update received by the channel.""" return self.typ - def checkpoint(self) -> tuple[Value, bool]: + def checkpoint(self) -> tuple[Value | Any, bool] | Any: if self.value is MISSING: return MISSING return (self.value, self.finished) - def from_checkpoint(self, checkpoint: tuple[Value, bool]) -> Self: + def from_checkpoint(self, checkpoint: tuple[Value | Any, bool] | Any) -> Self: empty = self.__class__(self.typ) empty.key = self.key if checkpoint is not MISSING: empty.value, empty.finished = checkpoint return empty - def update(self, values: Sequence[Value]) -> bool: + def update(self, values: Sequence[Value | Any]) -> bool: if len(values) == 0: return False diff --git a/libs/langgraph/langgraph/channels/named_barrier_value.py b/libs/langgraph/langgraph/channels/named_barrier_value.py index 7f9c8baa0..d45644110 100644 --- a/libs/langgraph/langgraph/channels/named_barrier_value.py +++ b/libs/langgraph/langgraph/channels/named_barrier_value.py @@ -3,8 +3,8 @@ from typing import Generic from typing_extensions import Self +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value -from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError, InvalidUpdateError __all__ = ("NamedBarrierValue", "NamedBarrierValueAfterFinish") diff --git a/libs/langgraph/langgraph/channels/topic.py b/libs/langgraph/langgraph/channels/topic.py index 4b9113570..917798ff2 100644 --- a/libs/langgraph/langgraph/channels/topic.py +++ b/libs/langgraph/langgraph/channels/topic.py @@ -5,8 +5,8 @@ from typing import Any, Generic, Union from typing_extensions import Self +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value -from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError __all__ = ("Topic",) diff --git a/libs/langgraph/langgraph/channels/untracked_value.py b/libs/langgraph/langgraph/channels/untracked_value.py index e339920dc..bcd55186b 100644 --- a/libs/langgraph/langgraph/channels/untracked_value.py +++ b/libs/langgraph/langgraph/channels/untracked_value.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from collections.abc import Sequence -from typing import Generic +from typing import Any, Generic from typing_extensions import Self +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value -from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError, InvalidUpdateError __all__ = ("UntrackedValue",) @@ -15,6 +17,9 @@ class UntrackedValue(Generic[Value], BaseChannel[Value, Value, Value]): __slots__ = ("value", "guard") + guard: bool + value: Value | Any + def __init__(self, typ: type[Value], guard: bool = True) -> None: super().__init__(typ) self.guard = guard @@ -40,7 +45,7 @@ class UntrackedValue(Generic[Value], BaseChannel[Value, Value, Value]): empty.value = self.value return empty - def checkpoint(self) -> Value: + def checkpoint(self) -> Value | Any: return MISSING def from_checkpoint(self, checkpoint: Value) -> Self: diff --git a/libs/langgraph/langgraph/constants.py b/libs/langgraph/langgraph/constants.py index 94ed8646e..726f15e45 100644 --- a/libs/langgraph/langgraph/constants.py +++ b/libs/langgraph/langgraph/constants.py @@ -31,10 +31,6 @@ def __getattr__(name: str) -> Any: raise AttributeError(f"module has no attribute '{name}'") -# --- Empty read-only containers --- -EMPTY_SEQ: tuple[str, ...] = tuple() -MISSING = object() - # --- Public constants --- TAG_NOSTREAM = sys.intern("nostream") """Tag to disable streaming for a chat model.""" diff --git a/libs/langgraph/langgraph/func/__init__.py b/libs/langgraph/langgraph/func/__init__.py index f31b82948..d1e6d07bf 100644 --- a/libs/langgraph/langgraph/func/__init__.py +++ b/libs/langgraph/langgraph/func/__init__.py @@ -20,7 +20,7 @@ from typing import ( from typing_extensions import Unpack -from langgraph._internal._typing import UNSET, DeprecatedKwargs +from langgraph._internal._typing import MISSING, DeprecatedKwargs from langgraph.cache.base import BaseCache from langgraph.channels.ephemeral_value import EphemeralValue from langgraph.channels.last_value import LastValue @@ -180,7 +180,7 @@ def task( await add_one.ainvoke([1, 2, 3]) # Returns [2, 3, 4] ``` """ - if (retry := kwargs.get("retry", UNSET)) is not UNSET: + if (retry := kwargs.get("retry", MISSING)) is not MISSING: warnings.warn( "`retry` is deprecated and will be removed. Please use `retry_policy` instead.", category=LangGraphDeprecatedSinceV05, @@ -383,7 +383,7 @@ class entrypoint(Generic[ContextT]): **kwargs: Unpack[DeprecatedKwargs], ) -> None: """Initialize the entrypoint decorator.""" - if (config_schema := kwargs.get("config_schema", UNSET)) is not UNSET: + if (config_schema := kwargs.get("config_schema", MISSING)) is not MISSING: warnings.warn( "`config_schema` is deprecated and will be removed. Please use `context_schema` instead.", category=LangGraphDeprecatedSinceV10, @@ -392,7 +392,7 @@ class entrypoint(Generic[ContextT]): if context_schema is None: context_schema = cast(type[ContextT], config_schema) - if (retry := kwargs.get("retry", UNSET)) is not UNSET: + if (retry := kwargs.get("retry", MISSING)) is not MISSING: warnings.warn( "`retry` is deprecated and will be removed. Please use `retry_policy` instead.", category=LangGraphDeprecatedSinceV05, diff --git a/libs/langgraph/langgraph/graph/_node.py b/libs/langgraph/langgraph/graph/_node.py index 48ecf683b..a21f14de5 100644 --- a/libs/langgraph/langgraph/graph/_node.py +++ b/libs/langgraph/langgraph/graph/_node.py @@ -8,7 +8,7 @@ from typing import Any, Generic, Protocol, Union from langchain_core.runnables import Runnable, RunnableConfig from typing_extensions import TypeAlias -from langgraph.constants import EMPTY_SEQ +from langgraph._internal._typing import EMPTY_SEQ from langgraph.runtime import Runtime from langgraph.store.base import BaseStore from langgraph.types import CachePolicy, RetryPolicy, StreamWriter diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 78d33a804..a4654a8c2 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -34,7 +34,7 @@ from langgraph._internal._fields import ( ) from langgraph._internal._pydantic import create_model from langgraph._internal._runnable import coerce_to_runnable -from langgraph._internal._typing import UNSET, DeprecatedKwargs +from langgraph._internal._typing import EMPTY_SEQ, MISSING, DeprecatedKwargs from langgraph.cache.base import BaseCache from langgraph.channels.base import BaseChannel from langgraph.channels.binop import BinaryOperatorAggregate @@ -46,10 +46,8 @@ from langgraph.channels.named_barrier_value import ( ) from langgraph.checkpoint.base import Checkpoint from langgraph.constants import ( - EMPTY_SEQ, END, INTERRUPT, - MISSING, NS_END, NS_SEP, START, @@ -193,7 +191,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]): output_schema: type[OutputT] | None = None, **kwargs: Unpack[DeprecatedKwargs], ) -> None: - if (config_schema := kwargs.get("config_schema", UNSET)) is not UNSET: + if (config_schema := kwargs.get("config_schema", MISSING)) is not MISSING: warnings.warn( "`config_schema` is deprecated and will be removed. Please use `context_schema` instead.", category=LangGraphDeprecatedSinceV10, @@ -202,7 +200,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]): if context_schema is None: context_schema = cast(type[ContextT], config_schema) - if (input_ := kwargs.get("input", UNSET)) is not UNSET: + if (input_ := kwargs.get("input", MISSING)) is not MISSING: warnings.warn( "`input` is deprecated and will be removed. Please use `input_schema` instead.", category=LangGraphDeprecatedSinceV05, @@ -211,7 +209,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]): if input_schema is None: input_schema = cast(type[InputT], input_) - if (output := kwargs.get("output", UNSET)) is not UNSET: + if (output := kwargs.get("output", MISSING)) is not MISSING: warnings.warn( "`output` is deprecated and will be removed. Please use `output_schema` instead.", category=LangGraphDeprecatedSinceV05, @@ -412,7 +410,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]): Returns: Self: The instance of the state graph, allowing for method chaining. """ - if (retry := kwargs.get("retry", UNSET)) is not UNSET: + if (retry := kwargs.get("retry", MISSING)) is not MISSING: warnings.warn( "`retry` is deprecated and will be removed. Please use `retry_policy` instead.", category=LangGraphDeprecatedSinceV05, @@ -420,7 +418,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]): if retry_policy is None: retry_policy = retry # type: ignore[assignment] - if (input_ := kwargs.get("input", UNSET)) is not UNSET: + if (input_ := kwargs.get("input", MISSING)) is not MISSING: warnings.warn( "`input` is deprecated and will be removed. Please use `input_schema` instead.", category=LangGraphDeprecatedSinceV05, diff --git a/libs/langgraph/langgraph/pregel/_algo.py b/libs/langgraph/langgraph/pregel/_algo.py index 14ff02fd4..c813a6805 100644 --- a/libs/langgraph/langgraph/pregel/_algo.py +++ b/libs/langgraph/langgraph/pregel/_algo.py @@ -27,6 +27,7 @@ from xxhash import xxh3_128_hexdigest from langgraph._internal._config import merge_configs, patch_config from langgraph._internal._runtime import patch_runtime_non_null +from langgraph._internal._typing import EMPTY_SEQ, MISSING from langgraph.channels.base import BaseChannel from langgraph.channels.topic import Topic from langgraph.checkpoint.base import ( @@ -49,10 +50,8 @@ from langgraph.constants import ( CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_SEND, CONFIG_KEY_TASK_ID, - EMPTY_SEQ, ERROR, INTERRUPT, - MISSING, NO_WRITES, NS_END, NS_SEP, diff --git a/libs/langgraph/langgraph/pregel/_checkpoint.py b/libs/langgraph/langgraph/pregel/_checkpoint.py index b404ee550..50eb254b8 100644 --- a/libs/langgraph/langgraph/pregel/_checkpoint.py +++ b/libs/langgraph/langgraph/pregel/_checkpoint.py @@ -3,10 +3,10 @@ from __future__ import annotations from collections.abc import Mapping from datetime import datetime, timezone +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import Checkpoint from langgraph.checkpoint.base.id import uuid6 -from langgraph.constants import MISSING from langgraph.managed.base import ManagedValueMapping, ManagedValueSpec LATEST_VERSION = 4 diff --git a/libs/langgraph/langgraph/pregel/_io.py b/libs/langgraph/langgraph/pregel/_io.py index 3eff58c8a..a5af16c23 100644 --- a/libs/langgraph/langgraph/pregel/_io.py +++ b/libs/langgraph/langgraph/pregel/_io.py @@ -4,12 +4,11 @@ from collections import Counter from collections.abc import Iterator, Mapping, Sequence from typing import Any, Literal +from langgraph._internal._typing import EMPTY_SEQ, MISSING from langgraph.channels.base import BaseChannel, EmptyChannelError from langgraph.constants import ( - EMPTY_SEQ, ERROR, INTERRUPT, - MISSING, NULL_TASK_ID, RESUME, RETURN, diff --git a/libs/langgraph/langgraph/pregel/_loop.py b/libs/langgraph/langgraph/pregel/_loop.py index 7dc2ec5dc..7b1663d15 100644 --- a/libs/langgraph/langgraph/pregel/_loop.py +++ b/libs/langgraph/langgraph/pregel/_loop.py @@ -28,6 +28,7 @@ from langchain_core.runnables import RunnableConfig from typing_extensions import ParamSpec, Self from langgraph._internal._config import patch_configurable +from langgraph._internal._typing import EMPTY_SEQ, MISSING from langgraph.cache.base import BaseCache from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import ( @@ -50,11 +51,9 @@ from langgraph.constants import ( CONFIG_KEY_STREAM, CONFIG_KEY_TASK_ID, CONFIG_KEY_THREAD_ID, - EMPTY_SEQ, ERROR, INPUT, INTERRUPT, - MISSING, NS_END, NS_SEP, NULL_TASK_ID, diff --git a/libs/langgraph/langgraph/pregel/_runner.py b/libs/langgraph/langgraph/pregel/_runner.py index 9c29eabca..835525a76 100644 --- a/libs/langgraph/langgraph/pregel/_runner.py +++ b/libs/langgraph/langgraph/pregel/_runner.py @@ -20,13 +20,13 @@ from typing import ( from langchain_core.callbacks import Callbacks from langgraph._internal._future import chain_future, run_coroutine_threadsafe +from langgraph._internal._typing import MISSING from langgraph.constants import ( CONF, CONFIG_KEY_CALL, CONFIG_KEY_SCRATCHPAD, ERROR, INTERRUPT, - MISSING, NO_WRITES, RESUME, RETURN, diff --git a/libs/langgraph/langgraph/pregel/_write.py b/libs/langgraph/langgraph/pregel/_write.py index d16de35a2..dcefb2a36 100644 --- a/libs/langgraph/langgraph/pregel/_write.py +++ b/libs/langgraph/langgraph/pregel/_write.py @@ -14,7 +14,8 @@ from typing import ( from langchain_core.runnables import Runnable, RunnableConfig from langgraph._internal._runnable import RunnableCallable -from langgraph.constants import CONF, CONFIG_KEY_SEND, MISSING, TASKS +from langgraph._internal._typing import MISSING +from langgraph.constants import CONF, CONFIG_KEY_SEND, TASKS from langgraph.errors import InvalidUpdateError from langgraph.types import Send diff --git a/libs/langgraph/langgraph/pregel/debug.py b/libs/langgraph/langgraph/pregel/debug.py index 0ccc168d0..aeaf99ad7 100644 --- a/libs/langgraph/langgraph/pregel/debug.py +++ b/libs/langgraph/langgraph/pregel/debug.py @@ -9,6 +9,7 @@ from langchain_core.runnables import RunnableConfig from typing_extensions import TypedDict from langgraph._internal._config import patch_checkpoint_map +from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import CheckpointMetadata, PendingWrite from langgraph.constants import ( @@ -16,7 +17,6 @@ from langgraph.constants import ( CONFIG_KEY_CHECKPOINT_NS, ERROR, INTERRUPT, - MISSING, NS_END, NS_SEP, RETURN, diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 69489bd86..95c2d708d 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -25,7 +25,7 @@ from xxhash import xxh3_128_hexdigest from langgraph._internal._cache import default_cache_key from langgraph._internal._fields import get_cached_annotated_keys, get_update_as_tuples from langgraph._internal._retry import default_retry_on -from langgraph._internal._typing import UNSET, DeprecatedKwargs +from langgraph._internal._typing import MISSING, DeprecatedKwargs from langgraph.checkpoint.base import BaseCheckpointSaver, CheckpointMetadata from langgraph.warnings import LangGraphDeprecatedSinceV10 @@ -157,7 +157,7 @@ class Interrupt: self.value = value if ( - (ns := deprecated_kwargs.get("ns", UNSET)) is not UNSET + (ns := deprecated_kwargs.get("ns", MISSING)) is not MISSING and (id == _DEFAULT_INTERRUPT_ID) and (isinstance(ns, Sequence)) ): diff --git a/libs/langgraph/tests/test_channels.py b/libs/langgraph/tests/test_channels.py index c8d679ab8..76254c504 100644 --- a/libs/langgraph/tests/test_channels.py +++ b/libs/langgraph/tests/test_channels.py @@ -4,10 +4,10 @@ from typing import Union import pytest +from langgraph._internal._typing import MISSING from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.last_value import LastValue from langgraph.channels.topic import Topic -from langgraph.constants import MISSING from langgraph.errors import EmptyChannelError, InvalidUpdateError pytestmark = pytest.mark.anyio diff --git a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py index 06efc8834..ee86d0ad8 100644 --- a/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py +++ b/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py @@ -36,7 +36,7 @@ from pydantic import BaseModel from typing_extensions import Annotated, TypedDict from langgraph._internal._runnable import RunnableCallable, RunnableLike -from langgraph._internal._typing import UNSET +from langgraph._internal._typing import MISSING from langgraph.errors import ErrorCode, create_error_message from langgraph.graph import END, StateGraph from langgraph.graph.message import add_messages @@ -405,7 +405,9 @@ def create_react_agent( print(chunk) ``` """ - if (config_schema := deprecated_kwargs.pop("config_schema", UNSET)) is not UNSET: + if ( + config_schema := deprecated_kwargs.pop("config_schema", MISSING) + ) is not MISSING: warn( "`config_schema` is no longer supported. Use `context_schema` instead.", category=LangGraphDeprecatedSinceV10, From 2eecaa85008c08bea0996cab90213f0c559edc7c Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:23:59 -0400 Subject: [PATCH 10/22] release(langgraph): v1.0.0a1 (#5520) prep for alpha release --- libs/langgraph/pyproject.toml | 2 +- libs/langgraph/uv.lock | 2 +- libs/prebuilt/uv.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index 811c0a195..c8e162a2f 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "langgraph" -version = "0.5.2" +version = "1.0.0a1" description = "Building stateful, multi-actor applications with LLMs" authors = [] requires-python = ">=3.9" diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index 9fcc5350f..bc756b96f 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -1192,7 +1192,7 @@ wheels = [ [[package]] name = "langgraph" -version = "0.5.2" +version = "1.0.0a1" source = { editable = "." } dependencies = [ { name = "langchain-core" }, diff --git a/libs/prebuilt/uv.lock b/libs/prebuilt/uv.lock index c7fc94f57..72ca44a4e 100644 --- a/libs/prebuilt/uv.lock +++ b/libs/prebuilt/uv.lock @@ -316,7 +316,7 @@ wheels = [ [[package]] name = "langgraph" -version = "0.5.2" +version = "1.0.0a1" source = { editable = "../langgraph" } dependencies = [ { name = "langchain-core" }, From 6e9e1ca1469dcaede5ac3b46aa10fce7b4b1ee35 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Wed, 16 Jul 2025 09:27:04 -0400 Subject: [PATCH 11/22] refactor(langgraph): make constants generally private with a few select exports (#5529) --- docs/docs/reference/constants.md | 3 +- libs/langgraph/langgraph/_internal/_config.py | 4 +- .../langgraph/_internal/_constants.py | 110 +++++++++++++ .../langgraph/_internal/_runnable.py | 4 +- libs/langgraph/langgraph/config.py | 2 +- libs/langgraph/langgraph/constants.py | 152 +++++------------- libs/langgraph/langgraph/func/__init__.py | 3 +- libs/langgraph/langgraph/graph/message.py | 3 +- libs/langgraph/langgraph/graph/state.py | 16 +- libs/langgraph/langgraph/graph/ui.py | 6 +- libs/langgraph/langgraph/pregel/_algo.py | 26 +-- libs/langgraph/langgraph/pregel/_call.py | 2 +- libs/langgraph/langgraph/pregel/_config.py | 0 libs/langgraph/langgraph/pregel/_draw.py | 3 +- libs/langgraph/langgraph/pregel/_io.py | 9 +- libs/langgraph/langgraph/pregel/_loop.py | 28 ++-- libs/langgraph/langgraph/pregel/_messages.py | 3 +- libs/langgraph/langgraph/pregel/_read.py | 2 +- libs/langgraph/langgraph/pregel/_retry.py | 2 +- libs/langgraph/langgraph/pregel/_runner.py | 8 +- libs/langgraph/langgraph/pregel/_validate.py | 2 +- libs/langgraph/langgraph/pregel/_write.py | 2 +- libs/langgraph/langgraph/pregel/debug.py | 10 +- libs/langgraph/langgraph/pregel/main.py | 52 +++--- libs/langgraph/langgraph/pregel/remote.py | 4 +- libs/langgraph/langgraph/runtime.py | 2 +- libs/langgraph/langgraph/types.py | 4 +- libs/langgraph/tests/test_algo.py | 2 +- libs/langgraph/tests/test_deprecation.py | 10 ++ libs/langgraph/tests/test_large_cases.py | 3 +- .../langgraph/tests/test_large_cases_async.py | 3 +- libs/langgraph/tests/test_messages_state.py | 3 +- libs/langgraph/tests/test_pregel.py | 4 +- libs/langgraph/tests/test_pregel_async.py | 4 +- libs/langgraph/tests/test_utils.py | 3 +- 35 files changed, 271 insertions(+), 223 deletions(-) create mode 100644 libs/langgraph/langgraph/_internal/_constants.py create mode 100644 libs/langgraph/langgraph/pregel/_config.py diff --git a/docs/docs/reference/constants.md b/docs/docs/reference/constants.md index f23e941fa..fe26ce727 100644 --- a/docs/docs/reference/constants.md +++ b/docs/docs/reference/constants.md @@ -2,5 +2,6 @@ options: members: - TAG_HIDDEN + - TAG_NOSTREAM - START - - END \ No newline at end of file + - END diff --git a/libs/langgraph/langgraph/_internal/_config.py b/libs/langgraph/langgraph/_internal/_config.py index 1c1428bb0..0b4739c98 100644 --- a/libs/langgraph/langgraph/_internal/_config.py +++ b/libs/langgraph/langgraph/_internal/_config.py @@ -18,8 +18,7 @@ from langchain_core.runnables.config import ( var_child_runnable_config, ) -from langgraph.checkpoint.base import CheckpointMetadata -from langgraph.constants import ( +from langgraph._internal._constants import ( CONF, CONFIG_KEY_CHECKPOINT_ID, CONFIG_KEY_CHECKPOINT_MAP, @@ -27,6 +26,7 @@ from langgraph.constants import ( NS_END, NS_SEP, ) +from langgraph.checkpoint.base import CheckpointMetadata DEFAULT_RECURSION_LIMIT = int(getenv("LANGGRAPH_DEFAULT_RECURSION_LIMIT", "25")) diff --git a/libs/langgraph/langgraph/_internal/_constants.py b/libs/langgraph/langgraph/_internal/_constants.py new file mode 100644 index 000000000..82b44bc15 --- /dev/null +++ b/libs/langgraph/langgraph/_internal/_constants.py @@ -0,0 +1,110 @@ +"""Constants used for Pregel operations.""" + +import sys +from typing import Literal, cast + +# --- Reserved write keys --- +INPUT = sys.intern("__input__") +# for values passed as input to the graph +INTERRUPT = sys.intern("__interrupt__") +# for dynamic interrupts raised by nodes +RESUME = sys.intern("__resume__") +# for values passed to resume a node after an interrupt +ERROR = sys.intern("__error__") +# for errors raised by nodes +NO_WRITES = sys.intern("__no_writes__") +# marker to signal node didn't write anything +TASKS = sys.intern("__pregel_tasks") +# for Send objects returned by nodes/edges, corresponds to PUSH below +RETURN = sys.intern("__return__") +# for writes of a task where we simply record the return value +PREVIOUS = sys.intern("__previous__") +# the implicit branch that handles each node's Control values + + +# --- Reserved cache namespaces --- +CACHE_NS_WRITES = sys.intern("__pregel_ns_writes") +# cache namespace for node writes + +# --- Reserved config.configurable keys --- +CONFIG_KEY_SEND = sys.intern("__pregel_send") +# holds the `write` function that accepts writes to state/edges/reserved keys +CONFIG_KEY_READ = sys.intern("__pregel_read") +# holds the `read` function that returns a copy of the current state +CONFIG_KEY_CALL = sys.intern("__pregel_call") +# holds the `call` function that accepts a node/func, args and returns a future +CONFIG_KEY_CHECKPOINTER = sys.intern("__pregel_checkpointer") +# holds a `BaseCheckpointSaver` passed from parent graph to child graphs +CONFIG_KEY_STREAM = sys.intern("__pregel_stream") +# holds a `StreamProtocol` passed from parent graph to child graphs +CONFIG_KEY_CACHE = sys.intern("__pregel_cache") +# holds a `BaseCache` made available to subgraphs +CONFIG_KEY_RESUMING = sys.intern("__pregel_resuming") +# holds a boolean indicating if subgraphs should resume from a previous checkpoint +CONFIG_KEY_TASK_ID = sys.intern("__pregel_task_id") +# holds the task ID for the current task +CONFIG_KEY_THREAD_ID = sys.intern("thread_id") +# holds the thread ID for the current invocation +CONFIG_KEY_CHECKPOINT_MAP = sys.intern("checkpoint_map") +# holds a mapping of checkpoint_ns -> checkpoint_id for parent graphs +CONFIG_KEY_CHECKPOINT_ID = sys.intern("checkpoint_id") +# holds the current checkpoint_id, if any +CONFIG_KEY_CHECKPOINT_NS = sys.intern("checkpoint_ns") +# holds the current checkpoint_ns, "" for root graph +CONFIG_KEY_NODE_FINISHED = sys.intern("__pregel_node_finished") +# holds a callback to be called when a node is finished +CONFIG_KEY_SCRATCHPAD = sys.intern("__pregel_scratchpad") +# holds a mutable dict for temporary storage scoped to the current task +CONFIG_KEY_RUNNER_SUBMIT = sys.intern("__pregel_runner_submit") +# holds a function that receives tasks from runner, executes them and returns results +CONFIG_KEY_CHECKPOINT_DURING = sys.intern("__pregel_checkpoint_during") +# holds a boolean indicating whether to checkpoint during the run (or only at the end) +CONFIG_KEY_RUNTIME = sys.intern("__pregel_runtime") +# holds a `Runtime` instance with context, store, stream writer, etc. +CONFIG_KEY_RESUME_MAP = sys.intern("__pregel_resume_map") +# holds a mapping of task ns -> resume value for resuming tasks + +# --- Other constants --- +PUSH = sys.intern("__pregel_push") +# denotes push-style tasks, ie. those created by Send objects +PULL = sys.intern("__pregel_pull") +# denotes pull-style tasks, ie. those triggered by edges +NS_SEP = sys.intern("|") +# for checkpoint_ns, separates each level (ie. graph|subgraph|subsubgraph) +NS_END = sys.intern(":") +# for checkpoint_ns, for each level, separates the namespace from the task_id +CONF = cast(Literal["configurable"], sys.intern("configurable")) +# key for the configurable dict in RunnableConfig +NULL_TASK_ID = sys.intern("00000000-0000-0000-0000-000000000000") +# the task_id to use for writes that are not associated with a task + +# redefined to avoid circular import with langgraph.constants +_TAG_HIDDEN = sys.intern("langsmith:hidden") + +RESERVED = { + _TAG_HIDDEN, + # reserved write keys + INPUT, + INTERRUPT, + RESUME, + ERROR, + NO_WRITES, + # reserved config.configurable keys + CONFIG_KEY_SEND, + CONFIG_KEY_READ, + CONFIG_KEY_CHECKPOINTER, + CONFIG_KEY_STREAM, + CONFIG_KEY_CHECKPOINT_MAP, + CONFIG_KEY_RESUMING, + CONFIG_KEY_TASK_ID, + CONFIG_KEY_CHECKPOINT_MAP, + CONFIG_KEY_CHECKPOINT_ID, + CONFIG_KEY_CHECKPOINT_NS, + CONFIG_KEY_RESUME_MAP, + # other constants + PUSH, + PULL, + NS_SEP, + NS_END, + CONF, +} diff --git a/libs/langgraph/langgraph/_internal/_runnable.py b/libs/langgraph/langgraph/_internal/_runnable.py index 22d1b0515..efa0dc825 100644 --- a/libs/langgraph/langgraph/_internal/_runnable.py +++ b/libs/langgraph/langgraph/_internal/_runnable.py @@ -48,11 +48,11 @@ from langgraph._internal._config import ( get_callback_manager_for_config, patch_config, ) -from langgraph._internal._typing import MISSING -from langgraph.constants import ( +from langgraph._internal._constants import ( CONF, CONFIG_KEY_RUNTIME, ) +from langgraph._internal._typing import MISSING from langgraph.store.base import BaseStore from langgraph.types import StreamWriter diff --git a/libs/langgraph/langgraph/config.py b/libs/langgraph/langgraph/config.py index d5f9db8fb..660924e46 100644 --- a/libs/langgraph/langgraph/config.py +++ b/libs/langgraph/langgraph/config.py @@ -5,7 +5,7 @@ from typing import Any from langchain_core.runnables import RunnableConfig from langchain_core.runnables.config import var_child_runnable_config -from langgraph.constants import CONF, CONFIG_KEY_RUNTIME +from langgraph._internal._constants import CONF, CONFIG_KEY_RUNTIME from langgraph.store.base import BaseStore from langgraph.types import StreamWriter diff --git a/libs/langgraph/langgraph/constants.py b/libs/langgraph/langgraph/constants.py index 726f15e45..5b7e52aae 100644 --- a/libs/langgraph/langgraph/constants.py +++ b/libs/langgraph/langgraph/constants.py @@ -1,7 +1,12 @@ import sys -from typing import Any, Literal, cast +from typing import Any from warnings import warn +from langgraph._internal._constants import ( + CONF, + CONFIG_KEY_CHECKPOINTER, + TASKS, +) from langgraph.warnings import LangGraphDeprecatedSinceV10 __all__ = ( @@ -9,10 +14,22 @@ __all__ = ( "TAG_HIDDEN", "START", "END", - "SELF", - "PREVIOUS", + # retained for backwards compatibility (mostly langgraph-api), should be removed in v2 (or earlier) + "CONF", + "TASKS", + "CONFIG_KEY_CHECKPOINTER", ) +# --- Public constants --- +TAG_NOSTREAM = sys.intern("nostream") +"""Tag to disable streaming for a chat model.""" +TAG_HIDDEN = sys.intern("langsmith:hidden") +"""Tag to hide a node/edge from certain tracing/streaming environments.""" +END = sys.intern("__end__") +"""The last (maybe virtual) node in graph-style Pregel.""" +START = sys.intern("__start__") +"""The first (maybe virtual) node in graph-style Pregel.""" + def __getattr__(name: str) -> Any: if name in ["Send", "Interrupt"]: @@ -28,117 +45,20 @@ def __getattr__(name: str) -> Any: module = import_module("langgraph.types") return getattr(module, name) + try: + from importlib import import_module + + private_constants = import_module("langgraph._internal._constants") + attr = getattr(private_constants, name) + warn( + f"Importing {name} from langgraph.constants is deprecated. " + f"This constant is now private and should not be used directly. " + "Please let the LangGraph team know if you need this value.", + LangGraphDeprecatedSinceV10, + stacklevel=2, + ) + return attr + except AttributeError: + pass + raise AttributeError(f"module has no attribute '{name}'") - - -# --- Public constants --- -TAG_NOSTREAM = sys.intern("nostream") -"""Tag to disable streaming for a chat model.""" -TAG_HIDDEN = sys.intern("langsmith:hidden") -"""Tag to hide a node/edge from certain tracing/streaming environments.""" -START = sys.intern("__start__") -"""The first (maybe virtual) node in graph-style Pregel.""" -END = sys.intern("__end__") -"""The last (maybe virtual) node in graph-style Pregel.""" -SELF = sys.intern("__self__") -"""The implicit branch that handles each node's Control values.""" -PREVIOUS = sys.intern("__previous__") - -# --- Reserved write keys --- -INPUT = sys.intern("__input__") -# for values passed as input to the graph -INTERRUPT = sys.intern("__interrupt__") -# for dynamic interrupts raised by nodes -RESUME = sys.intern("__resume__") -# for values passed to resume a node after an interrupt -ERROR = sys.intern("__error__") -# for errors raised by nodes -NO_WRITES = sys.intern("__no_writes__") -# marker to signal node didn't write anything -TASKS = sys.intern("__pregel_tasks") -# for Send objects returned by nodes/edges, corresponds to PUSH below -RETURN = sys.intern("__return__") -# for writes of a task where we simply record the return value - -# --- Reserved cache namespaces --- -CACHE_NS_WRITES = sys.intern("__pregel_ns_writes") -# cache namespace for node writes - -# --- Reserved config.configurable keys --- -CONFIG_KEY_SEND = sys.intern("__pregel_send") -# holds the `write` function that accepts writes to state/edges/reserved keys -CONFIG_KEY_READ = sys.intern("__pregel_read") -# holds the `read` function that returns a copy of the current state -CONFIG_KEY_CALL = sys.intern("__pregel_call") -# holds the `call` function that accepts a node/func, args and returns a future -CONFIG_KEY_CHECKPOINTER = sys.intern("__pregel_checkpointer") -# holds a `BaseCheckpointSaver` passed from parent graph to child graphs -CONFIG_KEY_STREAM = sys.intern("__pregel_stream") -# holds a `StreamProtocol` passed from parent graph to child graphs -CONFIG_KEY_CACHE = sys.intern("__pregel_cache") -# holds a `BaseCache` made available to subgraphs -CONFIG_KEY_RESUMING = sys.intern("__pregel_resuming") -# holds a boolean indicating if subgraphs should resume from a previous checkpoint -CONFIG_KEY_TASK_ID = sys.intern("__pregel_task_id") -# holds the task ID for the current task -CONFIG_KEY_THREAD_ID = sys.intern("thread_id") -# holds the thread ID for the current invocation -CONFIG_KEY_CHECKPOINT_MAP = sys.intern("checkpoint_map") -# holds a mapping of checkpoint_ns -> checkpoint_id for parent graphs -CONFIG_KEY_CHECKPOINT_ID = sys.intern("checkpoint_id") -# holds the current checkpoint_id, if any -CONFIG_KEY_CHECKPOINT_NS = sys.intern("checkpoint_ns") -# holds the current checkpoint_ns, "" for root graph -CONFIG_KEY_NODE_FINISHED = sys.intern("__pregel_node_finished") -# holds a callback to be called when a node is finished -CONFIG_KEY_SCRATCHPAD = sys.intern("__pregel_scratchpad") -# holds a mutable dict for temporary storage scoped to the current task -CONFIG_KEY_RUNNER_SUBMIT = sys.intern("__pregel_runner_submit") -# holds a function that receives tasks from runner, executes them and returns results -CONFIG_KEY_CHECKPOINT_DURING = sys.intern("__pregel_checkpoint_during") -# holds a boolean indicating whether to checkpoint during the run (or only at the end) -CONFIG_KEY_RUNTIME = sys.intern("__pregel_runtime") -# holds a `Runtime` instance with context, store, stream writer, etc. - -# --- Other constants --- -PUSH = sys.intern("__pregel_push") -# denotes push-style tasks, ie. those created by Send objects -PULL = sys.intern("__pregel_pull") -# denotes pull-style tasks, ie. those triggered by edges -NS_SEP = sys.intern("|") -# for checkpoint_ns, separates each level (ie. graph|subgraph|subsubgraph) -NS_END = sys.intern(":") -# for checkpoint_ns, for each level, separates the namespace from the task_id -CONF = cast(Literal["configurable"], sys.intern("configurable")) -# key for the configurable dict in RunnableConfig -NULL_TASK_ID = sys.intern("00000000-0000-0000-0000-000000000000") -# the task_id to use for writes that are not associated with a task -CONFIG_KEY_RESUME_MAP = sys.intern("__pregel_resume_map") -# holds a mapping of task ns -> resume value for resuming tasks - -RESERVED = { - TAG_HIDDEN, - # reserved write keys - INPUT, - INTERRUPT, - RESUME, - ERROR, - NO_WRITES, - # reserved config.configurable keys - CONFIG_KEY_SEND, - CONFIG_KEY_READ, - CONFIG_KEY_CHECKPOINTER, - CONFIG_KEY_STREAM, - CONFIG_KEY_CHECKPOINT_MAP, - CONFIG_KEY_RESUMING, - CONFIG_KEY_TASK_ID, - CONFIG_KEY_CHECKPOINT_MAP, - CONFIG_KEY_CHECKPOINT_ID, - CONFIG_KEY_CHECKPOINT_NS, - # other constants - PUSH, - PULL, - NS_SEP, - NS_END, - CONF, -} diff --git a/libs/langgraph/langgraph/func/__init__.py b/libs/langgraph/langgraph/func/__init__.py index d1e6d07bf..d62995ab5 100644 --- a/libs/langgraph/langgraph/func/__init__.py +++ b/libs/langgraph/langgraph/func/__init__.py @@ -20,12 +20,13 @@ from typing import ( from typing_extensions import Unpack +from langgraph._internal._constants import CACHE_NS_WRITES, PREVIOUS from langgraph._internal._typing import MISSING, DeprecatedKwargs from langgraph.cache.base import BaseCache from langgraph.channels.ephemeral_value import EphemeralValue from langgraph.channels.last_value import LastValue from langgraph.checkpoint.base import BaseCheckpointSaver -from langgraph.constants import CACHE_NS_WRITES, END, PREVIOUS, START +from langgraph.constants import END, START from langgraph.pregel import Pregel from langgraph.pregel._call import ( P, diff --git a/libs/langgraph/langgraph/graph/message.py b/libs/langgraph/langgraph/graph/message.py index fc3355eb6..e20c22185 100644 --- a/libs/langgraph/langgraph/graph/message.py +++ b/libs/langgraph/langgraph/graph/message.py @@ -24,7 +24,7 @@ from langchain_core.messages import ( ) from typing_extensions import TypedDict -from langgraph.constants import CONF, CONFIG_KEY_SEND +from langgraph._internal._constants import CONF, CONFIG_KEY_SEND, NS_SEP from langgraph.graph.state import StateGraph __all__ = ( @@ -320,7 +320,6 @@ def push_message( ) from langgraph.config import get_config - from langgraph.constants import NS_SEP from langgraph.pregel._messages import StreamMessagesHandler config = get_config() diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index a4654a8c2..40be8a8ee 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -27,6 +27,12 @@ from langchain_core.runnables import Runnable, RunnableConfig from pydantic import BaseModel, TypeAdapter from typing_extensions import Self, Unpack, is_typeddict +from langgraph._internal._constants import ( + INTERRUPT, + NS_END, + NS_SEP, + TASKS, +) from langgraph._internal._fields import ( get_cached_annotated_keys, get_field_default, @@ -45,15 +51,7 @@ from langgraph.channels.named_barrier_value import ( NamedBarrierValueAfterFinish, ) from langgraph.checkpoint.base import Checkpoint -from langgraph.constants import ( - END, - INTERRUPT, - NS_END, - NS_SEP, - START, - TAG_HIDDEN, - TASKS, -) +from langgraph.constants import END, START, TAG_HIDDEN from langgraph.errors import ( ErrorCode, InvalidUpdateError, diff --git a/libs/langgraph/langgraph/graph/ui.py b/libs/langgraph/langgraph/graph/ui.py index e829eff2e..f2fe5a1c2 100644 --- a/libs/langgraph/langgraph/graph/ui.py +++ b/libs/langgraph/langgraph/graph/ui.py @@ -7,7 +7,7 @@ from langchain_core.messages import AnyMessage from typing_extensions import TypedDict from langgraph.config import get_config, get_stream_writer -from langgraph.constants import CONF, CONFIG_KEY_SEND +from langgraph.constants import CONF __all__ = ( "UIMessage", @@ -96,6 +96,8 @@ def push_ui_message( ) """ + from langgraph._internal._constants import CONFIG_KEY_SEND + writer = get_stream_writer() config = get_config() @@ -148,6 +150,8 @@ def delete_ui_message(id: str, *, state_key: str = "ui") -> RemoveUIMessage: delete_ui_message("message-123") """ + from langgraph._internal._constants import CONFIG_KEY_SEND + writer = get_stream_writer() config = get_config() diff --git a/libs/langgraph/langgraph/pregel/_algo.py b/libs/langgraph/langgraph/pregel/_algo.py index c813a6805..adb8cdb64 100644 --- a/libs/langgraph/langgraph/pregel/_algo.py +++ b/libs/langgraph/langgraph/pregel/_algo.py @@ -26,18 +26,7 @@ from langchain_core.runnables.config import RunnableConfig from xxhash import xxh3_128_hexdigest from langgraph._internal._config import merge_configs, patch_config -from langgraph._internal._runtime import patch_runtime_non_null -from langgraph._internal._typing import EMPTY_SEQ, MISSING -from langgraph.channels.base import BaseChannel -from langgraph.channels.topic import Topic -from langgraph.checkpoint.base import ( - BaseCheckpointSaver, - ChannelVersions, - Checkpoint, - PendingWrite, - V, -) -from langgraph.constants import ( +from langgraph._internal._constants import ( CACHE_NS_WRITES, CONF, CONFIG_KEY_CHECKPOINT_ID, @@ -62,9 +51,20 @@ from langgraph.constants import ( RESERVED, RESUME, RETURN, - TAG_HIDDEN, TASKS, ) +from langgraph._internal._runtime import patch_runtime_non_null +from langgraph._internal._typing import EMPTY_SEQ, MISSING +from langgraph.channels.base import BaseChannel +from langgraph.channels.topic import Topic +from langgraph.checkpoint.base import ( + BaseCheckpointSaver, + ChannelVersions, + Checkpoint, + PendingWrite, + V, +) +from langgraph.constants import TAG_HIDDEN from langgraph.managed.base import ManagedValueMapping from langgraph.pregel._call import get_runnable_for_task, identifier from langgraph.pregel._io import read_channels diff --git a/libs/langgraph/langgraph/pregel/_call.py b/libs/langgraph/langgraph/pregel/_call.py index 6bcd93f05..5956160aa 100644 --- a/libs/langgraph/langgraph/pregel/_call.py +++ b/libs/langgraph/langgraph/pregel/_call.py @@ -13,6 +13,7 @@ from typing import Any, Callable, Generic, TypeVar, cast from langchain_core.runnables import Runnable from typing_extensions import ParamSpec +from langgraph._internal._constants import CONF, CONFIG_KEY_CALL, RETURN from langgraph._internal._runnable import ( RunnableCallable, RunnableSeq, @@ -20,7 +21,6 @@ from langgraph._internal._runnable import ( run_in_executor, ) from langgraph.config import get_config -from langgraph.constants import CONF, CONFIG_KEY_CALL, RETURN from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry from langgraph.types import CachePolicy, RetryPolicy diff --git a/libs/langgraph/langgraph/pregel/_config.py b/libs/langgraph/langgraph/pregel/_config.py new file mode 100644 index 000000000..e69de29bb diff --git a/libs/langgraph/langgraph/pregel/_draw.py b/libs/langgraph/langgraph/pregel/_draw.py index 9720f10e7..b8ae73389 100644 --- a/libs/langgraph/langgraph/pregel/_draw.py +++ b/libs/langgraph/langgraph/pregel/_draw.py @@ -7,9 +7,10 @@ from typing import Any, cast from langchain_core.runnables.config import RunnableConfig from langchain_core.runnables.graph import Graph, Node +from langgraph._internal._constants import CONF, CONFIG_KEY_SEND, INPUT from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import BaseCheckpointSaver -from langgraph.constants import CONF, CONFIG_KEY_SEND, END, INPUT, START +from langgraph.constants import END, START from langgraph.managed.base import ManagedValueSpec from langgraph.pregel._algo import ( PregelTaskWrites, diff --git a/libs/langgraph/langgraph/pregel/_io.py b/libs/langgraph/langgraph/pregel/_io.py index a5af16c23..3c05dbda7 100644 --- a/libs/langgraph/langgraph/pregel/_io.py +++ b/libs/langgraph/langgraph/pregel/_io.py @@ -4,18 +4,17 @@ from collections import Counter from collections.abc import Iterator, Mapping, Sequence from typing import Any, Literal -from langgraph._internal._typing import EMPTY_SEQ, MISSING -from langgraph.channels.base import BaseChannel, EmptyChannelError -from langgraph.constants import ( +from langgraph._internal._constants import ( ERROR, INTERRUPT, NULL_TASK_ID, RESUME, RETURN, - START, - TAG_HIDDEN, TASKS, ) +from langgraph._internal._typing import EMPTY_SEQ, MISSING +from langgraph.channels.base import BaseChannel, EmptyChannelError +from langgraph.constants import START, TAG_HIDDEN from langgraph.errors import InvalidUpdateError from langgraph.pregel._log import logger from langgraph.types import Command, PregelExecutableTask, Send diff --git a/libs/langgraph/langgraph/pregel/_loop.py b/libs/langgraph/langgraph/pregel/_loop.py index 7b1663d15..687b1d209 100644 --- a/libs/langgraph/langgraph/pregel/_loop.py +++ b/libs/langgraph/langgraph/pregel/_loop.py @@ -28,19 +28,7 @@ from langchain_core.runnables import RunnableConfig from typing_extensions import ParamSpec, Self from langgraph._internal._config import patch_configurable -from langgraph._internal._typing import EMPTY_SEQ, MISSING -from langgraph.cache.base import BaseCache -from langgraph.channels.base import BaseChannel -from langgraph.checkpoint.base import ( - WRITES_IDX_MAP, - BaseCheckpointSaver, - ChannelVersions, - Checkpoint, - CheckpointMetadata, - CheckpointTuple, - PendingWrite, -) -from langgraph.constants import ( +from langgraph._internal._constants import ( CONF, CONFIG_KEY_CHECKPOINT_ID, CONFIG_KEY_CHECKPOINT_MAP, @@ -59,8 +47,20 @@ from langgraph.constants import ( NULL_TASK_ID, PUSH, RESUME, - TAG_HIDDEN, ) +from langgraph._internal._typing import EMPTY_SEQ, MISSING +from langgraph.cache.base import BaseCache +from langgraph.channels.base import BaseChannel +from langgraph.checkpoint.base import ( + WRITES_IDX_MAP, + BaseCheckpointSaver, + ChannelVersions, + Checkpoint, + CheckpointMetadata, + CheckpointTuple, + PendingWrite, +) +from langgraph.constants import TAG_HIDDEN from langgraph.errors import ( EmptyInputError, GraphInterrupt, diff --git a/libs/langgraph/langgraph/pregel/_messages.py b/libs/langgraph/langgraph/pregel/_messages.py index b06991ba3..550ea789c 100644 --- a/libs/langgraph/langgraph/pregel/_messages.py +++ b/libs/langgraph/langgraph/pregel/_messages.py @@ -13,7 +13,8 @@ from langchain_core.callbacks import BaseCallbackHandler from langchain_core.messages import BaseMessage from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, LLMResult -from langgraph.constants import NS_SEP, TAG_HIDDEN, TAG_NOSTREAM +from langgraph._internal._constants import NS_SEP +from langgraph.constants import TAG_HIDDEN, TAG_NOSTREAM from langgraph.pregel.protocol import StreamChunk from langgraph.types import Command diff --git a/libs/langgraph/langgraph/pregel/_read.py b/libs/langgraph/langgraph/pregel/_read.py index a3edf2c31..bb3a6bf12 100644 --- a/libs/langgraph/langgraph/pregel/_read.py +++ b/libs/langgraph/langgraph/pregel/_read.py @@ -11,8 +11,8 @@ from typing import ( from langchain_core.runnables import Runnable, RunnableConfig from langgraph._internal._config import merge_configs +from langgraph._internal._constants import CONF, CONFIG_KEY_READ from langgraph._internal._runnable import RunnableCallable, RunnableSeq -from langgraph.constants import CONF, CONFIG_KEY_READ from langgraph.pregel._utils import find_subgraph_pregel from langgraph.pregel._write import ChannelWrite from langgraph.pregel.protocol import PregelProtocol diff --git a/libs/langgraph/langgraph/pregel/_retry.py b/libs/langgraph/langgraph/pregel/_retry.py index 4873e6824..d54797108 100644 --- a/libs/langgraph/langgraph/pregel/_retry.py +++ b/libs/langgraph/langgraph/pregel/_retry.py @@ -10,7 +10,7 @@ from dataclasses import replace from typing import Any, Callable from langgraph._internal._config import patch_configurable -from langgraph.constants import ( +from langgraph._internal._constants import ( CONF, CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_RESUMING, diff --git a/libs/langgraph/langgraph/pregel/_runner.py b/libs/langgraph/langgraph/pregel/_runner.py index 835525a76..d38afcfd6 100644 --- a/libs/langgraph/langgraph/pregel/_runner.py +++ b/libs/langgraph/langgraph/pregel/_runner.py @@ -19,9 +19,7 @@ from typing import ( from langchain_core.callbacks import Callbacks -from langgraph._internal._future import chain_future, run_coroutine_threadsafe -from langgraph._internal._typing import MISSING -from langgraph.constants import ( +from langgraph._internal._constants import ( CONF, CONFIG_KEY_CALL, CONFIG_KEY_SCRATCHPAD, @@ -30,8 +28,10 @@ from langgraph.constants import ( NO_WRITES, RESUME, RETURN, - TAG_HIDDEN, ) +from langgraph._internal._future import chain_future, run_coroutine_threadsafe +from langgraph._internal._typing import MISSING +from langgraph.constants import TAG_HIDDEN from langgraph.errors import GraphBubbleUp, GraphInterrupt from langgraph.pregel._algo import Call from langgraph.pregel._executor import Submit diff --git a/libs/langgraph/langgraph/pregel/_validate.py b/libs/langgraph/langgraph/pregel/_validate.py index 9a8910703..fcfb54c9a 100644 --- a/libs/langgraph/langgraph/pregel/_validate.py +++ b/libs/langgraph/langgraph/pregel/_validate.py @@ -3,8 +3,8 @@ from __future__ import annotations from collections.abc import Mapping, Sequence from typing import Any +from langgraph._internal._constants import RESERVED from langgraph.channels.base import BaseChannel -from langgraph.constants import RESERVED from langgraph.managed.base import ManagedValueMapping from langgraph.pregel._read import PregelNode from langgraph.types import All diff --git a/libs/langgraph/langgraph/pregel/_write.py b/libs/langgraph/langgraph/pregel/_write.py index dcefb2a36..6a6e4b612 100644 --- a/libs/langgraph/langgraph/pregel/_write.py +++ b/libs/langgraph/langgraph/pregel/_write.py @@ -13,9 +13,9 @@ from typing import ( from langchain_core.runnables import Runnable, RunnableConfig +from langgraph._internal._constants import CONF, CONFIG_KEY_SEND, TASKS from langgraph._internal._runnable import RunnableCallable from langgraph._internal._typing import MISSING -from langgraph.constants import CONF, CONFIG_KEY_SEND, TASKS from langgraph.errors import InvalidUpdateError from langgraph.types import Send diff --git a/libs/langgraph/langgraph/pregel/debug.py b/libs/langgraph/langgraph/pregel/debug.py index aeaf99ad7..d6fb1d630 100644 --- a/libs/langgraph/langgraph/pregel/debug.py +++ b/libs/langgraph/langgraph/pregel/debug.py @@ -9,10 +9,7 @@ from langchain_core.runnables import RunnableConfig from typing_extensions import TypedDict from langgraph._internal._config import patch_checkpoint_map -from langgraph._internal._typing import MISSING -from langgraph.channels.base import BaseChannel -from langgraph.checkpoint.base import CheckpointMetadata, PendingWrite -from langgraph.constants import ( +from langgraph._internal._constants import ( CONF, CONFIG_KEY_CHECKPOINT_NS, ERROR, @@ -20,8 +17,11 @@ from langgraph.constants import ( NS_END, NS_SEP, RETURN, - TAG_HIDDEN, ) +from langgraph._internal._typing import MISSING +from langgraph.channels.base import BaseChannel +from langgraph.checkpoint.base import CheckpointMetadata, PendingWrite +from langgraph.constants import TAG_HIDDEN from langgraph.pregel._io import read_channels from langgraph.types import PregelExecutableTask, PregelTask, StateSnapshot diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index 1d7324f65..22a02a88d 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -36,6 +36,31 @@ from langgraph._internal._config import ( patch_configurable, recast_checkpoint_ns, ) +from langgraph._internal._constants import ( + CACHE_NS_WRITES, + CONF, + CONFIG_KEY_CACHE, + CONFIG_KEY_CHECKPOINT_DURING, + CONFIG_KEY_CHECKPOINT_ID, + CONFIG_KEY_CHECKPOINT_NS, + CONFIG_KEY_CHECKPOINTER, + CONFIG_KEY_NODE_FINISHED, + CONFIG_KEY_READ, + CONFIG_KEY_RUNNER_SUBMIT, + CONFIG_KEY_RUNTIME, + CONFIG_KEY_SEND, + CONFIG_KEY_STREAM, + CONFIG_KEY_TASK_ID, + CONFIG_KEY_THREAD_ID, + ERROR, + INPUT, + INTERRUPT, + NS_END, + NS_SEP, + NULL_TASK_ID, + PUSH, + TASKS, +) from langgraph._internal._pydantic import create_model from langgraph._internal._queue import ( # type: ignore[attr-defined] AsyncQueue, @@ -57,32 +82,7 @@ from langgraph.checkpoint.base import ( CheckpointTuple, ) from langgraph.config import get_config -from langgraph.constants import ( - CACHE_NS_WRITES, - CONF, - CONFIG_KEY_CACHE, - CONFIG_KEY_CHECKPOINT_DURING, - CONFIG_KEY_CHECKPOINT_ID, - CONFIG_KEY_CHECKPOINT_NS, - CONFIG_KEY_CHECKPOINTER, - CONFIG_KEY_NODE_FINISHED, - CONFIG_KEY_READ, - CONFIG_KEY_RUNNER_SUBMIT, - CONFIG_KEY_RUNTIME, - CONFIG_KEY_SEND, - CONFIG_KEY_STREAM, - CONFIG_KEY_TASK_ID, - CONFIG_KEY_THREAD_ID, - END, - ERROR, - INPUT, - INTERRUPT, - NS_END, - NS_SEP, - NULL_TASK_ID, - PUSH, - TASKS, -) +from langgraph.constants import END from langgraph.errors import ( ErrorCode, GraphRecursionError, diff --git a/libs/langgraph/langgraph/pregel/remote.py b/libs/langgraph/langgraph/pregel/remote.py index c5e0e8dcd..837efa364 100644 --- a/libs/langgraph/langgraph/pregel/remote.py +++ b/libs/langgraph/langgraph/pregel/remote.py @@ -30,8 +30,7 @@ from langgraph_sdk.schema import StreamMode as StreamModeSDK from typing_extensions import Self from langgraph._internal._config import merge_configs -from langgraph.checkpoint.base import CheckpointMetadata -from langgraph.constants import ( +from langgraph._internal._constants import ( CONF, CONFIG_KEY_CHECKPOINT_ID, CONFIG_KEY_CHECKPOINT_MAP, @@ -41,6 +40,7 @@ from langgraph.constants import ( INTERRUPT, NS_SEP, ) +from langgraph.checkpoint.base import CheckpointMetadata from langgraph.errors import GraphInterrupt from langgraph.pregel.protocol import PregelProtocol, StreamProtocol from langgraph.types import ( diff --git a/libs/langgraph/langgraph/runtime.py b/libs/langgraph/langgraph/runtime.py index cfaa8dbe9..e793e007c 100644 --- a/libs/langgraph/langgraph/runtime.py +++ b/libs/langgraph/langgraph/runtime.py @@ -3,8 +3,8 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any, Generic, cast +from langgraph._internal._constants import CONF, CONFIG_KEY_RUNTIME from langgraph.config import get_config -from langgraph.constants import CONF, CONFIG_KEY_RUNTIME from langgraph.store.base import BaseStore from langgraph.types import _DC_KWARGS, StreamWriter from langgraph.typing import ContextT diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 95c2d708d..2cb6d58ab 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -473,13 +473,13 @@ def interrupt(value: Any) -> Any: Raises: GraphInterrupt: On the first invocation within the node, halts execution and surfaces the provided value to the client. """ - from langgraph.config import get_config - from langgraph.constants import ( + from langgraph._internal._constants import ( CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_SEND, RESUME, ) + from langgraph.config import get_config from langgraph.errors import GraphInterrupt conf = get_config()["configurable"] diff --git a/libs/langgraph/tests/test_algo.py b/libs/langgraph/tests/test_algo.py index f32fa4334..0bf988173 100644 --- a/libs/langgraph/tests/test_algo.py +++ b/libs/langgraph/tests/test_algo.py @@ -1,4 +1,4 @@ -from langgraph.constants import PULL, PUSH +from langgraph._internal._constants import PULL, PUSH from langgraph.pregel._algo import prepare_next_tasks, task_path_str from langgraph.pregel._checkpoint import channels_from_checkpoint, empty_checkpoint diff --git a/libs/langgraph/tests/test_deprecation.py b/libs/langgraph/tests/test_deprecation.py index dee208c4b..d33b1a8ac 100644 --- a/libs/langgraph/tests/test_deprecation.py +++ b/libs/langgraph/tests/test_deprecation.py @@ -142,6 +142,7 @@ def test_config_type_deprecation_pregel(mocker: MockerFixture) -> None: ) +@pytest.mark.filterwarnings("ignore:`interrupt_id` is deprecated. Use `id` instead.") def test_interrupt_attributes_deprecation() -> None: interrupt = Interrupt(value="question", id="abc") @@ -152,9 +153,18 @@ def test_interrupt_attributes_deprecation() -> None: interrupt.interrupt_id +@pytest.mark.filterwarnings("ignore:NodeInterrupt is deprecated.") def test_node_interrupt_deprecation() -> None: with pytest.warns( LangGraphDeprecatedSinceV10, match="NodeInterrupt is deprecated. Please use `langgraph.types.interrupt` instead.", ): NodeInterrupt(value="test") + + +def test_deprecated_import() -> None: + with pytest.warns( + LangGraphDeprecatedSinceV10, + 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: F401 diff --git a/libs/langgraph/tests/test_large_cases.py b/libs/langgraph/tests/test_large_cases.py index 419686255..463c4542f 100644 --- a/libs/langgraph/tests/test_large_cases.py +++ b/libs/langgraph/tests/test_large_cases.py @@ -11,11 +11,12 @@ from pytest_mock import MockerFixture from syrupy import SnapshotAssertion from typing_extensions import TypedDict +from langgraph._internal._constants import PULL, PUSH from langgraph.channels.last_value import LastValue from langgraph.channels.untracked_value import UntrackedValue from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.checkpoint.memory import InMemorySaver -from langgraph.constants import END, PULL, PUSH, START +from langgraph.constants import END, START from langgraph.graph import StateGraph from langgraph.graph.message import MessageGraph, MessagesState, add_messages from langgraph.prebuilt.chat_agent_executor import create_react_agent diff --git a/libs/langgraph/tests/test_large_cases_async.py b/libs/langgraph/tests/test_large_cases_async.py index 29f92d8b9..8be3e84c6 100644 --- a/libs/langgraph/tests/test_large_cases_async.py +++ b/libs/langgraph/tests/test_large_cases_async.py @@ -16,10 +16,11 @@ from langchain_core.runnables import RunnableConfig, RunnablePick from pytest_mock import MockerFixture from typing_extensions import TypedDict +from langgraph._internal._constants import PULL, PUSH from langgraph.channels.last_value import LastValue from langgraph.channels.untracked_value import UntrackedValue from langgraph.checkpoint.base import BaseCheckpointSaver -from langgraph.constants import END, PULL, PUSH, START +from langgraph.constants import END, START from langgraph.graph.message import MessageGraph, add_messages from langgraph.graph.state import StateGraph from langgraph.prebuilt.chat_agent_executor import create_react_agent diff --git a/libs/langgraph/tests/test_messages_state.py b/libs/langgraph/tests/test_messages_state.py index a481123a4..0a1e78ecb 100644 --- a/libs/langgraph/tests/test_messages_state.py +++ b/libs/langgraph/tests/test_messages_state.py @@ -14,9 +14,10 @@ from langchain_core.messages import ( from pydantic import BaseModel from typing_extensions import TypedDict +from langgraph.constants import END, START from langgraph.graph import add_messages from langgraph.graph.message import REMOVE_ALL_MESSAGES, MessagesState, push_message -from langgraph.graph.state import END, START, StateGraph +from langgraph.graph.state import StateGraph from tests.messages import _AnyIdHumanMessage _, CORE_MINOR, CORE_PATCH = (int(v) for v in langchain_core.__version__.split(".")) diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index a64f843da..df88212a0 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -28,6 +28,7 @@ from pytest_mock import MockerFixture from syrupy import SnapshotAssertion from typing_extensions import TypedDict +from langgraph._internal._constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL from langgraph.cache.base import BaseCache from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.ephemeral_value import EphemeralValue @@ -41,10 +42,9 @@ from langgraph.checkpoint.base import ( ) from langgraph.checkpoint.memory import InMemorySaver from langgraph.config import get_stream_writer -from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START from langgraph.errors import GraphRecursionError, InvalidUpdateError, ParentCommand from langgraph.func import entrypoint, task -from langgraph.graph import END, StateGraph +from langgraph.graph import END, START, StateGraph from langgraph.graph.message import MessageGraph, MessagesState, add_messages from langgraph.prebuilt.tool_node import ToolNode from langgraph.pregel import ( diff --git a/libs/langgraph/tests/test_pregel_async.py b/libs/langgraph/tests/test_pregel_async.py index b2e99af23..e81cb5e11 100644 --- a/libs/langgraph/tests/test_pregel_async.py +++ b/libs/langgraph/tests/test_pregel_async.py @@ -28,6 +28,7 @@ from pytest_mock import MockerFixture from syrupy import SnapshotAssertion from typing_extensions import TypedDict +from langgraph._internal._constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL from langgraph.cache.base import BaseCache from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.last_value import LastValue @@ -41,14 +42,13 @@ from langgraph.checkpoint.base import ( ) from langgraph.checkpoint.memory import InMemorySaver from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer -from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START from langgraph.errors import ( GraphRecursionError, InvalidUpdateError, ParentCommand, ) from langgraph.func import entrypoint, task -from langgraph.graph import END, StateGraph +from langgraph.graph import END, START, StateGraph from langgraph.graph.message import MessagesState, add_messages from langgraph.prebuilt.tool_node import ToolNode from langgraph.pregel import NodeBuilder, Pregel diff --git a/libs/langgraph/tests/test_utils.py b/libs/langgraph/tests/test_utils.py index fabac0595..afe486af2 100644 --- a/libs/langgraph/tests/test_utils.py +++ b/libs/langgraph/tests/test_utils.py @@ -27,7 +27,8 @@ from langgraph._internal._runnable import ( is_async_callable, is_async_generator, ) -from langgraph.graph import END, StateGraph +from langgraph.constants import END +from langgraph.graph import StateGraph from langgraph.graph.state import CompiledStateGraph pytestmark = pytest.mark.anyio From 294078adab2ff81d55bff153991245a176b98b60 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Wed, 16 Jul 2025 18:30:43 -0400 Subject: [PATCH 12/22] release(langgraph): revert alpha release, going to do v0.6 off main instead (#5543) Revert "release(langgraph): v1.0.0a1 (#5520)" This reverts commit 2eecaa85008c08bea0996cab90213f0c559edc7c. --- libs/langgraph/pyproject.toml | 2 +- libs/langgraph/uv.lock | 2 +- libs/prebuilt/uv.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index c8e162a2f..811c0a195 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "langgraph" -version = "1.0.0a1" +version = "0.5.2" description = "Building stateful, multi-actor applications with LLMs" authors = [] requires-python = ">=3.9" diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index bc756b96f..9fcc5350f 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -1192,7 +1192,7 @@ wheels = [ [[package]] name = "langgraph" -version = "1.0.0a1" +version = "0.5.2" source = { editable = "." } dependencies = [ { name = "langchain-core" }, diff --git a/libs/prebuilt/uv.lock b/libs/prebuilt/uv.lock index 72ca44a4e..c7fc94f57 100644 --- a/libs/prebuilt/uv.lock +++ b/libs/prebuilt/uv.lock @@ -316,7 +316,7 @@ wheels = [ [[package]] name = "langgraph" -version = "1.0.0a1" +version = "0.5.2" source = { editable = "../langgraph" } dependencies = [ { name = "langchain-core" }, From adc732272cdccdf4f903531c70b95aca6944fe17 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Thu, 17 Jul 2025 09:57:25 -0400 Subject: [PATCH 13/22] refactor(langgraph): improve `Runtime` interface re patch/overrides (#5546) --- .../langgraph/langgraph/_internal/_runtime.py | 39 ----------------- libs/langgraph/langgraph/graph/state.py | 2 +- libs/langgraph/langgraph/pregel/_algo.py | 24 ++++++----- libs/langgraph/langgraph/runtime.py | 42 ++++++++++++++++--- 4 files changed, 50 insertions(+), 57 deletions(-) delete mode 100644 libs/langgraph/langgraph/_internal/_runtime.py diff --git a/libs/langgraph/langgraph/_internal/_runtime.py b/libs/langgraph/langgraph/_internal/_runtime.py deleted file mode 100644 index d3296e523..000000000 --- a/libs/langgraph/langgraph/_internal/_runtime.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Internal utilities for the Runtime class.""" - -from __future__ import annotations - -from dataclasses import replace -from typing import Any, cast - -from typing_extensions import TypedDict, Unpack - -from langgraph.runtime import Runtime -from langgraph.store.base import BaseStore -from langgraph.types import StreamWriter - - -class RuntimePatch(TypedDict, total=False): - """Patch structure for the Runtime class.""" - - context: Any - store: BaseStore | None - stream_writer: StreamWriter - previous: Any - - -def patch_runtime(runtime: Runtime, **overrides: Unpack[RuntimePatch]) -> Runtime: - """Patch the runtime with the given overrides, returning a new instance.""" - return replace(runtime, **overrides) - - -def patch_runtime_non_null( - runtime: Runtime, **overrides: Unpack[RuntimePatch] -) -> Runtime: - """Patch the runtime with the given overrides, returning a new instance. - - Only patch fields with overrides that are not None. - """ - return replace( - runtime, - **cast(dict[str, Any], {k: v for k, v in overrides.items() if v is not None}), - ) diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 40be8a8ee..745db69a9 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -135,7 +135,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]): from typing_extensions import Annotated, TypedDict from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import StateGraph - from langgraph.rumtime import Runtime + from langgraph.runtime import Runtime def reducer(a: list, b: int | None) -> list: if b is not None: diff --git a/libs/langgraph/langgraph/pregel/_algo.py b/libs/langgraph/langgraph/pregel/_algo.py index adb8cdb64..8b9a433fb 100644 --- a/libs/langgraph/langgraph/pregel/_algo.py +++ b/libs/langgraph/langgraph/pregel/_algo.py @@ -53,7 +53,6 @@ from langgraph._internal._constants import ( RETURN, TASKS, ) -from langgraph._internal._runtime import patch_runtime_non_null from langgraph._internal._typing import EMPTY_SEQ, MISSING from langgraph.channels.base import BaseChannel from langgraph.channels.topic import Topic @@ -71,7 +70,7 @@ from langgraph.pregel._io import read_channels from langgraph.pregel._log import logger from langgraph.pregel._read import INPUT_CACHE_KEY_TYPE, PregelNode from langgraph.pregel._scratchpad import PregelScratchpad -from langgraph.runtime import DEFAULT_RUNTIME +from langgraph.runtime import DEFAULT_RUNTIME, Runtime from langgraph.store.base import BaseStore from langgraph.types import ( All, @@ -583,10 +582,10 @@ def prepare_single_task( step, stop, ) - runtime = patch_runtime_non_null( - configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME), - store=store, + runtime = cast( + Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME) ) + runtime = runtime.override(store=store) return PregelExecutableTask( name, call.input, @@ -713,10 +712,11 @@ def prepare_single_task( step, stop, ) - runtime = patch_runtime_non_null( - configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME), - store=store, - previous=checkpoint["channel_values"].get(PREVIOUS, None), + runtime = cast( + Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME) + ) + runtime = runtime.override( + store=store, previous=checkpoint["channel_values"].get(PREVIOUS, None) ) return PregelExecutableTask( packet.node, @@ -852,8 +852,10 @@ def prepare_single_task( ) else: cache_key = None - runtime = patch_runtime_non_null( - configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME), + runtime = cast( + Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME) + ) + runtime = runtime.override( previous=checkpoint["channel_values"].get(PREVIOUS, None), store=store, ) diff --git a/libs/langgraph/langgraph/runtime.py b/libs/langgraph/langgraph/runtime.py index e793e007c..c819f1d4e 100644 --- a/libs/langgraph/langgraph/runtime.py +++ b/libs/langgraph/langgraph/runtime.py @@ -1,8 +1,10 @@ from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, field, replace from typing import Any, Generic, cast +from typing_extensions import TypedDict, Unpack + from langgraph._internal._constants import CONF, CONFIG_KEY_RUNTIME from langgraph.config import get_config from langgraph.store.base import BaseStore @@ -13,6 +15,13 @@ from langgraph.typing import ContextT def _no_op_stream_writer(_: Any) -> None: ... +class _RuntimeOverrides(TypedDict, Generic[ContextT], total=False): + context: ContextT + store: BaseStore | None + stream_writer: StreamWriter + previous: Any + + @dataclass(**_DC_KWARGS) class Runtime(Generic[ContextT]): """Convenience class that bundles run-scoped context and graph configuration. @@ -20,21 +29,42 @@ class Runtime(Generic[ContextT]): !!! version-added "Added in version 1.0.0." """ - context: ContextT + context: ContextT = field(default=None) # type: ignore[assignment] """Static context for the graph run, like user_id, db_conn, etc. Can also be thought of as 'run dependencies'.""" - store: BaseStore | None + store: BaseStore | None = field(default=None) """Store for the graph run, enabling persistence and memory.""" - stream_writer: StreamWriter + stream_writer: StreamWriter = field(default=_no_op_stream_writer) """Function that writes to the custom stream.""" - previous: Any | None + previous: Any = field(default=None) """The previous return value for the given thread. - Only available with the functional API when a checkpointer is provided.""" + Only available with the functional API when a checkpointer is provided. + """ + + def merge(self, other: Runtime[ContextT]) -> Runtime[ContextT]: + """Merge two runtimes together. + + If a value is not provided in the other runtime, the value from the current runtime is used. + """ + return Runtime( + context=other.context or self.context, + store=other.store or self.store, + stream_writer=other.stream_writer + if other.stream_writer is not _no_op_stream_writer + else self.stream_writer, + previous=other.previous or self.previous, + ) + + def override( + self, **overrides: Unpack[_RuntimeOverrides[ContextT]] + ) -> Runtime[ContextT]: + """Replace the runtime with a new runtime with the given overrides.""" + return replace(self, **overrides) DEFAULT_RUNTIME = Runtime( From a5fe3316b627e773796a2590b661821240cf0699 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:40:09 -0400 Subject: [PATCH 14/22] chore(langgraph): bump api version and prep for v0.6 alpha (#5559) Bumping version on `v1` branch so that we can access most recent `langgraph-cli[inmem]` changes (with v0.6 compat) for store injection. --- libs/cli/pyproject.toml | 2 +- libs/cli/uv.lock | 14 +- libs/langgraph/pyproject.toml | 3 +- libs/langgraph/uv.lock | 410 ++++++++++++++++++---------------- libs/prebuilt/uv.lock | 4 +- 5 files changed, 228 insertions(+), 205 deletions(-) diff --git a/libs/cli/pyproject.toml b/libs/cli/pyproject.toml index 05045d3bc..40a3bc638 100644 --- a/libs/cli/pyproject.toml +++ b/libs/cli/pyproject.toml @@ -19,7 +19,7 @@ dependencies = [ [project.optional-dependencies] inmem = [ "langgraph-api>=0.2.67,<0.3.0 ; python_version >= '3.11'", - "langgraph-runtime-inmem>=0.3.4,<0.4.0 ; python_version >= '3.11'", + "langgraph-runtime-inmem>=0.6.0 ; python_version >= '3.11'", "python-dotenv>=0.8.0", ] diff --git a/libs/cli/uv.lock b/libs/cli/uv.lock index 5ac576369..948d3f499 100644 --- a/libs/cli/uv.lock +++ b/libs/cli/uv.lock @@ -479,7 +479,7 @@ wheels = [ [[package]] name = "langgraph-api" -version = "0.2.86" +version = "0.2.95" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle", marker = "python_full_version >= '3.11'" }, @@ -502,9 +502,9 @@ dependencies = [ { name = "uvicorn", marker = "python_full_version >= '3.11'" }, { name = "watchfiles", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/06/f8d6c1310772a8507dfa2c586bab8d0ab8b8cbe1f896106ee315af08fb1d/langgraph_api-0.2.86.tar.gz", hash = "sha256:220532a5a2232d32efef7e3b98be74ee6328d18f785e83949fa815ef2ac77f2f", size = 237417, upload-time = "2025-07-11T17:02:39.535Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/3d/5afd71e18806b71634e2178ba9e5e78a6678e0b8121158c9f458e57a8b9b/langgraph_api-0.2.95.tar.gz", hash = "sha256:7604cf276e592af00ab17642c053ced6f87122c53186256645593c7da4fbbfa3", size = 238773, upload-time = "2025-07-17T16:56:11.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/48/e6b774e8cfe254694b768629c72004689d94d002a54e949c23e05b776eae/langgraph_api-0.2.86-py3-none-any.whl", hash = "sha256:b20ac26ef9c5323732012eed602290ca9ca268473341dcb3282b02ed6622ec8c", size = 192498, upload-time = "2025-07-11T17:02:38.199Z" }, + { url = "https://files.pythonhosted.org/packages/7d/17/63636946f3d5d1c59b5b0a2d936d8009860227368e8868d33c72b61dfbbb/langgraph_api-0.2.95-py3-none-any.whl", hash = "sha256:25946eef80794bf92c27daf21db4af864779677bb9f92c1bc795901d7113e9ae", size = 194381, upload-time = "2025-07-17T16:56:10.275Z" }, ] [[package]] @@ -553,7 +553,7 @@ dev = [ requires-dist = [ { name = "click", specifier = ">=8.1.7" }, { name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.67,<0.3.0" }, - { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3.4,<0.4.0" }, + { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.6.0" }, { name = "langgraph-sdk", marker = "python_full_version >= '3.11'", specifier = ">=0.1.0" }, { name = "python-dotenv", marker = "extra == 'inmem'", specifier = ">=0.8.0" }, ] @@ -586,7 +586,7 @@ wheels = [ [[package]] name = "langgraph-runtime-inmem" -version = "0.3.4" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "blockbuster", marker = "python_full_version >= '3.11'" }, @@ -596,9 +596,9 @@ dependencies = [ { name = "starlette", marker = "python_full_version >= '3.11'" }, { name = "structlog", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c1/17/7ff669ff44a53ab342903c2996fff75a77494af9fe56abcfbca64fe2342b/langgraph_runtime_inmem-0.3.4.tar.gz", hash = "sha256:eda7828f3ea07126e5265024b74a3fa9bf611633ad83ba3296ab9f51d89b7c0c", size = 77424, upload-time = "2025-07-01T14:45:07.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/0c/d145c6d83d36efda17b10812760711b77ec05f5bbe962c961d75b32e3c17/langgraph_runtime_inmem-0.6.0.tar.gz", hash = "sha256:b09675789a331be4a2b387c9c46de8772c4c8418e74c057b4ca24e85c25acae3", size = 77618, upload-time = "2025-07-17T16:51:01.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/0e/39c13ca7229a9425a0e5744a1d3817f80d38dd5ca7703494fb9cf836ba45/langgraph_runtime_inmem-0.3.4-py3-none-any.whl", hash = "sha256:dcb9ac68ac90b3fb1ddaf666d14a367ab70e69d5bb5589b77a72c318e29104ae", size = 29139, upload-time = "2025-07-01T14:45:06.472Z" }, + { url = "https://files.pythonhosted.org/packages/12/6a/9dc5769b5d2f97d1feacbbf93b180c359dff7462454b37dfef8aed4ebcf7/langgraph_runtime_inmem-0.6.0-py3-none-any.whl", hash = "sha256:312dab25bec6557f1edf95cb8bd7c8bb52f7f4bfeecaf66e7001662f095c9079", size = 29317, upload-time = "2025-07-17T16:51:00.622Z" }, ] [[package]] diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index f14392880..661fe3007 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "langgraph" -version = "0.5.3" +version = "0.6.0a1" description = "Building stateful, multi-actor applications with LLMs" authors = [] requires-python = ">=3.9" @@ -60,6 +60,7 @@ langgraph-checkpoint = { path = "../checkpoint", editable = true } langgraph-checkpoint-sqlite = { path = "../checkpoint-sqlite", editable = true } langgraph-checkpoint-postgres = { path = "../checkpoint-postgres", editable = true } langgraph-sdk = { path = "../sdk-py", editable = true } +langgraph-cli = { path = "../cli", editable = true } [tool.ruff] lint.select = [ "E", "F", "I", "TID251", "UP" ] diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index d4455e78f..2d27e91a1 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -169,23 +169,23 @@ css = [ [[package]] name = "blockbuster" -version = "1.5.24" +version = "1.5.25" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "forbiddenfruit", marker = "python_full_version >= '3.11' and implementation_name == 'cpython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/c8/1e456a043179f2aef10bcaafea79f6d06c0ac45cc994767a54f680509f3b/blockbuster-1.5.24.tar.gz", hash = "sha256:97645775761a5d425666ec0bc99629b65c7eccdc2f770d2439850682567af4ec", size = 51245, upload-time = "2025-03-18T10:12:06.398Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/bc/57c49465decaeeedd58ce2d970b4cdfd93a74ba9993abff2dc498a31c283/blockbuster-1.5.25.tar.gz", hash = "sha256:b72f1d2aefdeecd2a820ddf1e1c8593bf00b96e9fdc4cd2199ebafd06f7cb8f0", size = 36058, upload-time = "2025-07-14T16:00:20.766Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c8/57a4c80e5abec29fa9406307a5277527f21210bfc6c2c61c3d8ded36c09b/blockbuster-1.5.24-py3-none-any.whl", hash = "sha256:e703497b55bc72af09d60d1cd746c2f3ba7ce0c446fa256be6ccda5e7d403520", size = 13214, upload-time = "2025-03-18T10:12:04.802Z" }, + { url = "https://files.pythonhosted.org/packages/0b/01/dccc277c014f171f61a6047bb22c684e16c7f2db6bb5c8cce1feaf41ec55/blockbuster-1.5.25-py3-none-any.whl", hash = "sha256:cb06229762273e0f5f3accdaed3d2c5a3b61b055e38843de202311ede21bb0f5", size = 13196, upload-time = "2025-07-14T16:00:19.396Z" }, ] [[package]] name = "certifi" -version = "2025.7.9" +version = "2025.7.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/8a/c729b6b60c66a38f590c4e774decc4b2ec7b0576be8f1aa984a53ffa812a/certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079", size = 160386, upload-time = "2025-07-09T02:13:58.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/f3/80a3f974c8b535d394ff960a11ac20368e06b736da395b551a49ce950cce/certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39", size = 159230, upload-time = "2025-07-09T02:13:57.007Z" }, + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, ] [[package]] @@ -520,31 +520,31 @@ wheels = [ [[package]] name = "debugpy" -version = "1.8.14" +version = "1.8.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/75/087fe07d40f490a78782ff3b0a30e3968936854105487decdb33446d4b0e/debugpy-1.8.14.tar.gz", hash = "sha256:7cd287184318416850aa8b60ac90105837bb1e59531898c07569d197d2ed5322", size = 1641444, upload-time = "2025-04-10T19:46:10.981Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/3a9a28ddb750a76eaec445c7f4d3147ea2c579a97dbd9e25d39001b92b21/debugpy-1.8.15.tar.gz", hash = "sha256:58d7a20b7773ab5ee6bdfb2e6cf622fdf1e40c9d5aef2857d85391526719ac00", size = 1643279, upload-time = "2025-07-15T16:43:29.135Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/df/156df75a41aaebd97cee9d3870fe68f8001b6c1c4ca023e221cfce69bece/debugpy-1.8.14-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:93fee753097e85623cab1c0e6a68c76308cd9f13ffdf44127e6fab4fbf024339", size = 2076510, upload-time = "2025-04-10T19:46:13.315Z" }, - { url = "https://files.pythonhosted.org/packages/69/cd/4fc391607bca0996db5f3658762106e3d2427beaef9bfd363fd370a3c054/debugpy-1.8.14-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d937d93ae4fa51cdc94d3e865f535f185d5f9748efb41d0d49e33bf3365bd79", size = 3559614, upload-time = "2025-04-10T19:46:14.647Z" }, - { url = "https://files.pythonhosted.org/packages/1a/42/4e6d2b9d63e002db79edfd0cb5656f1c403958915e0e73ab3e9220012eec/debugpy-1.8.14-cp310-cp310-win32.whl", hash = "sha256:c442f20577b38cc7a9aafecffe1094f78f07fb8423c3dddb384e6b8f49fd2987", size = 5208588, upload-time = "2025-04-10T19:46:16.233Z" }, - { url = "https://files.pythonhosted.org/packages/97/b1/cc9e4e5faadc9d00df1a64a3c2d5c5f4b9df28196c39ada06361c5141f89/debugpy-1.8.14-cp310-cp310-win_amd64.whl", hash = "sha256:f117dedda6d969c5c9483e23f573b38f4e39412845c7bc487b6f2648df30fe84", size = 5241043, upload-time = "2025-04-10T19:46:17.768Z" }, - { url = "https://files.pythonhosted.org/packages/67/e8/57fe0c86915671fd6a3d2d8746e40485fd55e8d9e682388fbb3a3d42b86f/debugpy-1.8.14-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:1b2ac8c13b2645e0b1eaf30e816404990fbdb168e193322be8f545e8c01644a9", size = 2175064, upload-time = "2025-04-10T19:46:19.486Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/2b2fd1b1c9569c6764ccdb650a6f752e4ac31be465049563c9eb127a8487/debugpy-1.8.14-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf431c343a99384ac7eab2f763980724834f933a271e90496944195318c619e2", size = 3132359, upload-time = "2025-04-10T19:46:21.192Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ee/b825c87ed06256ee2a7ed8bab8fb3bb5851293bf9465409fdffc6261c426/debugpy-1.8.14-cp311-cp311-win32.whl", hash = "sha256:c99295c76161ad8d507b413cd33422d7c542889fbb73035889420ac1fad354f2", size = 5133269, upload-time = "2025-04-10T19:46:23.047Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a6/6c70cd15afa43d37839d60f324213843174c1d1e6bb616bd89f7c1341bac/debugpy-1.8.14-cp311-cp311-win_amd64.whl", hash = "sha256:7816acea4a46d7e4e50ad8d09d963a680ecc814ae31cdef3622eb05ccacf7b01", size = 5158156, upload-time = "2025-04-10T19:46:24.521Z" }, - { url = "https://files.pythonhosted.org/packages/d9/2a/ac2df0eda4898f29c46eb6713a5148e6f8b2b389c8ec9e425a4a1d67bf07/debugpy-1.8.14-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:8899c17920d089cfa23e6005ad9f22582fd86f144b23acb9feeda59e84405b84", size = 2501268, upload-time = "2025-04-10T19:46:26.044Z" }, - { url = "https://files.pythonhosted.org/packages/10/53/0a0cb5d79dd9f7039169f8bf94a144ad3efa52cc519940b3b7dde23bcb89/debugpy-1.8.14-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6bb5c0dcf80ad5dbc7b7d6eac484e2af34bdacdf81df09b6a3e62792b722826", size = 4221077, upload-time = "2025-04-10T19:46:27.464Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d5/84e01821f362327bf4828728aa31e907a2eca7c78cd7c6ec062780d249f8/debugpy-1.8.14-cp312-cp312-win32.whl", hash = "sha256:281d44d248a0e1791ad0eafdbbd2912ff0de9eec48022a5bfbc332957487ed3f", size = 5255127, upload-time = "2025-04-10T19:46:29.467Z" }, - { url = "https://files.pythonhosted.org/packages/33/16/1ed929d812c758295cac7f9cf3dab5c73439c83d9091f2d91871e648093e/debugpy-1.8.14-cp312-cp312-win_amd64.whl", hash = "sha256:5aa56ef8538893e4502a7d79047fe39b1dae08d9ae257074c6464a7b290b806f", size = 5297249, upload-time = "2025-04-10T19:46:31.538Z" }, - { url = "https://files.pythonhosted.org/packages/4d/e4/395c792b243f2367d84202dc33689aa3d910fb9826a7491ba20fc9e261f5/debugpy-1.8.14-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:329a15d0660ee09fec6786acdb6e0443d595f64f5d096fc3e3ccf09a4259033f", size = 2485676, upload-time = "2025-04-10T19:46:32.96Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f1/6f2ee3f991327ad9e4c2f8b82611a467052a0fb0e247390192580e89f7ff/debugpy-1.8.14-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f920c7f9af409d90f5fd26e313e119d908b0dd2952c2393cd3247a462331f15", size = 4217514, upload-time = "2025-04-10T19:46:34.336Z" }, - { url = "https://files.pythonhosted.org/packages/79/28/b9d146f8f2dc535c236ee09ad3e5ac899adb39d7a19b49f03ac95d216beb/debugpy-1.8.14-cp313-cp313-win32.whl", hash = "sha256:3784ec6e8600c66cbdd4ca2726c72d8ca781e94bce2f396cc606d458146f8f4e", size = 5254756, upload-time = "2025-04-10T19:46:36.199Z" }, - { url = "https://files.pythonhosted.org/packages/e0/62/a7b4a57013eac4ccaef6977966e6bec5c63906dd25a86e35f155952e29a1/debugpy-1.8.14-cp313-cp313-win_amd64.whl", hash = "sha256:684eaf43c95a3ec39a96f1f5195a7ff3d4144e4a18d69bb66beeb1a6de605d6e", size = 5297119, upload-time = "2025-04-10T19:46:38.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/6f/96ba96545f55b6a675afa08c96b42810de9b18c7ad17446bbec82762127a/debugpy-1.8.14-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:413512d35ff52c2fb0fd2d65e69f373ffd24f0ecb1fac514c04a668599c5ce7f", size = 2077696, upload-time = "2025-04-10T19:46:46.817Z" }, - { url = "https://files.pythonhosted.org/packages/fa/84/f378a2dd837d94de3c85bca14f1db79f8fcad7e20b108b40d59da56a6d22/debugpy-1.8.14-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c9156f7524a0d70b7a7e22b2e311d8ba76a15496fb00730e46dcdeedb9e1eea", size = 3554846, upload-time = "2025-04-10T19:46:48.72Z" }, - { url = "https://files.pythonhosted.org/packages/db/52/88824fe5d6893f59933f664c6e12783749ab537a2101baf5c713164d8aa2/debugpy-1.8.14-cp39-cp39-win32.whl", hash = "sha256:b44985f97cc3dd9d52c42eb59ee9d7ee0c4e7ecd62bca704891f997de4cef23d", size = 5209350, upload-time = "2025-04-10T19:46:50.284Z" }, - { url = "https://files.pythonhosted.org/packages/41/35/72e9399be24a04cb72cfe1284572c9fcd1d742c7fa23786925c18fa54ad8/debugpy-1.8.14-cp39-cp39-win_amd64.whl", hash = "sha256:b1528cfee6c1b1c698eb10b6b096c598738a8238822d218173d21c3086de8123", size = 5241852, upload-time = "2025-04-10T19:46:52.022Z" }, - { url = "https://files.pythonhosted.org/packages/97/1a/481f33c37ee3ac8040d3d51fc4c4e4e7e61cb08b8bc8971d6032acc2279f/debugpy-1.8.14-py2.py3-none-any.whl", hash = "sha256:5cd9a579d553b6cb9759a7908a41988ee6280b961f24f63336835d9418216a20", size = 5256230, upload-time = "2025-04-10T19:46:54.077Z" }, + { url = "https://files.pythonhosted.org/packages/69/51/0b4315169f0d945271db037ae6b98c0548a2d48cc036335cd1b2f5516c1b/debugpy-1.8.15-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e9a8125c85172e3ec30985012e7a81ea5e70bbb836637f8a4104f454f9b06c97", size = 2084890, upload-time = "2025-07-15T16:43:31.239Z" }, + { url = "https://files.pythonhosted.org/packages/36/cc/a5391dedb079280d7b72418022e00ba8227ae0b5bc8b2e3d1ecffc5d6b01/debugpy-1.8.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fd0b6b5eccaa745c214fd240ea82f46049d99ef74b185a3517dad3ea1ec55d9", size = 3561470, upload-time = "2025-07-15T16:43:32.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/92/acf64b92010c66b33c077dee3862c733798a2c90e7d14b25c01d771e2a0d/debugpy-1.8.15-cp310-cp310-win32.whl", hash = "sha256:8181cce4d344010f6bfe94a531c351a46a96b0f7987750932b2908e7a1e14a55", size = 5229194, upload-time = "2025-07-15T16:43:33.997Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f5/c58c015c9ff78de35901bea3ab4dbf7946d7a4aa867ee73875df06ba6468/debugpy-1.8.15-cp310-cp310-win_amd64.whl", hash = "sha256:af2dcae4e4cd6e8b35f982ccab29fe65f7e8766e10720a717bc80c464584ee21", size = 5260900, upload-time = "2025-07-15T16:43:35.413Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b3/1c44a2ed311199ab11c2299c9474a6c7cd80d19278defd333aeb7c287995/debugpy-1.8.15-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:babc4fb1962dd6a37e94d611280e3d0d11a1f5e6c72ac9b3d87a08212c4b6dd3", size = 2183442, upload-time = "2025-07-15T16:43:36.733Z" }, + { url = "https://files.pythonhosted.org/packages/f6/69/e2dcb721491e1c294d348681227c9b44fb95218f379aa88e12a19d85528d/debugpy-1.8.15-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f778e68f2986a58479d0ac4f643e0b8c82fdd97c2e200d4d61e7c2d13838eb53", size = 3134215, upload-time = "2025-07-15T16:43:38.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/76/4ce63b95d8294dcf2fd1820860b300a420d077df4e93afcaa25a984c2ca7/debugpy-1.8.15-cp311-cp311-win32.whl", hash = "sha256:f9d1b5abd75cd965e2deabb1a06b0e93a1546f31f9f621d2705e78104377c702", size = 5154037, upload-time = "2025-07-15T16:43:39.471Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/e5a7c784465eb9c976d84408873d597dc7ce74a0fc69ed009548a1a94813/debugpy-1.8.15-cp311-cp311-win_amd64.whl", hash = "sha256:62954fb904bec463e2b5a415777f6d1926c97febb08ef1694da0e5d1463c5c3b", size = 5178133, upload-time = "2025-07-15T16:43:40.969Z" }, + { url = "https://files.pythonhosted.org/packages/ab/4a/4508d256e52897f5cdfee6a6d7580974811e911c6d01321df3264508a5ac/debugpy-1.8.15-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:3dcc7225cb317469721ab5136cda9ff9c8b6e6fb43e87c9e15d5b108b99d01ba", size = 2511197, upload-time = "2025-07-15T16:43:42.343Z" }, + { url = "https://files.pythonhosted.org/packages/99/8d/7f6ef1097e7fecf26b4ef72338d08e41644a41b7ee958a19f494ffcffc29/debugpy-1.8.15-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:047a493ca93c85ccede1dbbaf4e66816794bdc214213dde41a9a61e42d27f8fc", size = 4229517, upload-time = "2025-07-15T16:43:44.14Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e8/e8c6a9aa33a9c9c6dacbf31747384f6ed2adde4de2e9693c766bdf323aa3/debugpy-1.8.15-cp312-cp312-win32.whl", hash = "sha256:b08e9b0bc260cf324c890626961dad4ffd973f7568fbf57feb3c3a65ab6b6327", size = 5276132, upload-time = "2025-07-15T16:43:45.529Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ad/231050c6177b3476b85fcea01e565dac83607b5233d003ff067e2ee44d8f/debugpy-1.8.15-cp312-cp312-win_amd64.whl", hash = "sha256:e2a4fe357c92334272eb2845fcfcdbec3ef9f22c16cf613c388ac0887aed15fa", size = 5317645, upload-time = "2025-07-15T16:43:46.968Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/2928aad2310726d5920b18ed9f54b9f06df5aa4c10cf9b45fa18ff0ab7e8/debugpy-1.8.15-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:f5e01291ad7d6649aed5773256c5bba7a1a556196300232de1474c3c372592bf", size = 2495538, upload-time = "2025-07-15T16:43:48.927Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c6/9b8ffb4ca91fac8b2877eef63c9cc0e87dd2570b1120054c272815ec4cd0/debugpy-1.8.15-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94dc0f0d00e528d915e0ce1c78e771475b2335b376c49afcc7382ee0b146bab6", size = 4221874, upload-time = "2025-07-15T16:43:50.282Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/9b8d59674b4bf489318c7c46a1aab58e606e583651438084b7e029bf3c43/debugpy-1.8.15-cp313-cp313-win32.whl", hash = "sha256:fcf0748d4f6e25f89dc5e013d1129ca6f26ad4da405e0723a4f704583896a709", size = 5275949, upload-time = "2025-07-15T16:43:52.079Z" }, + { url = "https://files.pythonhosted.org/packages/72/83/9e58e6fdfa8710a5e6ec06c2401241b9ad48b71c0a7eb99570a1f1edb1d3/debugpy-1.8.15-cp313-cp313-win_amd64.whl", hash = "sha256:73c943776cb83e36baf95e8f7f8da765896fd94b05991e7bc162456d25500683", size = 5317720, upload-time = "2025-07-15T16:43:53.703Z" }, + { url = "https://files.pythonhosted.org/packages/90/ca/5253cc91a5380722bdf20f500cc03c3ffc78ef8e1f711788dd08a02a8a04/debugpy-1.8.15-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:085b6d0adb3eb457c2823ac497a0690b10a99eff8b01c01a041e84579f114b56", size = 2086078, upload-time = "2025-07-15T16:44:00.761Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ed/333d555e8a26792cec1d7521d7f6d4eb23f4c9e67e11ed55342c2312f188/debugpy-1.8.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd546a405381d17527814852642df0a74b7da8acc20ae5f3cfad0b7c86419511", size = 3556714, upload-time = "2025-07-15T16:44:02.188Z" }, + { url = "https://files.pythonhosted.org/packages/01/e2/699a9471a4c2bbbe5e2326e6dcd9b79d37a752c576f06ae2c27bc4f14a90/debugpy-1.8.15-cp39-cp39-win32.whl", hash = "sha256:ae0d445fe11ff4351428e6c2389e904e1cdcb4a47785da5a5ec4af6c5b95fce5", size = 5229934, upload-time = "2025-07-15T16:44:03.536Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c4/aa720b33b601b96fe482aa025e5a9eac22ff9f223756313d6f117474e366/debugpy-1.8.15-cp39-cp39-win_amd64.whl", hash = "sha256:de7db80189ca97ab4b10a87e4039cfe4dd7ddfccc8f33b5ae40fcd33792fc67a", size = 5261732, upload-time = "2025-07-15T16:44:04.97Z" }, + { url = "https://files.pythonhosted.org/packages/07/d5/98748d9860e767a1248b5e31ffa7ce8cb7006e97bf8abbf3d891d0a8ba4e/debugpy-1.8.15-py2.py3-none-any.whl", hash = "sha256:bce2e6c5ff4f2e00b98d45e7e01a49c7b489ff6df5f12d881c67d2f1ac635f3d", size = 5282697, upload-time = "2025-07-15T16:44:07.996Z" }, ] [[package]] @@ -885,7 +885,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.24.0" +version = "4.24.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -893,9 +893,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480, upload-time = "2025-05-26T18:48:10.459Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/6e/35174c1d3f30560848c82d3c233c01420e047d70925c897a4d6e932b4898/jsonschema-4.24.1.tar.gz", hash = "sha256:fe45a130cc7f67cd0d67640b4e7e3e2e666919462ae355eda238296eafeb4b5d", size = 356635, upload-time = "2025-07-17T14:40:01.05Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709, upload-time = "2025-05-26T18:48:08.417Z" }, + { url = "https://files.pythonhosted.org/packages/85/7f/ea48ffb58f9791f9d97ccb35e42fea1ebc81c67ce36dc4b8b2eee60e8661/jsonschema-4.24.1-py3-none-any.whl", hash = "sha256:6b916866aa0b61437785f1277aa2cbd63512e8d4b47151072ef13292049b4627", size = 89060, upload-time = "2025-07-17T14:39:59.471Z" }, ] [package.optional-dependencies] @@ -1174,7 +1174,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.68" +version = "0.3.69" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1185,14 +1185,14 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/20/f5b18a17bfbe3416177e702ab2fd230b7d168abb17be31fb48f43f0bb772/langchain_core-0.3.68.tar.gz", hash = "sha256:312e1932ac9aa2eaf111b70fdc171776fa571d1a86c1f873dcac88a094b19c6f", size = 563041, upload-time = "2025-07-03T17:02:28.704Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/26/c4770d3933237cde2918d502e3b0a8b6ce100b296840b632658f3e59b341/langchain_core-0.3.69.tar.gz", hash = "sha256:c132961117cc7f0227a4c58dd3e209674a6dd5b7e74abc61a0df93b0d736e283", size = 563824, upload-time = "2025-07-15T21:19:56.626Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/da/c89be0a272993bfcb762b2a356b9f55de507784c2755ad63caec25d183bf/langchain_core-0.3.68-py3-none-any.whl", hash = "sha256:5e5c1fbef419590537c91b8c2d86af896fbcbaf0d5ed7fdcdd77f7d8f3467ba0", size = 441405, upload-time = "2025-07-03T17:02:27.115Z" }, + { url = "https://files.pythonhosted.org/packages/51/7b/bb7b088440ff9cc55e9e6eba94162cbdcd3b1693c194e1ad4764acba29b9/langchain_core-0.3.69-py3-none-any.whl", hash = "sha256:383e9cb4919f7ef4b24bf8552ef42e4323c064924fea88b28dd5d7ddb740d3b8", size = 441556, upload-time = "2025-07-15T21:19:55.342Z" }, ] [[package]] name = "langgraph" -version = "0.5.3" +version = "0.6.0a1" source = { editable = "." } dependencies = [ { name = "langchain-core" }, @@ -1248,7 +1248,7 @@ dev = [ { name = "langgraph-checkpoint", editable = "../checkpoint" }, { name = "langgraph-checkpoint-postgres", editable = "../checkpoint-postgres" }, { name = "langgraph-checkpoint-sqlite", editable = "../checkpoint-sqlite" }, - { name = "langgraph-cli", extras = ["inmem"] }, + { name = "langgraph-cli", extras = ["inmem"], editable = "../cli" }, { name = "langgraph-prebuilt", editable = "../prebuilt" }, { name = "langgraph-sdk", editable = "../sdk-py" }, { name = "mypy" }, @@ -1271,7 +1271,7 @@ dev = [ [[package]] name = "langgraph-api" -version = "0.2.86" +version = "0.2.95" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle", marker = "python_full_version >= '3.11'" }, @@ -1294,9 +1294,9 @@ dependencies = [ { name = "uvicorn", marker = "python_full_version >= '3.11'" }, { name = "watchfiles", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/06/f8d6c1310772a8507dfa2c586bab8d0ab8b8cbe1f896106ee315af08fb1d/langgraph_api-0.2.86.tar.gz", hash = "sha256:220532a5a2232d32efef7e3b98be74ee6328d18f785e83949fa815ef2ac77f2f", size = 237417, upload-time = "2025-07-11T17:02:39.535Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/3d/5afd71e18806b71634e2178ba9e5e78a6678e0b8121158c9f458e57a8b9b/langgraph_api-0.2.95.tar.gz", hash = "sha256:7604cf276e592af00ab17642c053ced6f87122c53186256645593c7da4fbbfa3", size = 238773, upload-time = "2025-07-17T16:56:11.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/48/e6b774e8cfe254694b768629c72004689d94d002a54e949c23e05b776eae/langgraph_api-0.2.86-py3-none-any.whl", hash = "sha256:b20ac26ef9c5323732012eed602290ca9ca268473341dcb3282b02ed6622ec8c", size = 192498, upload-time = "2025-07-11T17:02:38.199Z" }, + { url = "https://files.pythonhosted.org/packages/7d/17/63636946f3d5d1c59b5b0a2d936d8009860227368e8868d33c72b61dfbbb/langgraph_api-0.2.95-py3-none-any.whl", hash = "sha256:25946eef80794bf92c27daf21db4af864779677bb9f92c1bc795901d7113e9ae", size = 194381, upload-time = "2025-07-17T16:56:10.275Z" }, ] [[package]] @@ -1395,16 +1395,12 @@ dev = [ [[package]] name = "langgraph-cli" version = "0.3.4" -source = { registry = "https://pypi.org/simple" } +source = { editable = "../cli" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "langgraph-sdk", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/ee/41f54032b2ab64c06e66e7f5e7a6c22d9159f2bff6bf08a38c9f11f84753/langgraph_cli-0.3.4.tar.gz", hash = "sha256:6300df4fc6f7106fd5fcdba2cbec9e8b1158daa6760d41333d1b3b5999280ad0", size = 728156, upload-time = "2025-07-08T19:52:24.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/9c/310dae8c638477e2f0e5744726d4b283878c1e11639fa693bf24b23cf7ac/langgraph_cli-0.3.4-py3-none-any.whl", hash = "sha256:b3ac9fbc67cec5d0295c23a9e7a9014f34502639fb52b2d02c89b3bb2ba36c33", size = 36525, upload-time = "2025-07-08T19:52:23.351Z" }, -] [package.optional-dependencies] inmem = [ @@ -1413,6 +1409,28 @@ inmem = [ { name = "python-dotenv" }, ] +[package.metadata] +requires-dist = [ + { name = "click", specifier = ">=8.1.7" }, + { name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.67,<0.3.0" }, + { name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.6.0" }, + { name = "langgraph-sdk", marker = "python_full_version >= '3.11'", specifier = ">=0.1.0" }, + { name = "python-dotenv", marker = "extra == 'inmem'", specifier = ">=0.8.0" }, +] +provides-extras = ["inmem"] + +[package.metadata.requires-dev] +dev = [ + { name = "codespell" }, + { name = "msgspec" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-mock" }, + { name = "pytest-watch" }, + { name = "ruff" }, +] + [[package]] name = "langgraph-prebuilt" version = "0.5.2" @@ -1446,7 +1464,7 @@ dev = [ [[package]] name = "langgraph-runtime-inmem" -version = "0.3.4" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "blockbuster", marker = "python_full_version >= '3.11'" }, @@ -1456,9 +1474,9 @@ dependencies = [ { name = "starlette", marker = "python_full_version >= '3.11'" }, { name = "structlog", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c1/17/7ff669ff44a53ab342903c2996fff75a77494af9fe56abcfbca64fe2342b/langgraph_runtime_inmem-0.3.4.tar.gz", hash = "sha256:eda7828f3ea07126e5265024b74a3fa9bf611633ad83ba3296ab9f51d89b7c0c", size = 77424, upload-time = "2025-07-01T14:45:07.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/0c/d145c6d83d36efda17b10812760711b77ec05f5bbe962c961d75b32e3c17/langgraph_runtime_inmem-0.6.0.tar.gz", hash = "sha256:b09675789a331be4a2b387c9c46de8772c4c8418e74c057b4ca24e85c25acae3", size = 77618, upload-time = "2025-07-17T16:51:01.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/0e/39c13ca7229a9425a0e5744a1d3817f80d38dd5ca7703494fb9cf836ba45/langgraph_runtime_inmem-0.3.4-py3-none-any.whl", hash = "sha256:dcb9ac68ac90b3fb1ddaf666d14a367ab70e69d5bb5589b77a72c318e29104ae", size = 29139, upload-time = "2025-07-01T14:45:06.472Z" }, + { url = "https://files.pythonhosted.org/packages/12/6a/9dc5769b5d2f97d1feacbbf93b180c359dff7462454b37dfef8aed4ebcf7/langgraph_runtime_inmem-0.6.0-py3-none-any.whl", hash = "sha256:312dab25bec6557f1edf95cb8bd7c8bb52f7f4bfeecaf66e7001662f095c9079", size = 29317, upload-time = "2025-07-17T16:51:00.622Z" }, ] [[package]] @@ -1489,7 +1507,7 @@ dev = [ [[package]] name = "langsmith" -version = "0.4.5" +version = "0.4.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1500,9 +1518,9 @@ dependencies = [ { name = "requests-toolbelt" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/92/7885823f3d13222f57773921f0da19b37d628c64607491233dc853a0f6ea/langsmith-0.4.5.tar.gz", hash = "sha256:49444bd8ccd4e46402f1b9ff1d686fa8e3a31b175e7085e72175ab8ec6164a34", size = 352235, upload-time = "2025-07-10T22:08:04.505Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/9e/11536528c6e351820ad3fca0d2807f0e0f0619ff907529c78f68ba648497/langsmith-0.4.6.tar.gz", hash = "sha256:9189dbc9c60f2086ca3a1f0110cfe3aff6b0b7c2e0e3384f9572e70502e7933c", size = 352364, upload-time = "2025-07-15T19:43:18.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/10/ad3107b666c3203b7938d10ea6b8746b9735c399cf737a51386d58e41d34/langsmith-0.4.5-py3-none-any.whl", hash = "sha256:4167717a2cccc4dff5809dbddc439628e836f6fd13d4fdb31ea013bc8d5cfaf5", size = 367795, upload-time = "2025-07-10T22:08:02.548Z" }, + { url = "https://files.pythonhosted.org/packages/a7/9b/f2be47db823e89448ea41bfd8fc5ce6a995556bd25be4c23e5b3bb5b6c9b/langsmith-0.4.6-py3-none-any.whl", hash = "sha256:900e83fe59ee672bcf2f75c8bb47cd012bf8154d92a99c0355fc38b6485cbd3e", size = 367901, upload-time = "2025-07-15T19:43:16.508Z" }, ] [[package]] @@ -1599,7 +1617,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.16.1" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, @@ -1607,39 +1625,39 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, - { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, - { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, - { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, - { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, - { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, - { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, - { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, - { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, - { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, - { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, - { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, - { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, - { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, - { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, - { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, - { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, - { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, - { url = "https://files.pythonhosted.org/packages/49/5e/ed1e6a7344005df11dfd58b0fdd59ce939a0ba9f7ed37754bf20670b74db/mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069", size = 10959511, upload-time = "2025-06-16T16:47:21.945Z" }, - { url = "https://files.pythonhosted.org/packages/30/88/a7cbc2541e91fe04f43d9e4577264b260fecedb9bccb64ffb1a34b7e6c22/mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da", size = 10075555, upload-time = "2025-06-16T16:50:14.084Z" }, - { url = "https://files.pythonhosted.org/packages/93/f7/c62b1e31a32fbd1546cca5e0a2e5f181be5761265ad1f2e94f2a306fa906/mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c", size = 11874169, upload-time = "2025-06-16T16:49:42.276Z" }, - { url = "https://files.pythonhosted.org/packages/c8/15/db580a28034657fb6cb87af2f8996435a5b19d429ea4dcd6e1c73d418e60/mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383", size = 12610060, upload-time = "2025-06-16T16:34:15.215Z" }, - { url = "https://files.pythonhosted.org/packages/ec/78/c17f48f6843048fa92d1489d3095e99324f2a8c420f831a04ccc454e2e51/mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40", size = 12875199, upload-time = "2025-06-16T16:35:14.448Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d6/ed42167d0a42680381653fd251d877382351e1bd2c6dd8a818764be3beb1/mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b", size = 9487033, upload-time = "2025-06-16T16:49:57.907Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, + { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, + { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, + { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, + { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, + { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, + { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, + { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, + { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, + { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, + { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, + { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, + { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, + { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, + { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, + { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, + { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, + { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, + { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, + { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, + { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a0/6263dd11941231f688f0a8f2faf90ceac1dc243d148d314a089d2fe25108/mypy-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:63e751f1b5ab51d6f3d219fe3a2fe4523eaa387d854ad06906c63883fde5b1ab", size = 10988185, upload-time = "2025-07-14T20:33:04.797Z" }, + { url = "https://files.pythonhosted.org/packages/02/13/b8f16d6b0dc80277129559c8e7dbc9011241a0da8f60d031edb0e6e9ac8f/mypy-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7fb09d05e0f1c329a36dcd30e27564a3555717cde87301fae4fb542402ddfad", size = 10120169, upload-time = "2025-07-14T20:32:38.84Z" }, + { url = "https://files.pythonhosted.org/packages/14/ef/978ba79df0d65af680e20d43121363cf643eb79b04bf3880d01fc8afeb6f/mypy-1.17.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b72c34ce05ac3a1361ae2ebb50757fb6e3624032d91488d93544e9f82db0ed6c", size = 11918121, upload-time = "2025-07-14T20:33:52.328Z" }, + { url = "https://files.pythonhosted.org/packages/f4/10/55ef70b104151a0d8280474f05268ff0a2a79be8d788d5e647257d121309/mypy-1.17.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434ad499ad8dde8b2f6391ddfa982f41cb07ccda8e3c67781b1bfd4e5f9450a8", size = 12648821, upload-time = "2025-07-14T20:32:59.631Z" }, + { url = "https://files.pythonhosted.org/packages/26/8c/7781fcd2e1eef48fbedd3a422c21fe300a8e03ed5be2eb4bd10246a77f4e/mypy-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f105f61a5eff52e137fd73bee32958b2add9d9f0a856f17314018646af838e97", size = 12896955, upload-time = "2025-07-14T20:32:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/78/13/03ac759dabe86e98ca7b6681f114f90ee03f3ff8365a57049d311bd4a4e3/mypy-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:ba06254a5a22729853209550d80f94e28690d5530c661f9416a68ac097b13fc4", size = 9512957, upload-time = "2025-07-14T20:33:28.619Z" }, + { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, ] [[package]] @@ -1746,81 +1764,81 @@ wheels = [ [[package]] name = "orjson" -version = "3.10.18" +version = "3.11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810, upload-time = "2025-04-29T23:30:08.423Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/87/03ababa86d984952304ac8ce9fbd3a317afb4a225b9a81f9b606ac60c873/orjson-3.11.0.tar.gz", hash = "sha256:2e4c129da624f291bcc607016a99e7f04a353f6874f3bd8d9b47b88597d5f700", size = 5318246, upload-time = "2025-07-15T16:08:29.194Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927, upload-time = "2025-04-29T23:28:08.643Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995, upload-time = "2025-04-29T23:28:11.503Z" }, - { url = "https://files.pythonhosted.org/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893, upload-time = "2025-04-29T23:28:12.751Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017, upload-time = "2025-04-29T23:28:14.498Z" }, - { url = "https://files.pythonhosted.org/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290, upload-time = "2025-04-29T23:28:16.211Z" }, - { url = "https://files.pythonhosted.org/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828, upload-time = "2025-04-29T23:28:18.065Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806, upload-time = "2025-04-29T23:28:19.782Z" }, - { url = "https://files.pythonhosted.org/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005, upload-time = "2025-04-29T23:28:21.367Z" }, - { url = "https://files.pythonhosted.org/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418, upload-time = "2025-04-29T23:28:23.097Z" }, - { url = "https://files.pythonhosted.org/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288, upload-time = "2025-04-29T23:28:25.02Z" }, - { url = "https://files.pythonhosted.org/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181, upload-time = "2025-04-29T23:28:26.318Z" }, - { url = "https://files.pythonhosted.org/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694, upload-time = "2025-04-29T23:28:28.092Z" }, - { url = "https://files.pythonhosted.org/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600, upload-time = "2025-04-29T23:28:29.422Z" }, - { url = "https://files.pythonhosted.org/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929, upload-time = "2025-04-29T23:28:30.716Z" }, - { url = "https://files.pythonhosted.org/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364, upload-time = "2025-04-29T23:28:32.392Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995, upload-time = "2025-04-29T23:28:34.024Z" }, - { url = "https://files.pythonhosted.org/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894, upload-time = "2025-04-29T23:28:35.318Z" }, - { url = "https://files.pythonhosted.org/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016, upload-time = "2025-04-29T23:28:36.674Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290, upload-time = "2025-04-29T23:28:38.3Z" }, - { url = "https://files.pythonhosted.org/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829, upload-time = "2025-04-29T23:28:39.657Z" }, - { url = "https://files.pythonhosted.org/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805, upload-time = "2025-04-29T23:28:40.969Z" }, - { url = "https://files.pythonhosted.org/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008, upload-time = "2025-04-29T23:28:42.284Z" }, - { url = "https://files.pythonhosted.org/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419, upload-time = "2025-04-29T23:28:43.673Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292, upload-time = "2025-04-29T23:28:45.573Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182, upload-time = "2025-04-29T23:28:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695, upload-time = "2025-04-29T23:28:48.564Z" }, - { url = "https://files.pythonhosted.org/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603, upload-time = "2025-04-29T23:28:50.442Z" }, - { url = "https://files.pythonhosted.org/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400, upload-time = "2025-04-29T23:28:51.838Z" }, - { url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184, upload-time = "2025-04-29T23:28:53.612Z" }, - { url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279, upload-time = "2025-04-29T23:28:55.055Z" }, - { url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799, upload-time = "2025-04-29T23:28:56.828Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791, upload-time = "2025-04-29T23:28:58.751Z" }, - { url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059, upload-time = "2025-04-29T23:29:00.129Z" }, - { url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359, upload-time = "2025-04-29T23:29:01.704Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853, upload-time = "2025-04-29T23:29:03.576Z" }, - { url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131, upload-time = "2025-04-29T23:29:05.753Z" }, - { url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834, upload-time = "2025-04-29T23:29:07.35Z" }, - { url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368, upload-time = "2025-04-29T23:29:09.301Z" }, - { url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359, upload-time = "2025-04-29T23:29:10.813Z" }, - { url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466, upload-time = "2025-04-29T23:29:12.26Z" }, - { url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683, upload-time = "2025-04-29T23:29:13.865Z" }, - { url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754, upload-time = "2025-04-29T23:29:15.338Z" }, - { url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218, upload-time = "2025-04-29T23:29:17.324Z" }, - { url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087, upload-time = "2025-04-29T23:29:19.083Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273, upload-time = "2025-04-29T23:29:20.602Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779, upload-time = "2025-04-29T23:29:22.062Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811, upload-time = "2025-04-29T23:29:23.602Z" }, - { url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018, upload-time = "2025-04-29T23:29:25.094Z" }, - { url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368, upload-time = "2025-04-29T23:29:26.609Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840, upload-time = "2025-04-29T23:29:28.153Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135, upload-time = "2025-04-29T23:29:29.726Z" }, - { url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810, upload-time = "2025-04-29T23:29:31.269Z" }, - { url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491, upload-time = "2025-04-29T23:29:33.315Z" }, - { url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277, upload-time = "2025-04-29T23:29:34.946Z" }, - { url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367, upload-time = "2025-04-29T23:29:36.52Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687, upload-time = "2025-04-29T23:29:38.292Z" }, - { url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794, upload-time = "2025-04-29T23:29:40.349Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186, upload-time = "2025-04-29T23:29:41.922Z" }, - { url = "https://files.pythonhosted.org/packages/df/db/69488acaa2316788b7e171f024912c6fe8193aa2e24e9cfc7bc41c3669ba/orjson-3.10.18-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95fae14225edfd699454e84f61c3dd938df6629a00c6ce15e704f57b58433bb", size = 249301, upload-time = "2025-04-29T23:29:44.719Z" }, - { url = "https://files.pythonhosted.org/packages/23/21/d816c44ec5d1482c654e1d23517d935bb2716e1453ff9380e861dc6efdd3/orjson-3.10.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5232d85f177f98e0cefabb48b5e7f60cff6f3f0365f9c60631fecd73849b2a82", size = 136786, upload-time = "2025-04-29T23:29:46.517Z" }, - { url = "https://files.pythonhosted.org/packages/a5/9f/f68d8a9985b717e39ba7bf95b57ba173fcd86aeca843229ec60d38f1faa7/orjson-3.10.18-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2783e121cafedf0d85c148c248a20470018b4ffd34494a68e125e7d5857655d1", size = 132711, upload-time = "2025-04-29T23:29:48.605Z" }, - { url = "https://files.pythonhosted.org/packages/b5/63/447f5955439bf7b99bdd67c38a3f689d140d998ac58e3b7d57340520343c/orjson-3.10.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e54ee3722caf3db09c91f442441e78f916046aa58d16b93af8a91500b7bbf273", size = 136841, upload-time = "2025-04-29T23:29:50.31Z" }, - { url = "https://files.pythonhosted.org/packages/68/9e/4855972f2be74097242e4681ab6766d36638a079e09d66f3d6a5d1188ce7/orjson-3.10.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2daf7e5379b61380808c24f6fc182b7719301739e4271c3ec88f2984a2d61f89", size = 138082, upload-time = "2025-04-29T23:29:51.992Z" }, - { url = "https://files.pythonhosted.org/packages/08/0f/e68431e53a39698d2355faf1f018c60a3019b4b54b4ea6be9dc6b8208a3d/orjson-3.10.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f39b371af3add20b25338f4b29a8d6e79a8c7ed0e9dd49e008228a065d07781", size = 142618, upload-time = "2025-04-29T23:29:53.642Z" }, - { url = "https://files.pythonhosted.org/packages/32/da/bdcfff239ddba1b6ef465efe49d7e43cc8c30041522feba9fd4241d47c32/orjson-3.10.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b819ed34c01d88c6bec290e6842966f8e9ff84b7694632e88341363440d4cc0", size = 132627, upload-time = "2025-04-29T23:29:55.318Z" }, - { url = "https://files.pythonhosted.org/packages/0c/28/bc634da09bbe972328f615b0961f1e7d91acb3cc68bddbca9e8dd64e8e24/orjson-3.10.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2f6c57debaef0b1aa13092822cbd3698a1fb0209a9ea013a969f4efa36bdea57", size = 134832, upload-time = "2025-04-29T23:29:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d2/e8ac0c2d0ec782ed8925b4eb33f040cee1f1fbd1d8b268aeb84b94153e49/orjson-3.10.18-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:755b6d61ffdb1ffa1e768330190132e21343757c9aa2308c67257cc81a1a6f5a", size = 413161, upload-time = "2025-04-29T23:29:59.148Z" }, - { url = "https://files.pythonhosted.org/packages/28/f0/397e98c352a27594566e865999dc6b88d6f37d5bbb87b23c982af24114c4/orjson-3.10.18-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce8d0a875a85b4c8579eab5ac535fb4b2a50937267482be402627ca7e7570ee3", size = 153012, upload-time = "2025-04-29T23:30:01.066Z" }, - { url = "https://files.pythonhosted.org/packages/93/bf/2c7334caeb48bdaa4cae0bde17ea417297ee136598653b1da7ae1f98c785/orjson-3.10.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57b5d0673cbd26781bebc2bf86f99dd19bd5a9cb55f71cc4f66419f6b50f3d77", size = 136999, upload-time = "2025-04-29T23:30:02.93Z" }, - { url = "https://files.pythonhosted.org/packages/35/72/4827b1c0c31621c2aa1e661a899cdd2cfac0565c6cd7131890daa4ef7535/orjson-3.10.18-cp39-cp39-win32.whl", hash = "sha256:951775d8b49d1d16ca8818b1f20c4965cae9157e7b562a2ae34d3967b8f21c8e", size = 142560, upload-time = "2025-04-29T23:30:04.805Z" }, - { url = "https://files.pythonhosted.org/packages/72/91/ef8e76868e7eed478887c82f60607a8abf58dadd24e95817229a4b2e2639/orjson-3.10.18-cp39-cp39-win_amd64.whl", hash = "sha256:fdd9d68f83f0bc4406610b1ac68bdcded8c5ee58605cc69e643a06f4d075f429", size = 134455, upload-time = "2025-04-29T23:30:06.588Z" }, + { url = "https://files.pythonhosted.org/packages/07/aa/50818f480f0edcb33290c8f35eef6dd3a31e2ff7e1195f8b236ac7419811/orjson-3.11.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b8913baba9751f7400f8fa4ec18a8b618ff01177490842e39e47b66c1b04bc79", size = 240422, upload-time = "2025-07-15T16:06:23.029Z" }, + { url = "https://files.pythonhosted.org/packages/16/50/5235aff455fa76337493d21e68618e7cf53aa9db011aaeb06cf378f1344c/orjson-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4d86910554de5c9c87bc560b3bdd315cc3988adbdc2acf5dda3797079407ed", size = 132473, upload-time = "2025-07-15T16:06:25.598Z" }, + { url = "https://files.pythonhosted.org/packages/23/93/bf1c4e77e7affc46cca13fb852842a86dca2dabbee1d91515ed17b1c21c4/orjson-3.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84ae3d329360cf18fb61b67c505c00dedb61b0ee23abfd50f377a58e7d7bed06", size = 127195, upload-time = "2025-07-15T16:06:27.001Z" }, + { url = "https://files.pythonhosted.org/packages/7e/2d/64b52c6827e43aa3d98def19e188e091a6c574ca13d9ecef5f3f3284fac6/orjson-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47a54e660414baacd71ebf41a69bb17ea25abb3c5b69ce9e13e43be7ac20e342", size = 128895, upload-time = "2025-07-15T16:06:28.641Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5f/9d290bc7a88392f9f7dc2e92ceb2e3efbbebaaf56bbba655b5fe2e3d2ca3/orjson-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2560b740604751854be146169c1de7e7ee1e6120b00c1788ec3f3a012c6a243f", size = 132016, upload-time = "2025-07-15T16:06:32.576Z" }, + { url = "https://files.pythonhosted.org/packages/ef/8c/b2bdc34649bbb7b44827d487aef7ad4d6a96c53ebc490ddcc191d47bc3b9/orjson-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7f9cd995da9e46fbac0a371f0ff6e89a21d8ecb7a8a113c0acb147b0a32f73", size = 134251, upload-time = "2025-07-15T16:06:34.075Z" }, + { url = "https://files.pythonhosted.org/packages/33/be/b763b602976aa27407e6f75331ac581258c719f8abb70f66f2de962f649f/orjson-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cf728cb3a013bdf9f4132575404bf885aa773d8bb4205656575e1890fc91990", size = 128078, upload-time = "2025-07-15T16:06:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/ac/24/1b0fed70392bf179ac8b5abe800f1102ed94f89ac4f889d83916947a2b4e/orjson-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c27de273320294121200440cd5002b6aeb922d3cb9dab3357087c69f04ca6934", size = 130734, upload-time = "2025-07-15T16:06:36.832Z" }, + { url = "https://files.pythonhosted.org/packages/05/d2/2d042bb4fe1da067692cb70d8c01a5ce2737e2f56444e6b2d716853ce8c3/orjson-3.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4430ec6ff1a1f4595dd7e0fad991bdb2fed65401ed294984c490ffa025926325", size = 404040, upload-time = "2025-07-15T16:06:38.259Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c5/54938ab416c0d19c93f0d6977a47bb2b3d121e150305380b783f7d6da185/orjson-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:325be41a8d7c227d460a9795a181511ba0e731cf3fee088c63eb47e706ea7559", size = 144808, upload-time = "2025-07-15T16:06:39.796Z" }, + { url = "https://files.pythonhosted.org/packages/6d/be/5ead422f396ee7c8941659ceee3da001e26998971f7d5fe0a38519c48aa5/orjson-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9760217b84d1aee393b4436fbe9c639e963ec7bc0f2c074581ce5fb3777e466", size = 132570, upload-time = "2025-07-15T16:06:41.209Z" }, + { url = "https://files.pythonhosted.org/packages/f6/01/db8352f7d0374d7eec25144e294991800aa85738b2dc7f19cc152ba1b254/orjson-3.11.0-cp310-cp310-win32.whl", hash = "sha256:fe36e5012f886ff91c68b87a499c227fa220e9668cea96335219874c8be5fab5", size = 134763, upload-time = "2025-07-15T16:06:42.524Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f5/1322b64d5836d92f0b0c119d959853b3c968b8aae23dd1e3c1bfa566823b/orjson-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebeecd5d5511b3ca9dc4e7db0ab95266afd41baf424cc2fad8c2d3a3cdae650a", size = 129506, upload-time = "2025-07-15T16:06:43.929Z" }, + { url = "https://files.pythonhosted.org/packages/f9/2c/0b71a763f0f5130aa2631ef79e2cd84d361294665acccbb12b7a9813194e/orjson-3.11.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1785df7ada75c18411ff7e20ac822af904a40161ea9dfe8c55b3f6b66939add6", size = 240007, upload-time = "2025-07-15T16:06:45.411Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5a/f79ccd63d378b9c7c771d7a54c203d261b4c618fe3034ae95cd30f934f34/orjson-3.11.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:a57899bebbcea146616a2426d20b51b3562b4bc9f8039a3bd14fae361c23053d", size = 129320, upload-time = "2025-07-15T16:06:47.249Z" }, + { url = "https://files.pythonhosted.org/packages/7b/8a/63dafc147fa5ba945ad809c374b8f4ee692bb6b18aa6e161c3e6b69b594e/orjson-3.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fbc2fc825aff1456dd358c11a0ad7912a4cb4537d3db92e5334af7463a967", size = 132254, upload-time = "2025-07-15T16:06:48.597Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/4d1eb230483cc689a2f039c531bb2c980029c40ca5a9b5f64dce9786e955/orjson-3.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4305a638f4cf9bed3746ca3b7c242f14e05177d5baec2527026e0f9ee6c24fb7", size = 127003, upload-time = "2025-07-15T16:06:50.34Z" }, + { url = "https://files.pythonhosted.org/packages/4f/39/b6e96072946d908684e0f4b3de1639062fd5b32016b2929c035bd8e5c847/orjson-3.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1235fe7bbc37164f69302199d46f29cfb874018738714dccc5a5a44042c79c77", size = 128674, upload-time = "2025-07-15T16:06:51.659Z" }, + { url = "https://files.pythonhosted.org/packages/1e/dd/c77e3013f35b202ec2cc1f78a95fadf86b8c5a320d56eb1a0bbb965a87bb/orjson-3.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a640e3954e7b4fcb160097551e54cafbde9966be3991932155b71071077881aa", size = 131846, upload-time = "2025-07-15T16:06:53.359Z" }, + { url = "https://files.pythonhosted.org/packages/3f/7d/d83f0f96c2b142f9cdcf12df19052ea3767970989dc757598dc108db208f/orjson-3.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d750b97d22d5566955e50b02c622f3a1d32744d7a578c878b29a873190ccb7a", size = 134016, upload-time = "2025-07-15T16:06:54.691Z" }, + { url = "https://files.pythonhosted.org/packages/67/4f/d22f79a3c56dde563c4fbc12eebf9224a1b87af5e4ec61beb11f9b3eb499/orjson-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfcfe498484161e011f8190a400591c52b026de96b3b3cbd3f21e8999b9dc0e", size = 127930, upload-time = "2025-07-15T16:06:56.001Z" }, + { url = "https://files.pythonhosted.org/packages/07/1e/26aede257db2163d974139fd4571f1e80f565216ccbd2c44ee1d43a63dcc/orjson-3.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:feaed3ed43a1d2df75c039798eb5ec92c350c7d86be53369bafc4f3700ce7df2", size = 130569, upload-time = "2025-07-15T16:06:57.275Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bf/2cb57eac8d6054b555cba27203490489a7d3f5dca8c34382f22f2f0f17ba/orjson-3.11.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa1120607ec8fc98acf8c54aac6fb0b7b003ba883401fa2d261833111e2fa071", size = 403844, upload-time = "2025-07-15T16:06:59.107Z" }, + { url = "https://files.pythonhosted.org/packages/76/34/36e859ccfc45464df7b35c438c0ecc7751c930b3ebbefb50db7e3a641eb7/orjson-3.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c4b48d9775b0cf1f0aca734f4c6b272cbfacfac38e6a455e6520662f9434afb7", size = 144613, upload-time = "2025-07-15T16:07:00.48Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/5aeb84cdd0b44dc3972668944a1312f7983c2a45fb6b0e5e32b2f9408540/orjson-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f018ed1986d79434ac712ff19f951cd00b4dfcb767444410fbb834ebec160abf", size = 132419, upload-time = "2025-07-15T16:07:01.927Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/95ee1e61a067ad24c4921609156b3beeca8b102f6f36dca62b08e1a7c7a8/orjson-3.11.0-cp311-cp311-win32.whl", hash = "sha256:08e191f8a55ac2c00be48e98a5d10dca004cbe8abe73392c55951bfda60fc123", size = 134620, upload-time = "2025-07-15T16:07:03.304Z" }, + { url = "https://files.pythonhosted.org/packages/94/3e/afd5e284db9387023803553061ea05c785c36fe7845e4fe25912424b343f/orjson-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:b5a4214ea59c8a3b56f8d484b28114af74e9fba0956f9be5c3ce388ae143bf1f", size = 129333, upload-time = "2025-07-15T16:07:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a4/d29e9995d73f23f2444b4db299a99477a4f7e6f5bf8923b775ef43a4e660/orjson-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:57e8e7198a679ab21241ab3f355a7990c7447559e35940595e628c107ef23736", size = 126656, upload-time = "2025-07-15T16:07:06.288Z" }, + { url = "https://files.pythonhosted.org/packages/92/c9/241e304fb1e58ea70b720f1a9e5349c6bb7735ffac401ef1b94f422edd6d/orjson-3.11.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b4089f940c638bb1947d54e46c1cd58f4259072fcc97bc833ea9c78903150ac9", size = 240269, upload-time = "2025-07-15T16:07:08.173Z" }, + { url = "https://files.pythonhosted.org/packages/26/7c/289457cdf40be992b43f1d90ae213ebc03a31a8e2850271ecd79e79a3135/orjson-3.11.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:8335a0ba1c26359fb5c82d643b4c1abbee2bc62875e0f2b5bde6c8e9e25eb68c", size = 129276, upload-time = "2025-07-15T16:07:10.128Z" }, + { url = "https://files.pythonhosted.org/packages/66/de/5c0528d46ded965939b6b7f75b1fe93af42b9906b0039096fc92c9001c12/orjson-3.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63c1c9772dafc811d16d6a7efa3369a739da15d1720d6e58ebe7562f54d6f4a2", size = 131966, upload-time = "2025-07-15T16:07:11.509Z" }, + { url = "https://files.pythonhosted.org/packages/ad/74/39822f267b5935fb6fc961ccc443f4968a74d34fc9270b83caa44e37d907/orjson-3.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9457ccbd8b241fb4ba516417a4c5b95ba0059df4ac801309bcb4ec3870f45ad9", size = 127028, upload-time = "2025-07-15T16:07:13.023Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e3/28f6ed7f03db69bddb3ef48621b2b05b394125188f5909ee0a43fcf4820e/orjson-3.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0846e13abe79daece94a00b92574f294acad1d362be766c04245b9b4dd0e47e1", size = 129105, upload-time = "2025-07-15T16:07:14.367Z" }, + { url = "https://files.pythonhosted.org/packages/cb/50/8867fd2fc92c0ab1c3e14673ec5d9d0191202e4ab8ba6256d7a1d6943ad3/orjson-3.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5587c85ae02f608a3f377b6af9eb04829606f518257cbffa8f5081c1aacf2e2f", size = 131902, upload-time = "2025-07-15T16:07:16.176Z" }, + { url = "https://files.pythonhosted.org/packages/13/65/c189deea10342afee08006331082ff67d11b98c2394989998b3ea060354a/orjson-3.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7a1964a71c1567b4570c932a0084ac24ad52c8cf6253d1881400936565ed438", size = 134042, upload-time = "2025-07-15T16:07:17.937Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e4/cf23c3f4231d2a9a043940ab045f799f84a6df1b4fb6c9b4412cdc3ebf8c/orjson-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5a8243e73690cc6e9151c9e1dd046a8f21778d775f7d478fa1eb4daa4897c61", size = 128260, upload-time = "2025-07-15T16:07:19.651Z" }, + { url = "https://files.pythonhosted.org/packages/de/b9/2cb94d3a67edb918d19bad4a831af99cd96c3657a23daa239611bcf335d7/orjson-3.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51646f6d995df37b6e1b628f092f41c0feccf1d47e3452c6e95e2474b547d842", size = 130282, upload-time = "2025-07-15T16:07:21.022Z" }, + { url = "https://files.pythonhosted.org/packages/0b/96/df963cc973e689d4c56398647917b4ee95f47e5b6d2779338c09c015b23b/orjson-3.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2fb8ca8f0b4e31b8aaec674c7540649b64ef02809410506a44dc68d31bd5647b", size = 403765, upload-time = "2025-07-15T16:07:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/fb/92/71429ee1badb69f53281602dbb270fa84fc2e51c83193a814d0208bb63b0/orjson-3.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:64a6a3e94a44856c3f6557e6aa56a6686544fed9816ae0afa8df9077f5759791", size = 144779, upload-time = "2025-07-15T16:07:27.339Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ab/3678b2e5ff0c622a974cb8664ed7cdda5ed26ae2b9d71ba66ec36f32d6cf/orjson-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d69f95d484938d8fab5963e09131bcf9fbbb81fa4ec132e316eb2fb9adb8ce78", size = 132797, upload-time = "2025-07-15T16:07:28.717Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/74509f715ff189d2aca90ebb0bd5af6658e0f9aa2512abbe6feca4c78208/orjson-3.11.0-cp312-cp312-win32.whl", hash = "sha256:8514f9f9c667ce7d7ef709ab1a73e7fcab78c297270e90b1963df7126d2b0e23", size = 134695, upload-time = "2025-07-15T16:07:30.034Z" }, + { url = "https://files.pythonhosted.org/packages/82/ba/ef25e3e223f452a01eac6a5b38d05c152d037508dcbf87ad2858cbb7d82e/orjson-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:41b38a894520b8cb5344a35ffafdf6ae8042f56d16771b2c5eb107798cee85ee", size = 129446, upload-time = "2025-07-15T16:07:31.412Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cd/6f4d93867c5d81bb4ab2d4ac870d3d6e9ba34fa580a03b8d04bf1ce1d8ad/orjson-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:5579acd235dd134467340b2f8a670c1c36023b5a69c6a3174c4792af7502bd92", size = 126400, upload-time = "2025-07-15T16:07:34.143Z" }, + { url = "https://files.pythonhosted.org/packages/31/63/82d9b6b48624009d230bc6038e54778af8f84dfd54402f9504f477c5cfd5/orjson-3.11.0-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4a8ba9698655e16746fdf5266939427da0f9553305152aeb1a1cc14974a19cfb", size = 240125, upload-time = "2025-07-15T16:07:35.976Z" }, + { url = "https://files.pythonhosted.org/packages/16/3a/d557ed87c63237d4c97a7bac7ac054c347ab8c4b6da09748d162ca287175/orjson-3.11.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:67133847f9a35a5ef5acfa3325d4a2f7fe05c11f1505c4117bb086fc06f2a58f", size = 129189, upload-time = "2025-07-15T16:07:37.486Z" }, + { url = "https://files.pythonhosted.org/packages/69/5e/b2c9e22e2cd10aa7d76a629cee65d661e06a61fbaf4dc226386f5636dd44/orjson-3.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f797d57814975b78f5f5423acb003db6f9be5186b72d48bd97a1000e89d331d", size = 131953, upload-time = "2025-07-15T16:07:39.254Z" }, + { url = "https://files.pythonhosted.org/packages/e2/60/760fcd9b50eb44d1206f2b30c8d310b79714553b9d94a02f9ea3252ebe63/orjson-3.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:28acd19822987c5163b9e03a6e60853a52acfee384af2b394d11cb413b889246", size = 126922, upload-time = "2025-07-15T16:07:41.282Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/8c46daa867ccc92da6de9567608be62052774b924a77c78382e30d50b579/orjson-3.11.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8d38d9e1e2cf9729658e35956cf01e13e89148beb4cb9e794c9c10c5cb252f8", size = 128787, upload-time = "2025-07-15T16:07:42.681Z" }, + { url = "https://files.pythonhosted.org/packages/f2/14/a2f1b123d85f11a19e8749f7d3f9ed6c9b331c61f7b47cfd3e9a1fedb9bc/orjson-3.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05f094edd2b782650b0761fd78858d9254de1c1286f5af43145b3d08cdacfd51", size = 131895, upload-time = "2025-07-15T16:07:44.519Z" }, + { url = "https://files.pythonhosted.org/packages/c8/10/362e8192df7528e8086ea712c5cb01355c8d4e52c59a804417ba01e2eb2d/orjson-3.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d09176a4a9e04a5394a4a0edd758f645d53d903b306d02f2691b97d5c736a9e", size = 133868, upload-time = "2025-07-15T16:07:46.227Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4e/ef43582ef3e3dfd2a39bc3106fa543364fde1ba58489841120219da6e22f/orjson-3.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a585042104e90a61eda2564d11317b6a304eb4e71cd33e839f5af6be56c34d3", size = 128234, upload-time = "2025-07-15T16:07:48.123Z" }, + { url = "https://files.pythonhosted.org/packages/d7/fa/02dabb2f1d605bee8c4bb1160cfc7467976b1ed359a62cc92e0681b53c45/orjson-3.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2218629dbfdeeb5c9e0573d59f809d42f9d49ae6464d2f479e667aee14c3ef4", size = 130232, upload-time = "2025-07-15T16:07:50.197Z" }, + { url = "https://files.pythonhosted.org/packages/16/76/951b5619605c8d2ede80cc989f32a66abc954530d86e84030db2250c63a1/orjson-3.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:613e54a2b10b51b656305c11235a9c4a5c5491ef5c283f86483d4e9e123ed5e4", size = 403648, upload-time = "2025-07-15T16:07:52.136Z" }, + { url = "https://files.pythonhosted.org/packages/96/e2/5fa53bb411455a63b3713db90b588e6ca5ed2db59ad49b3fb8a0e94e0dda/orjson-3.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9dac7fbf3b8b05965986c5cfae051eb9a30fced7f15f1d13a5adc608436eb486", size = 144572, upload-time = "2025-07-15T16:07:54.004Z" }, + { url = "https://files.pythonhosted.org/packages/ad/d0/7d6f91e1e0f034258c3a3358f20b0c9490070e8a7ab8880085547274c7f9/orjson-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93b64b254414e2be55ac5257124b5602c5f0b4d06b80bd27d1165efe8f36e836", size = 132766, upload-time = "2025-07-15T16:07:55.936Z" }, + { url = "https://files.pythonhosted.org/packages/ff/f8/4d46481f1b3fb40dc826d62179f96c808eb470cdcc74b6593fb114d74af3/orjson-3.11.0-cp313-cp313-win32.whl", hash = "sha256:359cbe11bc940c64cb3848cf22000d2aef36aff7bfd09ca2c0b9cb309c387132", size = 134638, upload-time = "2025-07-15T16:07:57.343Z" }, + { url = "https://files.pythonhosted.org/packages/85/3f/544938dcfb7337d85ee1e43d7685cf8f3bfd452e0b15a32fe70cb4ca5094/orjson-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:0759b36428067dc777b202dd286fbdd33d7f261c6455c4238ea4e8474358b1e6", size = 129411, upload-time = "2025-07-15T16:07:58.852Z" }, + { url = "https://files.pythonhosted.org/packages/43/0c/f75015669d7817d222df1bb207f402277b77d22c4833950c8c8c7cf2d325/orjson-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:51cdca2f36e923126d0734efaf72ddbb5d6da01dbd20eab898bdc50de80d7b5a", size = 126349, upload-time = "2025-07-15T16:08:00.322Z" }, + { url = "https://files.pythonhosted.org/packages/6c/41/eac31c44ce001b3da8a6b5ebbb8a4fc2c3eaf479e2d068e36b2ea6ab7095/orjson-3.11.0-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d79c180cfb3ae68f13245d0ff551dca03d96258aa560830bf8a223bd68d8272c", size = 241023, upload-time = "2025-07-15T16:08:02.233Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d6/1edc258f3eff573af7416b2b8536032e6f4ed3759fa5773c5db95a28d2f2/orjson-3.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:105bca887532dc71ce4b05a5de95dea447a310409d7a8cf0cb1c4a120469e9ad", size = 132245, upload-time = "2025-07-15T16:08:04.734Z" }, + { url = "https://files.pythonhosted.org/packages/24/89/49236838cdc8d88b93f1c80f44531103f589307e4e783c855a6a63f28b45/orjson-3.11.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acf5a63ae9cdb88274126af85913ceae554d8fd71122effa24a53227abbeee16", size = 126981, upload-time = "2025-07-15T16:08:06.114Z" }, + { url = "https://files.pythonhosted.org/packages/80/78/8744b86efae7693344edcf255addc2a9f9e4f5552ccf71d9581d03c3e1aa/orjson-3.11.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:894635df36c0be32f1c8c8607e853b8865edb58e7618e57892e85d06418723eb", size = 128686, upload-time = "2025-07-15T16:08:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/91/8c/4c45feee9fa52488e67be2e887eb966337d4ddb6675129471f0dab98587d/orjson-3.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02dd4f0a1a2be943a104ce5f3ec092631ee3e9f0b4bb9eeee3400430bd94ddef", size = 131830, upload-time = "2025-07-15T16:08:14.423Z" }, + { url = "https://files.pythonhosted.org/packages/47/15/9462308306650de38d042af226e186d2fe28ee8e44c5462e011e767e6e44/orjson-3.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:720b4bb5e1b971960a62c2fa254c2d2a14e7eb791e350d05df8583025aa59d15", size = 134004, upload-time = "2025-07-15T16:08:16.024Z" }, + { url = "https://files.pythonhosted.org/packages/db/1d/bfa55d7681cf704d73e9c6de8138535b2f41e06a49d88bf9bdf27c8d4d7b/orjson-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bf058105a8aed144e0d1cfe7ac4174748c3fc7203f225abaeac7f4121abccb0", size = 127893, upload-time = "2025-07-15T16:08:17.558Z" }, + { url = "https://files.pythonhosted.org/packages/1c/bb/e91aa9e63077d8754d1578787e8917078e5c6743579290bc454bbc609241/orjson-3.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a2788f741e5a0e885e5eaf1d91d0c9106e03cb9575b0c55ba36fd3d48b0b1e9b", size = 130546, upload-time = "2025-07-15T16:08:19.21Z" }, + { url = "https://files.pythonhosted.org/packages/9d/67/4c53a325ac9abf883e922da214707f63efcb8b4d54529984df0e6aff1d0b/orjson-3.11.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c60c99fe1e15894367b0340b2ff16c7c69f9c3f3a54aa3961a58c102b292ad94", size = 403849, upload-time = "2025-07-15T16:08:21.025Z" }, + { url = "https://files.pythonhosted.org/packages/5a/64/a779341bd2231e28eb09cf6e6260d9f713a39ae5163b0f1228ab5175bfee/orjson-3.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:99d17aab984f4d029b8f3c307e6be3c63d9ee5ef55e30d761caf05e883009949", size = 144600, upload-time = "2025-07-15T16:08:22.701Z" }, + { url = "https://files.pythonhosted.org/packages/03/c1/fc36a6e3b40df3388ecf57b18a940f6584362652e6ee57464ccc5715b2e3/orjson-3.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e98f02e23611763c9e5dfcb83bd33219231091589f0d1691e721aea9c52bf329", size = 132416, upload-time = "2025-07-15T16:08:24.258Z" }, + { url = "https://files.pythonhosted.org/packages/3b/29/eb5ed777d7ea5d0fdee5981751e3a4e9de73f47e32bb20f1ea748b04b1d2/orjson-3.11.0-cp39-cp39-win32.whl", hash = "sha256:923301f33ea866b18f8836cf41d9c6d33e3b5cab8577d20fed34ec29f0e13a0d", size = 134617, upload-time = "2025-07-15T16:08:26.052Z" }, + { url = "https://files.pythonhosted.org/packages/72/40/feba627d9349bb1a91500e0047ae526d83bb1918545ff4dfee3e1bd7195e/orjson-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:475491bb78af2a0170f49e90013f1a0f1286527f3617491f8940d7e5da862da7", size = 129320, upload-time = "2025-07-15T16:08:27.484Z" }, ] [[package]] @@ -1882,11 +1900,11 @@ wheels = [ [[package]] name = "packaging" -version = "24.2" +version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] @@ -2445,23 +2463,27 @@ wheels = [ [[package]] name = "pywin32" -version = "310" +version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/da/a5f38fffbba2fb99aa4aa905480ac4b8e83ca486659ac8c95bce47fb5276/pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1", size = 8848240, upload-time = "2025-03-17T00:55:46.783Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fe/d873a773324fa565619ba555a82c9dabd677301720f3660a731a5d07e49a/pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d", size = 9601854, upload-time = "2025-03-17T00:55:48.783Z" }, - { url = "https://files.pythonhosted.org/packages/3c/84/1a8e3d7a15490d28a5d816efa229ecb4999cdc51a7c30dd8914f669093b8/pywin32-310-cp310-cp310-win_arm64.whl", hash = "sha256:33babed0cf0c92a6f94cc6cc13546ab24ee13e3e800e61ed87609ab91e4c8213", size = 8522963, upload-time = "2025-03-17T00:55:50.969Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284, upload-time = "2025-03-17T00:55:53.124Z" }, - { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748, upload-time = "2025-03-17T00:55:55.203Z" }, - { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941, upload-time = "2025-03-17T00:55:57.048Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239, upload-time = "2025-03-17T00:55:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839, upload-time = "2025-03-17T00:56:00.8Z" }, - { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470, upload-time = "2025-03-17T00:56:02.601Z" }, - { url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384, upload-time = "2025-03-17T00:56:04.383Z" }, - { url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039, upload-time = "2025-03-17T00:56:06.207Z" }, - { url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152, upload-time = "2025-03-17T00:56:07.819Z" }, - { url = "https://files.pythonhosted.org/packages/a2/cd/d09d434630edb6a0c44ad5079611279a67530296cfe0451e003de7f449ff/pywin32-310-cp39-cp39-win32.whl", hash = "sha256:851c8d927af0d879221e616ae1f66145253537bbdd321a77e8ef701b443a9a1a", size = 8848099, upload-time = "2025-03-17T00:55:42.415Z" }, - { url = "https://files.pythonhosted.org/packages/93/ff/2a8c10315ffbdee7b3883ac0d1667e267ca8b3f6f640d81d43b87a82c0c7/pywin32-310-cp39-cp39-win_amd64.whl", hash = "sha256:96867217335559ac619f00ad70e513c0fcf84b8a3af9fc2bba3b59b97da70475", size = 9602031, upload-time = "2025-03-17T00:55:44.512Z" }, + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, + { url = "https://files.pythonhosted.org/packages/59/42/b86689aac0cdaee7ae1c58d464b0ff04ca909c19bb6502d4973cdd9f9544/pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b", size = 8760837, upload-time = "2025-07-14T20:12:59.59Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8a/1403d0353f8c5a2f0829d2b1c4becbf9da2f0a4d040886404fc4a5431e4d/pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91", size = 9590187, upload-time = "2025-07-14T20:13:01.419Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/e0e8d802f124772cec9c75430b01a212f86f9de7546bda715e54140d5aeb/pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d", size = 8778162, upload-time = "2025-07-14T20:13:03.544Z" }, ] [[package]] @@ -2821,27 +2843,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.3" +version = "0.12.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/2a/43955b530c49684d3c38fcda18c43caf91e99204c2a065552528e0552d4f/ruff-0.12.3.tar.gz", hash = "sha256:f1b5a4b6668fd7b7ea3697d8d98857390b40c1320a63a178eee6be0899ea2d77", size = 4459341, upload-time = "2025-07-11T13:21:16.086Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/fd/b44c5115539de0d598d75232a1cc7201430b6891808df111b8b0506aae43/ruff-0.12.3-py3-none-linux_armv6l.whl", hash = "sha256:47552138f7206454eaf0c4fe827e546e9ddac62c2a3d2585ca54d29a890137a2", size = 10430499, upload-time = "2025-07-11T13:20:26.321Z" }, - { url = "https://files.pythonhosted.org/packages/43/c5/9eba4f337970d7f639a37077be067e4ec80a2ad359e4cc6c5b56805cbc66/ruff-0.12.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0a9153b000c6fe169bb307f5bd1b691221c4286c133407b8827c406a55282041", size = 11213413, upload-time = "2025-07-11T13:20:30.017Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2c/fac3016236cf1fe0bdc8e5de4f24c76ce53c6dd9b5f350d902549b7719b2/ruff-0.12.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fa6b24600cf3b750e48ddb6057e901dd5b9aa426e316addb2a1af185a7509882", size = 10586941, upload-time = "2025-07-11T13:20:33.046Z" }, - { url = "https://files.pythonhosted.org/packages/c5/0f/41fec224e9dfa49a139f0b402ad6f5d53696ba1800e0f77b279d55210ca9/ruff-0.12.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2506961bf6ead54887ba3562604d69cb430f59b42133d36976421bc8bd45901", size = 10783001, upload-time = "2025-07-11T13:20:35.534Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ca/dd64a9ce56d9ed6cad109606ac014860b1c217c883e93bf61536400ba107/ruff-0.12.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4faaff1f90cea9d3033cbbcdf1acf5d7fb11d8180758feb31337391691f3df0", size = 10269641, upload-time = "2025-07-11T13:20:38.459Z" }, - { url = "https://files.pythonhosted.org/packages/63/5c/2be545034c6bd5ce5bb740ced3e7014d7916f4c445974be11d2a406d5088/ruff-0.12.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40dced4a79d7c264389de1c59467d5d5cefd79e7e06d1dfa2c75497b5269a5a6", size = 11875059, upload-time = "2025-07-11T13:20:41.517Z" }, - { url = "https://files.pythonhosted.org/packages/8e/d4/a74ef1e801ceb5855e9527dae105eaff136afcb9cc4d2056d44feb0e4792/ruff-0.12.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0262d50ba2767ed0fe212aa7e62112a1dcbfd46b858c5bf7bbd11f326998bafc", size = 12658890, upload-time = "2025-07-11T13:20:44.442Z" }, - { url = "https://files.pythonhosted.org/packages/13/c8/1057916416de02e6d7c9bcd550868a49b72df94e3cca0aeb77457dcd9644/ruff-0.12.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12371aec33e1a3758597c5c631bae9a5286f3c963bdfb4d17acdd2d395406687", size = 12232008, upload-time = "2025-07-11T13:20:47.374Z" }, - { url = "https://files.pythonhosted.org/packages/f5/59/4f7c130cc25220392051fadfe15f63ed70001487eca21d1796db46cbcc04/ruff-0.12.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:560f13b6baa49785665276c963edc363f8ad4b4fc910a883e2625bdb14a83a9e", size = 11499096, upload-time = "2025-07-11T13:20:50.348Z" }, - { url = "https://files.pythonhosted.org/packages/d4/01/a0ad24a5d2ed6be03a312e30d32d4e3904bfdbc1cdbe63c47be9d0e82c79/ruff-0.12.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:023040a3499f6f974ae9091bcdd0385dd9e9eb4942f231c23c57708147b06311", size = 11688307, upload-time = "2025-07-11T13:20:52.945Z" }, - { url = "https://files.pythonhosted.org/packages/93/72/08f9e826085b1f57c9a0226e48acb27643ff19b61516a34c6cab9d6ff3fa/ruff-0.12.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:883d844967bffff5ab28bba1a4d246c1a1b2933f48cb9840f3fdc5111c603b07", size = 10661020, upload-time = "2025-07-11T13:20:55.799Z" }, - { url = "https://files.pythonhosted.org/packages/80/a0/68da1250d12893466c78e54b4a0ff381370a33d848804bb51279367fc688/ruff-0.12.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2120d3aa855ff385e0e562fdee14d564c9675edbe41625c87eeab744a7830d12", size = 10246300, upload-time = "2025-07-11T13:20:58.222Z" }, - { url = "https://files.pythonhosted.org/packages/6a/22/5f0093d556403e04b6fd0984fc0fb32fbb6f6ce116828fd54306a946f444/ruff-0.12.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6b16647cbb470eaf4750d27dddc6ebf7758b918887b56d39e9c22cce2049082b", size = 11263119, upload-time = "2025-07-11T13:21:01.503Z" }, - { url = "https://files.pythonhosted.org/packages/92/c9/f4c0b69bdaffb9968ba40dd5fa7df354ae0c73d01f988601d8fac0c639b1/ruff-0.12.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e1417051edb436230023575b149e8ff843a324557fe0a265863b7602df86722f", size = 11746990, upload-time = "2025-07-11T13:21:04.524Z" }, - { url = "https://files.pythonhosted.org/packages/fe/84/7cc7bd73924ee6be4724be0db5414a4a2ed82d06b30827342315a1be9e9c/ruff-0.12.3-py3-none-win32.whl", hash = "sha256:dfd45e6e926deb6409d0616078a666ebce93e55e07f0fb0228d4b2608b2c248d", size = 10589263, upload-time = "2025-07-11T13:21:07.148Z" }, - { url = "https://files.pythonhosted.org/packages/07/87/c070f5f027bd81f3efee7d14cb4d84067ecf67a3a8efb43aadfc72aa79a6/ruff-0.12.3-py3-none-win_amd64.whl", hash = "sha256:a946cf1e7ba3209bdef039eb97647f1c77f6f540e5845ec9c114d3af8df873e7", size = 11695072, upload-time = "2025-07-11T13:21:11.004Z" }, - { url = "https://files.pythonhosted.org/packages/e0/30/f3eaf6563c637b6e66238ed6535f6775480db973c836336e4122161986fc/ruff-0.12.3-py3-none-win_arm64.whl", hash = "sha256:5f9c7c9c8f84c2d7f27e93674d27136fbf489720251544c4da7fb3d742e011b1", size = 10805855, upload-time = "2025-07-11T13:21:13.547Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" }, + { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" }, + { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" }, + { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" }, + { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" }, + { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" }, + { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" }, + { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" }, + { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" }, + { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" }, ] [[package]] diff --git a/libs/prebuilt/uv.lock b/libs/prebuilt/uv.lock index e27bd8b97..817093c68 100644 --- a/libs/prebuilt/uv.lock +++ b/libs/prebuilt/uv.lock @@ -316,7 +316,7 @@ wheels = [ [[package]] name = "langgraph" -version = "0.5.3" +version = "0.6.0a1" source = { editable = "../langgraph" } dependencies = [ { name = "langchain-core" }, @@ -344,7 +344,7 @@ dev = [ { name = "langgraph-checkpoint", editable = "../checkpoint" }, { name = "langgraph-checkpoint-postgres", editable = "../checkpoint-postgres" }, { name = "langgraph-checkpoint-sqlite", editable = "../checkpoint-sqlite" }, - { name = "langgraph-cli", extras = ["inmem"] }, + { name = "langgraph-cli", extras = ["inmem"], editable = "../cli" }, { name = "langgraph-prebuilt", editable = "." }, { name = "langgraph-sdk", editable = "../sdk-py" }, { name = "mypy" }, From cb7b924006c799481ecc225c4568181730dc0f29 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Sun, 20 Jul 2025 16:42:18 +0200 Subject: [PATCH 15/22] feat: Implement durability mode argument (#5432) - Replaces checkpoint_during: bool - checkpoint_during is deprecated but still respected - We implement three durability modes (from least to most durable): - "exit" - save checkpoint only when the graph exits (equivalent to checkpoint_during=False) - "async" - save checkpoint asynchronously while the next step executes (the default, equivalent to old checkpoint_during=True) - "sync" - save checkpoint synchronously before the next step starts (new mode, slower but most durable) Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> --- .../langgraph/checkpoint/postgres/shallow.py | 4 +- libs/langgraph/bench/__main__.py | 8 +- .../langgraph/_internal/_constants.py | 4 +- libs/langgraph/langgraph/pregel/_loop.py | 21 +- libs/langgraph/langgraph/pregel/main.py | 100 ++++++-- libs/langgraph/langgraph/types.py | 6 + libs/langgraph/tests/conftest.py | 5 +- .../tests/test_checkpoint_migration.py | 8 +- libs/langgraph/tests/test_interruption.py | 29 ++- libs/langgraph/tests/test_large_cases.py | 77 +++--- .../langgraph/tests/test_large_cases_async.py | 39 ++- libs/langgraph/tests/test_pregel.py | 213 ++++++---------- libs/langgraph/tests/test_pregel_async.py | 239 ++++++++---------- 13 files changed, 369 insertions(+), 384 deletions(-) diff --git a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py index 16f4094ae..6626332b5 100644 --- a/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py +++ b/libs/checkpoint-postgres/langgraph/checkpoint/postgres/shallow.py @@ -191,7 +191,7 @@ class ShallowPostgresSaver(BasePostgresSaver): ) -> None: warnings.warn( "ShallowPostgresSaver is deprecated as of version 2.0.20 and will be removed in 3.0.0. " - "Use PostgresSaver instead, and invoke the graph with `graph.invoke(..., checkpoint_during=False)`.", + "Use PostgresSaver instead, and invoke the graph with `graph.invoke(..., durability='exit')`.", DeprecationWarning, stacklevel=2, ) @@ -547,7 +547,7 @@ class AsyncShallowPostgresSaver(BasePostgresSaver): ) -> None: warnings.warn( "AsyncShallowPostgresSaver is deprecated as of version 2.0.20 and will be removed in 3.0.0. " - "Use AsyncPostgresSaver instead, and invoke the graph with `await graph.ainvoke(..., checkpoint_during=False)`.", + "Use AsyncPostgresSaver instead, and invoke the graph with `await graph.ainvoke(..., durability='exit')`.", DeprecationWarning, stacklevel=2, ) diff --git a/libs/langgraph/bench/__main__.py b/libs/langgraph/bench/__main__.py index f75f2122b..1d9d3c60f 100644 --- a/libs/langgraph/bench/__main__.py +++ b/libs/langgraph/bench/__main__.py @@ -26,7 +26,7 @@ async def arun(graph: Pregel, input: dict): "configurable": {"thread_id": str(uuid4())}, "recursion_limit": 1000000000, }, - checkpoint_during=False, + durability="exit", ) ] ) @@ -43,7 +43,7 @@ async def arun_first_event_latency(graph: Pregel, input: dict) -> None: "configurable": {"thread_id": str(uuid4())}, "recursion_limit": 1000000000, }, - checkpoint_during=False, + durability="exit", ) try: @@ -63,7 +63,7 @@ def run(graph: Pregel, input: dict): "configurable": {"thread_id": str(uuid4())}, "recursion_limit": 1000000000, }, - checkpoint_during=False, + durability="exit", ) ] ) @@ -80,7 +80,7 @@ def run_first_event_latency(graph: Pregel, input: dict) -> None: "configurable": {"thread_id": str(uuid4())}, "recursion_limit": 1000000000, }, - checkpoint_during=False, + durability="exit", ) try: diff --git a/libs/langgraph/langgraph/_internal/_constants.py b/libs/langgraph/langgraph/_internal/_constants.py index 82b44bc15..abab56295 100644 --- a/libs/langgraph/langgraph/_internal/_constants.py +++ b/libs/langgraph/langgraph/_internal/_constants.py @@ -57,8 +57,8 @@ CONFIG_KEY_SCRATCHPAD = sys.intern("__pregel_scratchpad") # holds a mutable dict for temporary storage scoped to the current task CONFIG_KEY_RUNNER_SUBMIT = sys.intern("__pregel_runner_submit") # holds a function that receives tasks from runner, executes them and returns results -CONFIG_KEY_CHECKPOINT_DURING = sys.intern("__pregel_checkpoint_during") -# holds a boolean indicating whether to checkpoint during the run (or only at the end) +CONFIG_KEY_DURABILITY = sys.intern("__pregel_durability") +# holds the durability mode, one of "sync", "async", or "exit" CONFIG_KEY_RUNTIME = sys.intern("__pregel_runtime") # holds a `Runtime` instance with context, store, stream writer, etc. CONFIG_KEY_RESUME_MAP = sys.intern("__pregel_resume_map") diff --git a/libs/langgraph/langgraph/pregel/_loop.py b/libs/langgraph/langgraph/pregel/_loop.py index 687b1d209..0f1c30d47 100644 --- a/libs/langgraph/langgraph/pregel/_loop.py +++ b/libs/langgraph/langgraph/pregel/_loop.py @@ -113,6 +113,7 @@ from langgraph.types import ( All, CachePolicy, Command, + Durability, PregelExecutableTask, RetryPolicy, StreamMode, @@ -154,7 +155,7 @@ class PregelLoop: manager: None | AsyncParentRunManager | ParentRunManager interrupt_after: All | Sequence[str] interrupt_before: All | Sequence[str] - checkpoint_during: bool + durability: Durability retry_policy: Sequence[RetryPolicy] cache_policy: CachePolicy | None @@ -216,13 +217,13 @@ class PregelLoop: output_keys: str | Sequence[str], stream_keys: str | Sequence[str], trigger_to_nodes: Mapping[str, Sequence[str]], + durability: Durability, interrupt_after: All | Sequence[str] = EMPTY_SEQ, interrupt_before: All | Sequence[str] = EMPTY_SEQ, manager: None | AsyncParentRunManager | ParentRunManager = None, migrate_checkpoint: Callable[[Checkpoint], None] | None = None, retry_policy: Sequence[RetryPolicy] = (), cache_policy: CachePolicy | None = None, - checkpoint_during: bool = True, ) -> None: self.stream = stream self.config = config @@ -246,7 +247,7 @@ class PregelLoop: self.trigger_to_nodes = trigger_to_nodes self.retry_policy = retry_policy self.cache_policy = cache_policy - self.checkpoint_during = checkpoint_during + self.durability = durability if self.stream is not None and CONFIG_KEY_STREAM in config[CONF]: self.stream = DuplexStream(self.stream, config[CONF][CONFIG_KEY_STREAM]) scratchpad: PregelScratchpad | None = config[CONF].get(CONFIG_KEY_SCRATCHPAD) @@ -323,7 +324,7 @@ class PregelLoop: writes_to_save = writes # save writes self.checkpoint_pending_writes.extend((task_id, c, v) for c, v in writes) - if self.checkpoint_during and self.checkpointer_put_writes is not None: + if self.durability != "exit" and self.checkpointer_put_writes is not None: config = patch_configurable( self.checkpoint_config, { @@ -684,7 +685,7 @@ class PregelLoop: self.checkpoint_metadata = metadata # do checkpoint? do_checkpoint = self._checkpointer_put_after_previous is not None and ( - exiting or self.checkpoint_during + exiting or self.durability != "exit" ) # create new checkpoint self.checkpoint = create_checkpoint( @@ -746,7 +747,7 @@ class PregelLoop: traceback: TracebackType | None, ) -> bool | None: # persist current checkpoint and writes - if not self.checkpoint_during and ( + if self.durability == "exit" and ( # if it's a top graph not self.is_nested # or a nested graph with error or interrupt @@ -891,6 +892,7 @@ class SyncPregelLoop(PregelLoop, AbstractContextManager): nodes: Mapping[str, PregelNode], specs: Mapping[str, BaseChannel | ManagedValueSpec], trigger_to_nodes: Mapping[str, Sequence[str]], + durability: Durability, manager: None | AsyncParentRunManager | ParentRunManager = None, interrupt_after: All | Sequence[str] = EMPTY_SEQ, interrupt_before: All | Sequence[str] = EMPTY_SEQ, @@ -900,7 +902,6 @@ class SyncPregelLoop(PregelLoop, AbstractContextManager): migrate_checkpoint: Callable[[Checkpoint], None] | None = None, retry_policy: Sequence[RetryPolicy] = (), cache_policy: CachePolicy | None = None, - checkpoint_during: bool = True, ) -> None: super().__init__( input, @@ -921,7 +922,7 @@ class SyncPregelLoop(PregelLoop, AbstractContextManager): trigger_to_nodes=trigger_to_nodes, retry_policy=retry_policy, cache_policy=cache_policy, - checkpoint_during=checkpoint_during, + durability=durability, ) self.stack = ExitStack() if checkpointer: @@ -1062,6 +1063,7 @@ class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager): nodes: Mapping[str, PregelNode], specs: Mapping[str, BaseChannel | ManagedValueSpec], trigger_to_nodes: Mapping[str, Sequence[str]], + durability: Durability, interrupt_after: All | Sequence[str] = EMPTY_SEQ, interrupt_before: All | Sequence[str] = EMPTY_SEQ, manager: None | AsyncParentRunManager | ParentRunManager = None, @@ -1071,7 +1073,6 @@ class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager): migrate_checkpoint: Callable[[Checkpoint], None] | None = None, retry_policy: Sequence[RetryPolicy] = (), cache_policy: CachePolicy | None = None, - checkpoint_during: bool = True, ) -> None: super().__init__( input, @@ -1092,7 +1093,7 @@ class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager): trigger_to_nodes=trigger_to_nodes, retry_policy=retry_policy, cache_policy=cache_policy, - checkpoint_during=checkpoint_during, + durability=durability, ) self.stack = AsyncExitStack() if checkpointer: diff --git a/libs/langgraph/langgraph/pregel/main.py b/libs/langgraph/langgraph/pregel/main.py index 22a02a88d..1aec1da33 100644 --- a/libs/langgraph/langgraph/pregel/main.py +++ b/libs/langgraph/langgraph/pregel/main.py @@ -11,7 +11,7 @@ from collections.abc import AsyncIterator, Iterator, Mapping, Sequence from dataclasses import is_dataclass from functools import partial from inspect import isclass -from typing import Any, Callable, Generic, Union, cast, get_type_hints +from typing import Any, Callable, Generic, Optional, Union, cast, get_type_hints from uuid import UUID, uuid5 from langchain_core.globals import get_debug @@ -40,10 +40,10 @@ from langgraph._internal._constants import ( CACHE_NS_WRITES, CONF, CONFIG_KEY_CACHE, - CONFIG_KEY_CHECKPOINT_DURING, CONFIG_KEY_CHECKPOINT_ID, CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_CHECKPOINTER, + CONFIG_KEY_DURABILITY, CONFIG_KEY_NODE_FINISHED, CONFIG_KEY_READ, CONFIG_KEY_RUNNER_SUBMIT, @@ -123,6 +123,7 @@ from langgraph.types import ( CachePolicy, Checkpointer, Command, + Durability, Interrupt, Send, StateSnapshot, @@ -2345,6 +2346,8 @@ class Pregel( output_keys: str | Sequence[str] | None, interrupt_before: All | Sequence[str] | None, interrupt_after: All | Sequence[str] | None, + durability: Durability | None = None, + checkpoint_during: bool | None = None, ) -> tuple[ set[StreamMode], str | Sequence[str], @@ -2353,6 +2356,7 @@ class Pregel( BaseCheckpointSaver | None, BaseStore | None, BaseCache | None, + Durability, ]: if config["recursion_limit"] < 1: raise ValueError("recursion_limit must be at least 1") @@ -2391,6 +2395,17 @@ class Pregel( cache: BaseCache | None = config[CONF][CONFIG_KEY_CACHE] else: cache = self.cache + if checkpoint_during is not None: + if durability is not None: + raise ValueError( + "Cannot use both `checkpoint_during` and `durability` parameters." + ) + elif checkpoint_during: + durability = "async" + else: + durability = "exit" + if durability is None: + durability = config.get(CONF, {}).get(CONFIG_KEY_DURABILITY, "async") return ( stream_modes, output_keys, @@ -2399,6 +2414,7 @@ class Pregel( checkpointer, store, cache, + durability, ) def stream( @@ -2412,9 +2428,10 @@ class Pregel( output_keys: str | Sequence[str] | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, - checkpoint_during: bool | None = None, - debug: bool | None = None, + durability: Durability | None = None, subgraphs: bool = False, + debug: bool | None = None, + **kwargs: Unpack[DeprecatedKwargs], ) -> Iterator[dict[str, Any] | Any]: """Stream graph steps for a single input. @@ -2442,7 +2459,10 @@ class Pregel( output_keys: The keys to stream, defaults to all non-context channels. interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph. interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph. - checkpoint_during: Whether to checkpoint intermediate steps, defaults to False. If False, only the final checkpoint is saved. + durability: The durability mode for the graph execution, defaults to "async". Options are: + - `"sync"`: Changes are persisted synchronously before the next step starts. + - `"async"`: Changes are persisted asynchronously while the next step executes. + - `"exit"`: Changes are persisted only when the graph exits. subgraphs: Whether to stream events from inside subgraphs, defaults to False. If True, the events will be emitted as tuples `(namespace, data)`, or `(namespace, mode, data)` if `stream_mode` is a list, @@ -2477,6 +2497,14 @@ class Pregel( run_id=config.get("run_id"), ) try: + deprecated_checkpoint_during = cast( + Optional[bool], kwargs.get("checkpoint_during") + ) + if deprecated_checkpoint_during is not None: + warnings.warn( + "`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.", + category=LangGraphDeprecatedSinceV10, + ) # assign defaults ( stream_modes, @@ -2486,6 +2514,7 @@ class Pregel( checkpointer, store, cache, + durability_, ) = self._defaults( config, stream_mode=stream_mode, @@ -2493,7 +2522,15 @@ class Pregel( output_keys=output_keys, interrupt_before=interrupt_before, interrupt_after=interrupt_after, + durability=durability, + checkpoint_during=deprecated_checkpoint_during, ) + if checkpointer is None and ( + durability is not None or deprecated_checkpoint_during is not None + ): + warnings.warn( + "`durability` has no effect when no checkpointer is present.", + ) # set up subgraph checkpointing if self.checkpointer is True: ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) @@ -2526,9 +2563,9 @@ class Pregel( def stream_writer(c: Any) -> None: pass - # set checkpointing mode for subgraphs - if checkpoint_during is not None: - config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during + # set durability mode for subgraphs + if durability is not None or deprecated_checkpoint_during is not None: + config[CONF][CONFIG_KEY_DURABILITY] = durability_ config[CONF][CONFIG_KEY_RUNTIME] = Runtime( context=context, @@ -2551,9 +2588,7 @@ class Pregel( interrupt_before=interrupt_before_, interrupt_after=interrupt_after_, manager=run_manager, - checkpoint_during=checkpoint_during - if checkpoint_during is not None - else config[CONF].get(CONFIG_KEY_CHECKPOINT_DURING, True), + durability=durability_, trigger_to_nodes=self.trigger_to_nodes, migrate_checkpoint=self._migrate_checkpoint, retry_policy=self.retry_policy, @@ -2614,6 +2649,9 @@ class Pregel( stream_mode, print_mode, subgraphs, stream.get, queue.Empty ) loop.after_tick() + # wait for checkpoint + if durability_ == "sync": + loop._put_checkpoint_fut.result() # emit output yield from _output( stream_mode, print_mode, subgraphs, stream.get, queue.Empty @@ -2646,9 +2684,10 @@ class Pregel( output_keys: str | Sequence[str] | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, - checkpoint_during: bool | None = None, - debug: bool | None = None, + durability: Durability | None = None, subgraphs: bool = False, + debug: bool | None = None, + **kwargs: Unpack[DeprecatedKwargs], ) -> AsyncIterator[dict[str, Any] | Any]: """Asynchronously stream graph steps for a single input. @@ -2675,7 +2714,10 @@ class Pregel( output_keys: The keys to stream, defaults to all non-context channels. interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph. interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph. - checkpoint_during: Whether to checkpoint intermediate steps, defaults to False. If False, only the final checkpoint is saved. + durability: The durability mode for the graph execution, defaults to "async". Options are: + - `"sync"`: Changes are persisted synchronously before the next step starts. + - `"async"`: Changes are persisted asynchronously while the next step executes. + - `"exit"`: Changes are persisted only when the graph exits. subgraphs: Whether to stream events from inside subgraphs, defaults to False. If True, the events will be emitted as tuples `(namespace, data)`, or `(namespace, mode, data)` if `stream_mode` is a list, @@ -2729,6 +2771,14 @@ class Pregel( else False ) try: + deprecated_checkpoint_during = cast( + Optional[bool], kwargs.get("checkpoint_during") + ) + if deprecated_checkpoint_during is not None: + warnings.warn( + "`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.", + category=LangGraphDeprecatedSinceV10, + ) # assign defaults ( stream_modes, @@ -2738,6 +2788,7 @@ class Pregel( checkpointer, store, cache, + durability_, ) = self._defaults( config, stream_mode=stream_mode, @@ -2745,7 +2796,15 @@ class Pregel( output_keys=output_keys, interrupt_before=interrupt_before, interrupt_after=interrupt_after, + durability=durability, + checkpoint_during=deprecated_checkpoint_during, ) + if checkpointer is None and ( + durability is not None or deprecated_checkpoint_during is not None + ): + warnings.warn( + "`durability` has no effect when no checkpointer is present.", + ) # set up subgraph checkpointing if self.checkpointer is True: ns = cast(str, config[CONF][CONFIG_KEY_CHECKPOINT_NS]) @@ -2793,9 +2852,9 @@ class Pregel( def stream_writer(c: Any) -> None: pass - # set checkpointing mode for subgraphs - if checkpoint_during is not None: - config[CONF][CONFIG_KEY_CHECKPOINT_DURING] = checkpoint_during + # set durability mode for subgraphs + if durability is not None or deprecated_checkpoint_during is not None: + config[CONF][CONFIG_KEY_DURABILITY] = durability_ config[CONF][CONFIG_KEY_RUNTIME] = Runtime( context=context, @@ -2818,9 +2877,7 @@ class Pregel( interrupt_before=interrupt_before_, interrupt_after=interrupt_after_, manager=run_manager, - checkpoint_during=checkpoint_during - if checkpoint_during is not None - else config[CONF].get(CONFIG_KEY_CHECKPOINT_DURING, True), + durability=durability_, trigger_to_nodes=self.trigger_to_nodes, migrate_checkpoint=self._migrate_checkpoint, retry_policy=self.retry_policy, @@ -2877,6 +2934,9 @@ class Pregel( ): yield o loop.after_tick() + # wait for checkpoint + if durability_ == "sync": + await cast(asyncio.Future, loop._put_checkpoint_fut) # emit output for o in _output( stream_mode, diff --git a/libs/langgraph/langgraph/types.py b/libs/langgraph/langgraph/types.py index 2cb6d58ab..72078bc55 100644 --- a/libs/langgraph/langgraph/types.py +++ b/libs/langgraph/langgraph/types.py @@ -55,9 +55,15 @@ __all__ = ( "StateSnapshot", "Send", "Command", + "Durability", "interrupt", ) +Durability = Literal["sync", "async", "exit"] +"""Durability mode for the graph execution. +- `"sync"`: Changes are persisted synchronously before the next step starts. +- `"async"`: Changes are persisted asynchronously while the next step executes. +- `"exit"`: Changes are persisted only when the graph exits.""" All = Literal["*"] """Special value to indicate that graph should interrupt on all nodes.""" diff --git a/libs/langgraph/tests/conftest.py b/libs/langgraph/tests/conftest.py index 269c3e66b..d82239aa5 100644 --- a/libs/langgraph/tests/conftest.py +++ b/libs/langgraph/tests/conftest.py @@ -10,6 +10,7 @@ from langgraph.cache.memory import InMemoryCache from langgraph.cache.sqlite import SqliteCache from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.store.base import BaseStore +from langgraph.types import Durability from tests.conftest_checkpointer import ( _checkpointer_memory, _checkpointer_memory_migrate_sends, @@ -49,8 +50,8 @@ def deterministic_uuids(mocker: MockerFixture) -> MockerFixture: return mocker.patch("uuid.uuid4", side_effect=side_effect) -@pytest.fixture(params=[True, False]) -def checkpoint_during(request: pytest.FixtureRequest) -> bool: +@pytest.fixture(params=["sync", "async", "exit"]) +def durability(request: pytest.FixtureRequest) -> Durability: return request.param diff --git a/libs/langgraph/tests/test_checkpoint_migration.py b/libs/langgraph/tests/test_checkpoint_migration.py index 9fa6d7421..f8b4adbdc 100644 --- a/libs/langgraph/tests/test_checkpoint_migration.py +++ b/libs/langgraph/tests/test_checkpoint_migration.py @@ -1513,7 +1513,7 @@ def test_latest_checkpoint_state_graph( config = {"configurable": {"thread_id": "1"}} assert [ - *app.stream({"query": "what is weather in sf"}, config, checkpoint_during=True) + *app.stream({"query": "what is weather in sf"}, config, durability="async") ] == [ {"rewrite_query": {"query": "query: what is weather in sf"}}, {"analyzer_one": {"query": "analyzed: query: what is weather in sf"}}, @@ -1529,7 +1529,7 @@ def test_latest_checkpoint_state_graph( }, ] - assert [*app.stream(Command(resume=""), config, checkpoint_during=True)] == [ + assert [*app.stream(Command(resume=""), config, durability="async")] == [ {"qa": {"answer": "doc1,doc2,doc3,doc4"}}, ] @@ -1556,7 +1556,7 @@ async def test_latest_checkpoint_state_graph_async( assert [ c async for c in app.astream( - {"query": "what is weather in sf"}, config, checkpoint_during=True + {"query": "what is weather in sf"}, config, durability="async" ) ] == [ {"rewrite_query": {"query": "query: what is weather in sf"}}, @@ -1574,7 +1574,7 @@ async def test_latest_checkpoint_state_graph_async( ] assert [ - c async for c in app.astream(Command(resume=""), config, checkpoint_during=True) + c async for c in app.astream(Command(resume=""), config, durability="async") ] == [ {"qa": {"answer": "doc1,doc2,doc3,doc4"}}, ] diff --git a/libs/langgraph/tests/test_interruption.py b/libs/langgraph/tests/test_interruption.py index 6b86129fc..9e5f928ce 100644 --- a/libs/langgraph/tests/test_interruption.py +++ b/libs/langgraph/tests/test_interruption.py @@ -3,12 +3,13 @@ from typing_extensions import TypedDict from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.graph import END, START, StateGraph +from langgraph.types import Durability pytestmark = pytest.mark.anyio def test_interruption_without_state_updates( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: """Test interruption without state updates. This test confirms that interrupting doesn't require a state key having been updated in the prev step""" @@ -33,24 +34,24 @@ def test_interruption_without_state_updates( initial_input = {"input": "hello world"} thread = {"configurable": {"thread_id": "1"}} - graph.invoke(initial_input, thread, checkpoint_during=checkpoint_during) + graph.invoke(initial_input, thread, durability=durability) assert graph.get_state(thread).next == ("step_2",) n_checkpoints = len([c for c in graph.get_state_history(thread)]) - assert n_checkpoints == (3 if checkpoint_during else 1) + assert n_checkpoints == (3 if durability != "exit" else 1) - graph.invoke(None, thread, checkpoint_during=checkpoint_during) + graph.invoke(None, thread, durability=durability) assert graph.get_state(thread).next == ("step_3",) n_checkpoints = len([c for c in graph.get_state_history(thread)]) - assert n_checkpoints == (4 if checkpoint_during else 2) + assert n_checkpoints == (4 if durability != "exit" else 2) - graph.invoke(None, thread, checkpoint_during=checkpoint_during) + graph.invoke(None, thread, durability=durability) assert graph.get_state(thread).next == () n_checkpoints = len([c for c in graph.get_state_history(thread)]) - assert n_checkpoints == (5 if checkpoint_during else 3) + assert n_checkpoints == (5 if durability != "exit" else 3) async def test_interruption_without_state_updates_async( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: """Test interruption without state updates. This test confirms that interrupting doesn't require a state key having been updated in the prev step""" @@ -75,17 +76,17 @@ async def test_interruption_without_state_updates_async( initial_input = {"input": "hello world"} thread = {"configurable": {"thread_id": "1"}} - await graph.ainvoke(initial_input, thread, checkpoint_during=checkpoint_during) + await graph.ainvoke(initial_input, thread, durability=durability) assert (await graph.aget_state(thread)).next == ("step_2",) n_checkpoints = len([c async for c in graph.aget_state_history(thread)]) - assert n_checkpoints == (3 if checkpoint_during else 1) + assert n_checkpoints == (3 if durability != "exit" else 1) - await graph.ainvoke(None, thread, checkpoint_during=checkpoint_during) + await graph.ainvoke(None, thread, durability=durability) assert (await graph.aget_state(thread)).next == ("step_3",) n_checkpoints = len([c async for c in graph.aget_state_history(thread)]) - assert n_checkpoints == (4 if checkpoint_during else 2) + assert n_checkpoints == (4 if durability != "exit" else 2) - await graph.ainvoke(None, thread, checkpoint_during=checkpoint_during) + await graph.ainvoke(None, thread, durability=durability) assert (await graph.aget_state(thread)).next == () n_checkpoints = len([c async for c in graph.aget_state_history(thread)]) - assert n_checkpoints == (5 if checkpoint_during else 3) + assert n_checkpoints == (5 if durability != "exit" else 3) diff --git a/libs/langgraph/tests/test_large_cases.py b/libs/langgraph/tests/test_large_cases.py index 463c4542f..6f5877966 100644 --- a/libs/langgraph/tests/test_large_cases.py +++ b/libs/langgraph/tests/test_large_cases.py @@ -24,6 +24,7 @@ from langgraph.prebuilt.tool_node import ToolNode from langgraph.pregel import NodeBuilder, Pregel from langgraph.types import ( Command, + Durability, Interrupt, PregelTask, RetryPolicy, @@ -69,7 +70,7 @@ def test_invoke_two_processes_in_out_interrupt( thread2 = {"configurable": {"thread_id": "2"}} # start execution, stop at inbox - assert app.invoke(2, thread1, checkpoint_during=True) is None + assert app.invoke(2, thread1, durability="async") is None # inbox == 3 checkpoint = sync_checkpointer.get(thread1) @@ -77,10 +78,10 @@ def test_invoke_two_processes_in_out_interrupt( assert checkpoint["channel_values"]["inbox"] == 3 # resume execution, finish - assert app.invoke(None, thread1, checkpoint_during=True) == 4 + assert app.invoke(None, thread1, durability="async") == 4 # start execution again, stop at inbox - assert app.invoke(20, thread1, checkpoint_during=True) is None + assert app.invoke(20, thread1, durability="async") is None # inbox == 21 checkpoint = sync_checkpointer.get(thread1) @@ -88,11 +89,11 @@ def test_invoke_two_processes_in_out_interrupt( assert checkpoint["channel_values"]["inbox"] == 21 # send a new value in, interrupting the previous execution - assert app.invoke(3, thread1, checkpoint_during=True) is None - assert app.invoke(None, thread1, checkpoint_during=True) == 5 + assert app.invoke(3, thread1, durability="async") is None + assert app.invoke(None, thread1, durability="async") == 5 # start execution again, stopping at inbox - assert app.invoke(20, thread2, checkpoint_during=True) is None + assert app.invoke(20, thread2, durability="async") is None # inbox == 21 snapshot = app.get_state(thread2) @@ -299,9 +300,7 @@ def test_fork_always_re_runs_nodes( # start execution, stop at inbox assert [ - *graph.stream( - 1, thread1, stream_mode=["values", "updates"], checkpoint_during=True - ) + *graph.stream(1, thread1, stream_mode=["values", "updates"], durability="async") ] == [ ("values", 1), ("updates", {"add_one": 1}), @@ -666,7 +665,7 @@ def test_conditional_state_graph( assert [ c for c in app_w_interrupt.stream( - {"input": "what is weather in sf"}, config, checkpoint_during=False + {"input": "what is weather in sf"}, config, durability="exit" ) ] == [ { @@ -836,7 +835,7 @@ def test_conditional_state_graph( assert [ c for c in app_w_interrupt.stream( - {"input": "what is weather in sf"}, config, checkpoint_during=False + {"input": "what is weather in sf"}, config, durability="exit" ) ] == [ { @@ -1003,7 +1002,7 @@ def test_conditional_state_graph( assert [ c for c in app_w_interrupt.stream( - {"input": "what is weather in sf"}, config, checkpoint_during=False + {"input": "what is weather in sf"}, config, durability="exit" ) ] == [ {"__interrupt__": ()}, @@ -1150,7 +1149,7 @@ def test_conditional_state_graph( assert [ c for c in app_w_interrupt.stream( - {"input": "what is weather in sf"}, config, checkpoint_during=False + {"input": "what is weather in sf"}, config, durability="exit" ) ] == [ { @@ -1851,7 +1850,7 @@ def test_state_graph_packets( for c in app_w_interrupt.stream( {"messages": HumanMessage(content="what is weather in sf")}, config, - checkpoint_during=False, + durability="exit", ) ] == [ { @@ -2116,7 +2115,7 @@ def test_state_graph_packets( for c in app_w_interrupt.stream( {"messages": HumanMessage(content="what is weather in sf")}, config, - checkpoint_during=False, + durability="exit", ) ] == [ { @@ -2584,7 +2583,7 @@ def test_message_graph( assert [ c for c in app_w_interrupt.stream( - ("human", "what is weather in sf"), config, checkpoint_during=False + ("human", "what is weather in sf"), config, durability="exit" ) ] == [ { @@ -2809,7 +2808,7 @@ def test_message_graph( assert [ c for c in app_w_interrupt.stream( - "what is weather in sf", config, checkpoint_during=False + "what is weather in sf", config, durability="exit" ) ] == [ { @@ -3306,7 +3305,7 @@ def test_root_graph( assert [ c for c in app_w_interrupt.stream( - ("human", "what is weather in sf"), config, checkpoint_during=False + ("human", "what is weather in sf"), config, durability="exit" ) ] == [ { @@ -3533,7 +3532,7 @@ def test_root_graph( assert [ c for c in app_w_interrupt.stream( - "what is weather in sf", config, checkpoint_during=False + "what is weather in sf", config, durability="exit" ) ] == [ { @@ -4217,7 +4216,7 @@ def test_dynamic_interrupt(sync_checkpointer: BaseCheckpointSaver) -> None: thread1 = {"configurable": {"thread_id": "1"}} # stop when about to enter node assert tool_two.invoke( - {"my_key": "value ⛰️", "market": "DE"}, thread1, checkpoint_during=False + {"my_key": "value ⛰️", "market": "DE"}, thread1, durability="exit" ) == { "my_key": "value ⛰️", "market": "DE", @@ -4377,7 +4376,7 @@ def test_partial_pending_checkpoint(sync_checkpointer: BaseCheckpointSaver) -> N thread1 = {"configurable": {"thread_id": "1"}} # stop when about to enter node assert tool_two.invoke( - {"my_key": "value ⛰️", "market": "DE"}, thread1, checkpoint_during=False + {"my_key": "value ⛰️", "market": "DE"}, thread1, durability="exit" ) == { "my_key": "value ⛰️ one", "market": "DE", @@ -4545,7 +4544,7 @@ def test_dynamic_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> N thread1 = {"configurable": {"thread_id": "1"}} # stop when about to enter node assert tool_two.invoke( - {"my_key": "value ⛰️", "market": "DE"}, thread1, checkpoint_during=False + {"my_key": "value ⛰️", "market": "DE"}, thread1, durability="exit" ) == { "my_key": "value ⛰️", "market": "DE", @@ -4645,7 +4644,7 @@ def test_dynamic_interrupt_subgraph(sync_checkpointer: BaseCheckpointSaver) -> N def test_send_dedupe_on_resume( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class InterruptOnce: ticks: int = 0 @@ -4699,7 +4698,7 @@ def test_send_dedupe_on_resume( graph = builder.compile(checkpointer=sync_checkpointer) thread1 = {"configurable": {"thread_id": "1"}} - assert graph.invoke(["0"], thread1, checkpoint_during=checkpoint_during) == { + assert graph.invoke(["0"], thread1, durability=durability) == { "__interrupt__": [ Interrupt( value="Bahh", @@ -4714,10 +4713,10 @@ def test_send_dedupe_on_resume( assert state.next == ("flaky",) # check history history = [c for c in graph.get_state_history(thread1)] - assert len(history) == (4 if checkpoint_during else 1) + assert len(history) == (4 if durability != "exit" else 1) # resume execution - assert graph.invoke(None, thread1, checkpoint_during=checkpoint_during) == [ + assert graph.invoke(None, thread1, durability=durability) == [ "0", "1", "3.1", @@ -4737,7 +4736,7 @@ def test_send_dedupe_on_resume( assert state.next == () # check history history = [c for c in graph.get_state_history(thread1)] - assert len(history) == (6 if checkpoint_during else 2) + assert len(history) == (6 if durability != "exit" else 2) expected_history = [ StateSnapshot( values=[ @@ -4866,7 +4865,7 @@ def test_send_dedupe_on_resume( error=None, interrupts=(Interrupt(value="Bahh", id=AnyStr()),), state=None, - result=["flaky|4"] if checkpoint_during else None, + result=["flaky|4"] if durability != "exit" else None, ), PregelTask( id=AnyStr(), @@ -5001,7 +5000,7 @@ def test_send_dedupe_on_resume( ), ), ] - if checkpoint_during: + if durability != "exit": assert history == expected_history else: assert history[0] == expected_history[0]._replace( @@ -5059,7 +5058,7 @@ def test_nested_graph_state(sync_checkpointer: BaseCheckpointSaver) -> None: app = graph.compile(checkpointer=sync_checkpointer) config = {"configurable": {"thread_id": "1"}} - app.invoke({"my_key": "my value"}, config, checkpoint_during=False) + app.invoke({"my_key": "my value"}, config, durability="exit") # test state w/ nested subgraph state (right after interrupt) # first get_state without subgraph state expected = StateSnapshot( @@ -5183,7 +5182,7 @@ def test_nested_graph_state(sync_checkpointer: BaseCheckpointSaver) -> None: assert child_history == expected_child_history # resume - app.invoke(None, config, checkpoint_during=False) + app.invoke(None, config, durability="exit") # test state w/ nested subgraph state (after resuming from interrupt) assert app.get_state(config) == StateSnapshot( values={"my_key": "hi my value here and there and back again"}, @@ -5339,7 +5338,7 @@ def test_doubly_nested_graph_state( assert [ c for c in app.stream( - {"my_key": "my value"}, config, subgraphs=True, checkpoint_during=False + {"my_key": "my value"}, config, subgraphs=True, durability="exit" ) ] == [ ((), {"parent_1": {"my_key": "hi my value"}}), @@ -5558,9 +5557,7 @@ def test_doubly_nested_graph_state( interrupts=(), ) # # resume - assert [ - c for c in app.stream(None, config, subgraphs=True, checkpoint_during=False) - ] == [ + assert [c for c in app.stream(None, config, subgraphs=True, durability="exit")] == [ ( (AnyStr("child:"), AnyStr("child_1:")), {"grandchild_2": {"my_key": "hi my value here and there"}}, @@ -5918,7 +5915,7 @@ def test_send_react_interrupt( graph = builder.compile(checkpointer=sync_checkpointer, interrupt_before=["foo"]) thread1 = {"configurable": {"thread_id": "2"}} assert graph.invoke( - {"messages": [HumanMessage("hello")]}, thread1, checkpoint_during=False + {"messages": [HumanMessage("hello")]}, thread1, durability="exit" ) == { "messages": [ _AnyIdHumanMessage(content="hello"), @@ -6042,7 +6039,7 @@ def test_send_react_interrupt( graph = builder.compile(checkpointer=sync_checkpointer, interrupt_before=["foo"]) thread1 = {"configurable": {"thread_id": "3"}} assert graph.invoke( - {"messages": [HumanMessage("hello")]}, thread1, checkpoint_during=False + {"messages": [HumanMessage("hello")]}, thread1, durability="exit" ) == { "messages": [ _AnyIdHumanMessage(content="hello"), @@ -6308,7 +6305,7 @@ def test_send_react_interrupt_control( graph = builder.compile(checkpointer=sync_checkpointer, interrupt_before=["foo"]) thread1 = {"configurable": {"thread_id": "2"}} assert graph.invoke( - {"messages": [HumanMessage("hello")]}, thread1, checkpoint_during=False + {"messages": [HumanMessage("hello")]}, thread1, durability="exit" ) == { "messages": [ _AnyIdHumanMessage(content="hello"), @@ -6567,7 +6564,7 @@ def test_weather_subgraph( config=config, stream_mode="updates", subgraphs=True, - checkpoint_during=False, + durability="exit", ) ] == [ ((), {"router_node": {"route": "weather"}}), @@ -6654,7 +6651,7 @@ def test_weather_subgraph( config=config, stream_mode="updates", subgraphs=True, - checkpoint_during=False, + durability="exit", ) ] == [ ((), {"router_node": {"route": "weather"}}), diff --git a/libs/langgraph/tests/test_large_cases_async.py b/libs/langgraph/tests/test_large_cases_async.py index 8be3e84c6..5e7245a40 100644 --- a/libs/langgraph/tests/test_large_cases_async.py +++ b/libs/langgraph/tests/test_large_cases_async.py @@ -63,7 +63,7 @@ async def test_invoke_two_processes_in_out_interrupt( thread2 = {"configurable": {"thread_id": "2"}} # start execution, stop at inbox - assert await app.ainvoke(2, thread1, checkpoint_during=True) is None + assert await app.ainvoke(2, thread1, durability="async") is None # inbox == 3 checkpoint = await async_checkpointer.aget(thread1) @@ -71,10 +71,10 @@ async def test_invoke_two_processes_in_out_interrupt( assert checkpoint["channel_values"]["inbox"] == 3 # resume execution, finish - assert await app.ainvoke(None, thread1, checkpoint_during=True) == 4 + assert await app.ainvoke(None, thread1, durability="async") == 4 # start execution again, stop at inbox - assert await app.ainvoke(20, thread1, checkpoint_during=True) is None + assert await app.ainvoke(20, thread1, durability="async") is None # inbox == 21 checkpoint = await async_checkpointer.aget(thread1) @@ -82,11 +82,11 @@ async def test_invoke_two_processes_in_out_interrupt( assert checkpoint["channel_values"]["inbox"] == 21 # send a new value in, interrupting the previous execution - assert await app.ainvoke(3, thread1, checkpoint_during=True) is None - assert await app.ainvoke(None, thread1, checkpoint_during=True) == 5 + assert await app.ainvoke(3, thread1, durability="async") is None + assert await app.ainvoke(None, thread1, durability="async") == 5 # start execution again, stopping at inbox - assert await app.ainvoke(20, thread2, checkpoint_during=True) is None + assert await app.ainvoke(20, thread2, durability="async") is None # inbox == 21 snapshot = await app.aget_state(thread2) @@ -301,7 +301,7 @@ async def test_fork_always_re_runs_nodes( assert [ c async for c in graph.astream( - 1, thread1, stream_mode=["values", "updates"], checkpoint_during=True + 1, thread1, stream_mode=["values", "updates"], durability="async" ) ] == [ ("values", 1), @@ -684,7 +684,7 @@ async def test_conditional_graph_state(async_checkpointer: BaseCheckpointSaver) assert [ c async for c in app_w_interrupt.astream( - {"input": "what is weather in sf"}, config, checkpoint_during=False + {"input": "what is weather in sf"}, config, durability="exit" ) ] == [ { @@ -859,7 +859,7 @@ async def test_conditional_graph_state(async_checkpointer: BaseCheckpointSaver) assert [ c async for c in app_w_interrupt.astream( - {"input": "what is weather in sf"}, config, checkpoint_during=False + {"input": "what is weather in sf"}, config, durability="exit" ) ] == [ { @@ -1577,7 +1577,7 @@ async def test_state_graph_packets(async_checkpointer: BaseCheckpointSaver) -> N async for c in app_w_interrupt.astream( {"messages": HumanMessage(content="what is weather in sf")}, config, - checkpoint_during=False, + durability="exit", ) ] == [ { @@ -1828,7 +1828,7 @@ async def test_state_graph_packets(async_checkpointer: BaseCheckpointSaver) -> N async for c in app_w_interrupt.astream( {"messages": HumanMessage(content="what is weather in sf")}, config, - checkpoint_during=False, + durability="exit", ) ] == [ { @@ -2257,7 +2257,7 @@ async def test_message_graph(async_checkpointer: BaseCheckpointSaver) -> None: async for c in app_w_interrupt.astream( HumanMessage(content="what is weather in sf"), config, - checkpoint_during=False, + durability="exit", ) ] == [ { @@ -2740,7 +2740,7 @@ async def test_nested_graph_state(async_checkpointer: BaseCheckpointSaver) -> No app = graph.compile(checkpointer=async_checkpointer) config = {"configurable": {"thread_id": "1"}} - await app.ainvoke({"my_key": "my value"}, config, checkpoint_during=False) + await app.ainvoke({"my_key": "my value"}, config, durability="exit") # test state w/ nested subgraph state (right after interrupt) # first get_state without subgraph state expected = StateSnapshot( @@ -2871,7 +2871,7 @@ async def test_nested_graph_state(async_checkpointer: BaseCheckpointSaver) -> No assert child_history == expected_child_history # resume - await app.ainvoke(None, config, checkpoint_during=False) + await app.ainvoke(None, config, durability="exit") # test state w/ nested subgraph state (after resuming from interrupt) assert await app.aget_state(config) == StateSnapshot( values={"my_key": "hi my value here and there and back again"}, @@ -3030,7 +3030,7 @@ async def test_doubly_nested_graph_state( assert [ c async for c in app.astream( - {"my_key": "my value"}, config, subgraphs=True, checkpoint_during=False + {"my_key": "my value"}, config, subgraphs=True, durability="exit" ) ] == [ ((), {"parent_1": {"my_key": "hi my value"}}), @@ -3250,10 +3250,7 @@ async def test_doubly_nested_graph_state( ) # resume assert [ - c - async for c in app.astream( - None, config, subgraphs=True, checkpoint_during=False - ) + c async for c in app.astream(None, config, subgraphs=True, durability="exit") ] == [ ( (AnyStr("child:"), AnyStr("child_1:")), @@ -3662,7 +3659,7 @@ async def test_weather_subgraph( config=config, stream_mode="updates", subgraphs=True, - checkpoint_during=False, + durability="exit", ) ] == [ ((), {"router_node": {"route": "weather"}}), @@ -3751,7 +3748,7 @@ async def test_weather_subgraph( config=config, stream_mode="updates", subgraphs=True, - checkpoint_during=False, + durability="exit", ) ] == [ ((), {"router_node": {"route": "weather"}}), diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index df88212a0..cd01a3388 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -57,6 +57,7 @@ from langgraph.store.base import BaseStore from langgraph.types import ( CachePolicy, Command, + Durability, Interrupt, PregelTask, RetryPolicy, @@ -185,7 +186,7 @@ def test_checkpoint_errors() -> None: graph = builder.compile(checkpointer=FaultyPutWritesCheckpointer()) with pytest.raises(ValueError, match="Faulty put_writes"): graph.invoke( - "", {"configurable": {"thread_id": "thread-1"}}, checkpoint_during=True + "", {"configurable": {"thread_id": "thread-1"}}, durability="async" ) @@ -570,7 +571,7 @@ def test_run_from_checkpoint_id_retains_previous_writes( thread_id = uuid.uuid4() thread1 = {"configurable": {"thread_id": str(thread_id)}} - result = graph.invoke({"myval": 1}, thread1, checkpoint_during=True) + result = graph.invoke({"myval": 1}, thread1, durability="async") assert result["myval"] == 4 history = [c for c in graph.get_state_history(thread1)] @@ -827,7 +828,7 @@ def test_invoke_checkpoint_two( def test_pending_writes_resume( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): value: Annotated[int, operator.add] @@ -864,7 +865,7 @@ def test_pending_writes_resume( thread1: RunnableConfig = {"configurable": {"thread_id": "1"}} with pytest.raises(ConnectionError, match="I'm not good"): - graph.invoke({"value": 1}, thread1, checkpoint_during=checkpoint_during) + graph.invoke({"value": 1}, thread1, durability=durability) # both nodes should have been called once assert one.calls == 1 @@ -908,7 +909,7 @@ def test_pending_writes_resume( # resume execution with pytest.raises(ConnectionError, match="I'm not good"): - graph.invoke(None, thread1, checkpoint_during=checkpoint_during) + graph.invoke(None, thread1, durability=durability) # node "one" succeeded previously, so shouldn't be called again assert one.calls == 1 @@ -922,14 +923,12 @@ def test_pending_writes_resume( # resume execution, without exception two.rtn = {"value": 3} # both the pending write and the new write were applied, 1 + 2 + 3 = 6 - assert graph.invoke(None, thread1, checkpoint_during=checkpoint_during) == { - "value": 6 - } + assert graph.invoke(None, thread1, durability=durability) == {"value": 6} # check all final checkpoints checkpoints = [c for c in sync_checkpointer.list(thread1)] # we should have 3 - assert len(checkpoints) == (3 if checkpoint_during else 2) + assert len(checkpoints) == (3 if durability != "exit" else 2) # the last one not too interesting for this test assert checkpoints[0] == CheckpointTuple( config={ @@ -1030,7 +1029,7 @@ def test_pending_writes_resume( ), } } - if checkpoint_during + if durability != "exit" else None, pending_writes=( UnsortedSequence( @@ -1038,7 +1037,7 @@ def test_pending_writes_resume( (AnyStr(), "__error__", 'ConnectionError("I\'m not good")'), (AnyStr(), "value", 3), ) - if checkpoint_during + if durability != "exit" else UnsortedSequence( (AnyStr(), "value", 2), (AnyStr(), "__error__", 'ConnectionError("I\'m not good")'), @@ -1047,7 +1046,7 @@ def test_pending_writes_resume( ) ), ) - if not checkpoint_during: + if durability == "exit": return assert checkpoints[2] == CheckpointTuple( config={ @@ -1204,7 +1203,7 @@ def test_send_sequences() -> None: def test_imp_task( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: mapper_calls = 0 @@ -1243,7 +1242,7 @@ def test_imp_task( } thread1 = {"configurable": {"thread_id": "1"}} - assert [*graph.stream([0, 1], thread1, checkpoint_during=checkpoint_during)] == [ + assert [*graph.stream([0, 1], thread1, durability=durability)] == [ {"mapper": "00"}, {"mapper": "11"}, { @@ -1257,9 +1256,7 @@ def test_imp_task( ] assert mapper_calls == 2 - assert graph.invoke( - Command(resume="answer"), thread1, checkpoint_during=checkpoint_during - ) == [ + assert graph.invoke(Command(resume="answer"), thread1, durability=durability) == [ "00answer", "11answer", ] @@ -1267,7 +1264,7 @@ def test_imp_task( def test_imp_nested( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: def mynode(input: list[str]) -> list[str]: return [it + "a" for it in input] @@ -1308,7 +1305,7 @@ def test_imp_nested( } thread1 = {"configurable": {"thread_id": "1"}} - assert [*graph.stream([0, 1], thread1, checkpoint_during=checkpoint_during)] == [ + assert [*graph.stream([0, 1], thread1, durability=durability)] == [ {"submapper": "0"}, {"mapper": "00"}, {"submapper": "1"}, @@ -1323,16 +1320,14 @@ def test_imp_nested( }, ] - assert graph.invoke( - Command(resume="answer"), thread1, checkpoint_during=checkpoint_during - ) == [ + assert graph.invoke(Command(resume="answer"), thread1, durability=durability) == [ "00answera", "11answera", ] def test_imp_stream_order( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: @task() def foo(state: dict) -> tuple: @@ -1354,10 +1349,7 @@ def test_imp_stream_order( return fut_baz.result() thread1 = {"configurable": {"thread_id": "1"}} - assert [ - c - for c in graph.stream({"a": "0"}, thread1, checkpoint_during=checkpoint_during) - ] == [ + assert [c for c in graph.stream({"a": "0"}, thread1, durability=durability)] == [ { "foo": ( "0foo", @@ -1405,7 +1397,7 @@ def test_invoke_checkpoint_three( thread_1 = {"configurable": {"thread_id": "1"}} # total starts out as 0, so output is 0+2=2 - assert app.invoke(2, thread_1, checkpoint_during=True) == 2 + assert app.invoke(2, thread_1, durability="async") == 2 state = app.get_state(thread_1) assert state is not None assert state.values.get("total") == 2 @@ -1415,7 +1407,7 @@ def test_invoke_checkpoint_three( == sync_checkpointer.get(thread_1)["id"] ) # total is now 2, so output is 2+3=5 - assert app.invoke(3, thread_1, checkpoint_during=True) == 5 + assert app.invoke(3, thread_1, durability="async") == 5 state = app.get_state(thread_1) assert state is not None assert state.values.get("total") == 7 @@ -1425,7 +1417,7 @@ def test_invoke_checkpoint_three( ) # total is now 2+5=7, so output would be 7+4=11, but raises ValueError with pytest.raises(ValueError): - app.invoke(4, thread_1, checkpoint_during=True) + app.invoke(4, thread_1, durability="async") # checkpoint is updated with new input state = app.get_state(thread_1) assert state is not None @@ -1433,7 +1425,7 @@ def test_invoke_checkpoint_three( assert state.next == ("one",) """we checkpoint inputs and it failed on "one", so the next node is one""" # we can recover from error by sending new inputs - assert app.invoke(2, thread_1, checkpoint_during=True) == 9 + assert app.invoke(2, thread_1, durability="async") == 9 state = app.get_state(thread_1) assert state is not None assert state.values.get("total") == 16, "total is now 7+9=16" @@ -3176,7 +3168,7 @@ def test_nested_graph(snapshot: SnapshotAssertion) -> None: def test_subgraph_checkpoint_true( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class InnerState(TypedDict): my_key: Annotated[str, operator.add] @@ -3210,7 +3202,7 @@ def test_subgraph_checkpoint_true( assert [ c for c in app.stream( - {"my_key": ""}, config, subgraphs=True, checkpoint_during=checkpoint_during + {"my_key": ""}, config, subgraphs=True, durability=durability ) ] == [ (("inner",), {"inner_1": {"my_key": " got here", "my_other_key": ""}}), @@ -3237,13 +3229,13 @@ def test_subgraph_checkpoint_true( ] checkpoints = list(app.get_state_history(config)) - if checkpoint_during: + if durability != "exit": assert len(checkpoints) == 4 else: assert len(checkpoints) == 1 -def test_subgraph_checkpoint_during_false_inherited() -> None: +def test_subgraph_durability_inherited(durability: Durability) -> None: sync_checkpointer = InMemorySaver() class InnerState(TypedDict): @@ -3274,22 +3266,19 @@ def test_subgraph_checkpoint_during_false_inherited() -> None: "inner", lambda s: "inner" if s["my_key"].count("there") < 2 else END ) app = graph.compile(checkpointer=sync_checkpointer) - for checkpoint_during in [True, False]: - thread_id = str(uuid.uuid4()) - config = {"configurable": {"thread_id": thread_id}} - app.invoke( - {"my_key": ""}, config, subgraphs=True, checkpoint_during=checkpoint_during - ) - if checkpoint_during: - checkpoints = list(sync_checkpointer.list(config)) - assert len(checkpoints) == 12 - else: - checkpoints = list(sync_checkpointer.list(config)) - assert len(checkpoints) == 1 + thread_id = str(uuid.uuid4()) + config = {"configurable": {"thread_id": thread_id}} + app.invoke({"my_key": ""}, config, subgraphs=True, durability=durability) + if durability != "exit": + checkpoints = list(sync_checkpointer.list(config)) + assert len(checkpoints) == 12 + else: + checkpoints = list(sync_checkpointer.list(config)) + assert len(checkpoints) == 1 def test_subgraph_checkpoint_true_interrupt( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: # Define subgraph class SubgraphState(TypedDict): @@ -3330,9 +3319,7 @@ def test_subgraph_checkpoint_true_interrupt( graph = builder.compile(checkpointer=sync_checkpointer) config = {"configurable": {"thread_id": "1"}} - assert graph.invoke( - {"foo": "foo"}, config, checkpoint_during=checkpoint_during - ) == { + assert graph.invoke({"foo": "foo"}, config, durability=durability) == { "foo": "hi! foo", "__interrupt__": [ Interrupt( @@ -3344,9 +3331,9 @@ def test_subgraph_checkpoint_true_interrupt( assert graph.get_state(config, subgraphs=True).tasks[0].state.values == { "bar": "hi! foo" } - assert graph.invoke( - Command(resume="baz"), config, checkpoint_during=checkpoint_during - ) == {"foo": "hi! foobaz"} + assert graph.invoke(Command(resume="baz"), config, durability=durability) == { + "foo": "hi! foobaz" + } def test_stream_subgraphs_during_execution( @@ -3455,7 +3442,7 @@ def test_stream_buffering_single_node(sync_checkpointer: BaseCheckpointSaver) -> def test_nested_graph_interrupts_parallel( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class InnerState(TypedDict): my_key: Annotated[str, operator.add] @@ -3501,11 +3488,11 @@ def test_nested_graph_interrupts_parallel( # test invoke w/ nested interrupt config = {"configurable": {"thread_id": "1"}} - assert app.invoke({"my_key": ""}, config, checkpoint_during=checkpoint_during) == { + assert app.invoke({"my_key": ""}, config, durability=durability) == { "my_key": " and parallel", } - assert app.invoke(None, config, checkpoint_during=checkpoint_during) == { + assert app.invoke(None, config, durability=durability) == { "my_key": "got here and there and parallel and back again", } @@ -3515,16 +3502,14 @@ def test_nested_graph_interrupts_parallel( # test stream updates w/ nested interrupt config = {"configurable": {"thread_id": "2"}} assert [ - *app.stream( - {"my_key": ""}, config, subgraphs=True, checkpoint_during=checkpoint_during - ) + *app.stream({"my_key": ""}, config, subgraphs=True, durability=durability) ] == [ # we got to parallel node first ((), {"outer_1": {"my_key": " and parallel"}}), ((AnyStr("inner:"),), {"inner_1": {"my_key": "got here", "my_other_key": ""}}), ((), {"__interrupt__": ()}), ] - assert [*app.stream(None, config, checkpoint_during=checkpoint_during)] == [ + assert [*app.stream(None, config, durability=durability)] == [ {"outer_1": {"my_key": " and parallel"}, "__metadata__": {"cached": True}}, {"inner": {"my_key": "got here and there"}}, {"outer_2": {"my_key": " and back again"}}, @@ -3537,17 +3522,13 @@ def test_nested_graph_interrupts_parallel( {"my_key": ""}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ {"my_key": ""}, {"my_key": " and parallel"}, ] - assert [ - *app.stream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during - ) - ] == [ + assert [*app.stream(None, config, stream_mode="values", durability=durability)] == [ {"my_key": ""}, {"my_key": "got here and there and parallel"}, {"my_key": "got here and there and parallel and back again"}, @@ -3561,23 +3542,15 @@ def test_nested_graph_interrupts_parallel( {"my_key": ""}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [{"my_key": ""}] # while we're waiting for the node w/ interrupt inside to finish - assert [ - *app.stream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during - ) - ] == [ + assert [*app.stream(None, config, stream_mode="values", durability=durability)] == [ {"my_key": ""}, {"my_key": " and parallel"}, ] - assert [ - *app.stream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during - ) - ] == [ + assert [*app.stream(None, config, stream_mode="values", durability=durability)] == [ {"my_key": ""}, {"my_key": "got here and there and parallel"}, {"my_key": "got here and there and parallel and back again"}, @@ -3591,32 +3564,24 @@ def test_nested_graph_interrupts_parallel( {"my_key": ""}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ {"my_key": ""}, {"my_key": " and parallel"}, ] - assert [ - *app.stream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during - ) - ] == [ + assert [*app.stream(None, config, stream_mode="values", durability=durability)] == [ {"my_key": ""}, {"my_key": "got here and there and parallel"}, ] - assert [ - *app.stream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during - ) - ] == [ + assert [*app.stream(None, config, stream_mode="values", durability=durability)] == [ {"my_key": "got here and there and parallel"}, {"my_key": "got here and there and parallel and back again"}, ] def test_doubly_nested_graph_interrupts( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): my_key: str @@ -3669,13 +3634,11 @@ def test_doubly_nested_graph_interrupts( # test invoke w/ nested interrupt config = {"configurable": {"thread_id": "1"}} - assert app.invoke( - {"my_key": "my value"}, config, checkpoint_during=checkpoint_during - ) == { + assert app.invoke({"my_key": "my value"}, config, durability=durability) == { "my_key": "hi my value", } - assert app.invoke(None, config, checkpoint_during=checkpoint_during) == { + assert app.invoke(None, config, durability=durability) == { "my_key": "hi my value here and there and back again", } @@ -3684,14 +3647,12 @@ def test_doubly_nested_graph_interrupts( config = { "configurable": {"thread_id": "2", CONFIG_KEY_NODE_FINISHED: nodes.append} } - assert [ - *app.stream({"my_key": "my value"}, config, checkpoint_during=checkpoint_during) - ] == [ + assert [*app.stream({"my_key": "my value"}, config, durability=durability)] == [ {"parent_1": {"my_key": "hi my value"}}, {"__interrupt__": ()}, ] assert nodes == ["parent_1", "grandchild_1"] - assert [*app.stream(None, config, checkpoint_during=checkpoint_during)] == [ + assert [*app.stream(None, config, durability=durability)] == [ {"child": {"my_key": "hi my value here and there"}}, {"parent_2": {"my_key": "hi my value here and there and back again"}}, ] @@ -3711,17 +3672,13 @@ def test_doubly_nested_graph_interrupts( {"my_key": "my value"}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ {"my_key": "my value"}, {"my_key": "hi my value"}, ] - assert [ - *app.stream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during - ) - ] == [ + assert [*app.stream(None, config, stream_mode="values", durability=durability)] == [ {"my_key": "hi my value"}, {"my_key": "hi my value here and there"}, {"my_key": "hi my value here and there and back again"}, @@ -4372,7 +4329,7 @@ def test_debug_retry(sync_checkpointer: BaseCheckpointSaver): graph = builder.compile(checkpointer=sync_checkpointer) config = {"configurable": {"thread_id": "1"}} - graph.invoke({"messages": []}, config=config, checkpoint_during=True) + graph.invoke({"messages": []}, config=config, durability="async") # re-run step: 1 target_config = next( @@ -4384,7 +4341,7 @@ def test_debug_retry(sync_checkpointer: BaseCheckpointSaver): events = [ *graph.stream( - None, config=update_config, stream_mode="debug", checkpoint_during=True + None, config=update_config, stream_mode="debug", durability="async" ) ] @@ -4417,7 +4374,7 @@ def test_debug_retry(sync_checkpointer: BaseCheckpointSaver): def test_debug_subgraphs( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ): class State(TypedDict): messages: Annotated[list[str], operator.add] @@ -4451,14 +4408,14 @@ def test_debug_subgraphs( {"messages": []}, config=config, stream_mode="debug", - checkpoint_during=checkpoint_during, + durability=durability, ) ] checkpoint_events = list( reversed([e["payload"] for e in events if e["type"] == "checkpoint"]) ) - if not checkpoint_during: + if durability == "exit": checkpoint_events = checkpoint_events[:1] checkpoint_history = list(graph.get_state_history(config)) @@ -4489,7 +4446,7 @@ def test_debug_subgraphs( def test_debug_nested_subgraphs( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ): from collections import defaultdict @@ -4533,7 +4490,7 @@ def test_debug_nested_subgraphs( config=config, stream_mode="debug", subgraphs=True, - checkpoint_during=checkpoint_during, + durability=durability, ) ] @@ -4573,9 +4530,9 @@ def test_debug_nested_subgraphs( for checkpoint_events, checkpoint_history, ns in zip( stream_ns.values(), history_ns.values(), stream_ns.keys() ): - if not checkpoint_during: + if durability == "exit": checkpoint_events = checkpoint_events[-1:] - if ns: # Save no checkpoints for subgraphs when checkpoint_during=False + if ns: # Save no checkpoints for subgraphs when durability="exit" assert not checkpoint_history continue assert len(checkpoint_events) == len(checkpoint_history) @@ -4777,7 +4734,7 @@ def test_parent_command( config = {"configurable": {"thread_id": "1"}} assert graph.invoke( - {"messages": [("user", "get user name")]}, config, checkpoint_during=False + {"messages": [("user", "get user name")]}, config, durability="exit" ) == { "messages": [ _AnyIdHumanMessage( @@ -5363,7 +5320,7 @@ def test_concurrent_execution_thread_safety(): def test_checkpoint_recovery( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ): """Test recovery from checkpoints after failures.""" @@ -5394,7 +5351,7 @@ def test_checkpoint_recovery( graph.invoke( {"steps": ["start"], "attempt": 1}, config, - checkpoint_during=checkpoint_during, + durability=durability, ) # Verify checkpoint state @@ -5405,14 +5362,12 @@ def test_checkpoint_recovery( assert "RuntimeError('Simulated failure')" in state.tasks[0].error # Retry with updated attempt count - result = graph.invoke( - {"steps": [], "attempt": 2}, config, checkpoint_during=checkpoint_during - ) + result = graph.invoke({"steps": [], "attempt": 2}, config, durability=durability) assert result == {"steps": ["start", "node1", "node2"], "attempt": 2} # Verify checkpoint history shows both attempts history = list(graph.get_state_history(config)) - if checkpoint_during: + if durability != "exit": assert len(history) == 6 # Initial + failed attempt + successful attempt else: assert len(history) == 2 # error + success @@ -5495,7 +5450,7 @@ def test_falsy_return_from_task(sync_checkpointer: BaseCheckpointSaver): assert [ chunk for chunk in graph.stream( - {"a": 5}, configurable, stream_mode="debug", checkpoint_during=False + {"a": 5}, configurable, stream_mode="debug", durability="exit" ) ] == [ { @@ -5598,7 +5553,7 @@ def test_falsy_return_from_task(sync_checkpointer: BaseCheckpointSaver): Command(resume="123"), configurable, stream_mode="debug", - checkpoint_during=False, + durability="exit", ) ] == [ { @@ -7585,7 +7540,7 @@ def test_pregel_node_copy() -> None: def test_update_as_input( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): foo: str @@ -7608,13 +7563,13 @@ def test_update_as_input( assert graph.invoke( {"foo": "input"}, {"configurable": {"thread_id": "1"}}, - checkpoint_during=checkpoint_during, + durability=durability, ) == {"foo": "tool"} assert graph.invoke( {"foo": "input"}, {"configurable": {"thread_id": "1"}}, - checkpoint_during=checkpoint_during, + durability=durability, ) == {"foo": "tool"} def map_snapshot(i: StateSnapshot) -> dict: @@ -7653,14 +7608,14 @@ def test_update_as_input( for s in graph.get_state_history({"configurable": {"thread_id": "2"}}) ] - if checkpoint_during: + if durability != "exit": assert new_history == history else: assert [new_history[0], new_history[4]] == history def test_batch_update_as_input( - sync_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + sync_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): foo: str @@ -7695,7 +7650,7 @@ def test_batch_update_as_input( assert graph.invoke( {"foo": "input"}, {"configurable": {"thread_id": "1"}}, - checkpoint_during=checkpoint_during, + durability=durability, ) == { "foo": "map", "tasks": [0, 1, 2], @@ -7749,7 +7704,7 @@ def test_batch_update_as_input( for s in graph.get_state_history({"configurable": {"thread_id": "2"}}) ] - if checkpoint_during: + if durability != "exit": assert new_history == history else: assert new_history[:1] == history diff --git a/libs/langgraph/tests/test_pregel_async.py b/libs/langgraph/tests/test_pregel_async.py index e81cb5e11..9b7fa4628 100644 --- a/libs/langgraph/tests/test_pregel_async.py +++ b/libs/langgraph/tests/test_pregel_async.py @@ -58,6 +58,7 @@ from langgraph.store.base import BaseStore from langgraph.types import ( CachePolicy, Command, + Durability, Interrupt, PregelTask, RetryPolicy, @@ -177,11 +178,11 @@ async def test_checkpoint_errors() -> None: graph = builder.compile(checkpointer=FaultyPutWritesCheckpointer()) with pytest.raises(ValueError, match="Faulty put_writes"): await graph.ainvoke( - "", {"configurable": {"thread_id": "thread-1"}}, checkpoint_during=True + "", {"configurable": {"thread_id": "thread-1"}}, durability="async" ) with pytest.raises(ValueError, match="Faulty put_writes"): async for _ in graph.astream( - "", {"configurable": {"thread_id": "thread-2"}}, checkpoint_during=True + "", {"configurable": {"thread_id": "thread-2"}}, durability="async" ): pass with pytest.raises(ValueError, match="Faulty put_writes"): @@ -189,7 +190,7 @@ async def test_checkpoint_errors() -> None: "", {"configurable": {"thread_id": "thread-3"}}, version="v2", - checkpoint_during=True, + durability="async", ): pass @@ -316,7 +317,7 @@ async def test_checkpoint_put_after_cancellation() -> None: # start the task t = asyncio.create_task( - graph.ainvoke({"hello": "world"}, thread1, checkpoint_during=False) + graph.ainvoke({"hello": "world"}, thread1, durability="exit") ) # cancel after 0.2 seconds await asyncio.sleep(0.2) @@ -383,7 +384,7 @@ async def test_checkpoint_put_after_cancellation_stream_anext() -> None: thread1 = {"configurable": {"thread_id": "1"}} # start the task - s = graph.astream({"hello": "world"}, thread1, checkpoint_during=False) + s = graph.astream({"hello": "world"}, thread1, durability="exit") t = asyncio.create_task(s.__anext__()) # cancel after 0.2 seconds await asyncio.sleep(0.2) @@ -455,7 +456,7 @@ async def test_checkpoint_put_after_cancellation_stream_events_anext() -> None: thread1, version="v2", include_names=["LangGraph"], - checkpoint_during=False, + durability="exit", ) # skip first event (happens right away) await s.__anext__() @@ -640,7 +641,7 @@ async def test_dynamic_interrupt(async_checkpointer: BaseCheckpointSaver) -> Non assert [ c async for c in tool_two.astream( - {"my_key": "value ⛰️", "market": "DE"}, thread1, checkpoint_during=False + {"my_key": "value ⛰️", "market": "DE"}, thread1, durability="exit" ) ] == [ { @@ -807,7 +808,7 @@ async def test_dynamic_interrupt_subgraph( assert [ c async for c in tool_two.astream( - {"my_key": "value ⛰️", "market": "DE"}, thread1, checkpoint_during=False + {"my_key": "value ⛰️", "market": "DE"}, thread1, durability="exit" ) ] == [ { @@ -980,7 +981,7 @@ async def test_partial_pending_checkpoint( thread1 = {"configurable": {"thread_id": "1"}} # stop when about to enter node assert await tool_two.ainvoke( - {"my_key": "value ⛰️", "market": "DE"}, thread1, checkpoint_during=False + {"my_key": "value ⛰️", "market": "DE"}, thread1, durability="exit" ) == { "my_key": "value ⛰️ one", "market": "DE", @@ -1763,7 +1764,7 @@ async def test_invoke_checkpoint( async def test_pending_writes_resume( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): value: Annotated[int, operator.add] @@ -1800,7 +1801,7 @@ async def test_pending_writes_resume( thread1: RunnableConfig = {"configurable": {"thread_id": "1"}} with pytest.raises(ConnectionError, match="I'm not good"): - await graph.ainvoke({"value": 1}, thread1, checkpoint_during=checkpoint_during) + await graph.ainvoke({"value": 1}, thread1, durability=durability) # both nodes should have been called once assert one.calls == 1 @@ -1849,7 +1850,7 @@ async def test_pending_writes_resume( # resume execution with pytest.raises(ConnectionError, match="I'm not good"): - await graph.ainvoke(None, thread1, checkpoint_during=checkpoint_during) + await graph.ainvoke(None, thread1, durability=durability) # node "one" succeeded previously, so shouldn't be called again assert one.calls == 1 @@ -1863,14 +1864,12 @@ async def test_pending_writes_resume( # resume execution, without exception two.rtn = {"value": 3} # both the pending write and the new write were applied, 1 + 2 + 3 = 6 - assert await graph.ainvoke(None, thread1, checkpoint_during=checkpoint_during) == { - "value": 6 - } + assert await graph.ainvoke(None, thread1, durability=durability) == {"value": 6} # check all final checkpoints checkpoints = [c async for c in async_checkpointer.alist(thread1)] # we should have 3 - assert len(checkpoints) == (3 if checkpoint_during else 2) + assert len(checkpoints) == (3 if durability != "exit" else 2) # the last one not too interesting for this test assert checkpoints[0] == CheckpointTuple( config={ @@ -1969,14 +1968,14 @@ async def test_pending_writes_resume( "checkpoint_id": checkpoints[2].config["configurable"]["checkpoint_id"], } } - if checkpoint_during + if durability != "exit" else None, pending_writes=UnsortedSequence( (AnyStr(), "value", 2), (AnyStr(), "__error__", 'ConnectionError("I\'m not good")'), (AnyStr(), "value", 3), ) - if checkpoint_during + if durability != "exit" else UnsortedSequence( (AnyStr(), "value", 2), (AnyStr(), "__error__", 'ConnectionError("I\'m not good")'), @@ -1984,7 +1983,7 @@ async def test_pending_writes_resume( # produced in a run where only the next checkpoint (the last) is saved ), ) - if not checkpoint_during: + if durability == "exit": return assert checkpoints[2] == CheckpointTuple( config={ @@ -2058,7 +2057,7 @@ async def test_run_from_checkpoint_id_retains_previous_writes( thread_id = uuid.uuid4() thread1 = {"configurable": {"thread_id": str(thread_id)}} - result = await graph.ainvoke({"myval": 1}, thread1, checkpoint_during=True) + result = await graph.ainvoke({"myval": 1}, thread1, durability="async") assert result["myval"] == 4 history = [c async for c in graph.aget_state_history(thread1)] @@ -2240,7 +2239,7 @@ async def test_send_sequences(async_checkpointer: BaseCheckpointSaver) -> None: @NEEDS_CONTEXTVARS async def test_imp_task( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: mapper_calls = 0 @@ -2260,12 +2259,7 @@ async def test_imp_task( tracer = FakeTracer() thread1 = {"configurable": {"thread_id": "1"}, "callbacks": [tracer]} - assert [ - c - async for c in graph.astream( - [0, 1], thread1, checkpoint_during=checkpoint_during - ) - ] == [ + assert [c async for c in graph.astream([0, 1], thread1, durability=durability)] == [ {"mapper": "00"}, {"mapper": "11"}, { @@ -2288,7 +2282,7 @@ async def test_imp_task( assert any(r.inputs == {"input": 1} for r in mapper_runs) assert await graph.ainvoke( - Command(resume="answer"), thread1, checkpoint_during=checkpoint_during + Command(resume="answer"), thread1, durability=durability ) == [ "00answer", "11answer", @@ -2298,7 +2292,7 @@ async def test_imp_task( @NEEDS_CONTEXTVARS async def test_imp_nested( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: async def mynode(input: list[str]) -> list[str]: return [it + "a" for it in input] @@ -2337,12 +2331,7 @@ async def test_imp_nested( } thread1 = {"configurable": {"thread_id": "1"}} - assert [ - c - async for c in graph.astream( - [0, 1], thread1, checkpoint_during=checkpoint_during - ) - ] == [ + assert [c async for c in graph.astream([0, 1], thread1, durability=durability)] == [ {"submapper": "0"}, {"mapper": "00"}, {"submapper": "1"}, @@ -2358,7 +2347,7 @@ async def test_imp_nested( ] assert await graph.ainvoke( - Command(resume="answer"), thread1, checkpoint_during=checkpoint_during + Command(resume="answer"), thread1, durability=durability ) == [ "00answera", "11answera", @@ -2367,7 +2356,7 @@ async def test_imp_nested( @NEEDS_CONTEXTVARS async def test_imp_task_cancel( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: mapper_calls = 0 mapper_cancels = 0 @@ -2393,12 +2382,7 @@ async def test_imp_task_cancel( return [m + answer for m in mapped] thread1 = {"configurable": {"thread_id": "1"}} - assert [ - c - async for c in graph.astream( - [0, 1], thread1, checkpoint_during=checkpoint_during - ) - ] == [ + assert [c async for c in graph.astream([0, 1], thread1, durability=durability)] == [ {"mapper": "00"}, { "__interrupt__": ( @@ -2413,7 +2397,7 @@ async def test_imp_task_cancel( assert mapper_cancels == 1 assert await graph.ainvoke( - Command(resume="answer"), thread1, checkpoint_during=checkpoint_during + Command(resume="answer"), thread1, durability=durability ) == [ "00answer", ] @@ -2423,7 +2407,7 @@ async def test_imp_task_cancel( @NEEDS_CONTEXTVARS async def test_imp_sync_from_async( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: @task() def foo(state: dict) -> dict: @@ -2446,10 +2430,7 @@ async def test_imp_sync_from_async( thread1 = {"configurable": {"thread_id": "1"}} assert [ - c - async for c in graph.astream( - {"a": "0"}, thread1, checkpoint_during=checkpoint_during - ) + c async for c in graph.astream({"a": "0"}, thread1, durability=durability) ] == [ {"foo": {"a": "0foo", "b": "bar"}}, {"bar": {"a": "0foobar", "c": "bark"}}, @@ -2460,7 +2441,7 @@ async def test_imp_sync_from_async( @NEEDS_CONTEXTVARS async def test_imp_stream_order( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: @task() async def foo(state: dict) -> dict: @@ -2484,10 +2465,7 @@ async def test_imp_stream_order( thread1 = {"configurable": {"thread_id": "1"}} assert [ - c - async for c in graph.astream( - {"a": "0"}, thread1, checkpoint_during=checkpoint_during - ) + c async for c in graph.astream({"a": "0"}, thread1, durability=durability) ] == [ {"foo": {"a": "0foo", "b": "bar"}}, {"bar": {"a": "0foobar", "c": "bark"}}, @@ -2501,7 +2479,7 @@ async def test_imp_stream_order( reason="Requires Python 3.11 or higher for context management", ) async def test_send_dedupe_on_resume( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class InterruptOnce: ticks: int = 0 @@ -2552,7 +2530,7 @@ async def test_send_dedupe_on_resume( graph = builder.compile(checkpointer=async_checkpointer) thread1 = {"configurable": {"thread_id": "1"}} - assert await graph.ainvoke(["0"], thread1, checkpoint_during=checkpoint_during) == { + assert await graph.ainvoke(["0"], thread1, durability=durability) == { "__interrupt__": [ Interrupt( value="Bahh", @@ -2563,7 +2541,7 @@ async def test_send_dedupe_on_resume( assert builder.nodes["2"].runnable.func.ticks == 3 assert builder.nodes["flaky"].runnable.func.ticks == 1 # resume execution - assert await graph.ainvoke(None, thread1, checkpoint_during=checkpoint_during) == [ + assert await graph.ainvoke(None, thread1, durability=durability) == [ "0", "1", "3.1", @@ -2580,7 +2558,7 @@ async def test_send_dedupe_on_resume( assert builder.nodes["flaky"].runnable.func.ticks == 2 # check history history = [c async for c in graph.aget_state_history(thread1)] - assert len(history) == (6 if checkpoint_during else 2) + assert len(history) == (6 if durability != "exit" else 2) expected_history = [ StateSnapshot( values=[ @@ -2709,7 +2687,7 @@ async def test_send_dedupe_on_resume( error=None, interrupts=(Interrupt(value="Bahh", id=AnyStr()),), state=None, - result=["flaky|4"] if checkpoint_during else None, + result=["flaky|4"] if durability != "exit" else None, ), PregelTask( id=AnyStr(), @@ -2844,7 +2822,7 @@ async def test_send_dedupe_on_resume( interrupts=(), ), ] - if checkpoint_during: + if durability != "exit": assert history == expected_history else: assert history[0] == expected_history[0]._replace( @@ -2955,7 +2933,7 @@ async def test_send_react_interrupt(async_checkpointer: BaseCheckpointSaver) -> graph = builder.compile(checkpointer=async_checkpointer, interrupt_before=["foo"]) thread1 = {"configurable": {"thread_id": "2"}} assert await graph.ainvoke( - {"messages": [HumanMessage("hello")]}, thread1, checkpoint_during=False + {"messages": [HumanMessage("hello")]}, thread1, durability="exit" ) == { "messages": [ _AnyIdHumanMessage(content="hello"), @@ -3079,7 +3057,7 @@ async def test_send_react_interrupt(async_checkpointer: BaseCheckpointSaver) -> graph = builder.compile(checkpointer=async_checkpointer, interrupt_before=["foo"]) thread1 = {"configurable": {"thread_id": "3"}} assert await graph.ainvoke( - {"messages": [HumanMessage("hello")]}, thread1, checkpoint_during=False + {"messages": [HumanMessage("hello")]}, thread1, durability="exit" ) == { "messages": [ _AnyIdHumanMessage(content="hello"), @@ -3344,7 +3322,7 @@ async def test_send_react_interrupt_control( graph = builder.compile(checkpointer=async_checkpointer, interrupt_before=["foo"]) thread1 = {"configurable": {"thread_id": "2"}} assert await graph.ainvoke( - {"messages": [HumanMessage("hello")]}, thread1, checkpoint_during=False + {"messages": [HumanMessage("hello")]}, thread1, durability="exit" ) == { "messages": [ _AnyIdHumanMessage(content="hello"), @@ -3631,7 +3609,7 @@ async def test_invoke_checkpoint_three( thread_1 = {"configurable": {"thread_id": "1"}} # total starts out as 0, so output is 0+2=2 - assert await app.ainvoke(2, thread_1, checkpoint_during=True) == 2 + assert await app.ainvoke(2, thread_1, durability="async") == 2 state = await app.aget_state(thread_1) assert state is not None assert state.values.get("total") == 2 @@ -3640,7 +3618,7 @@ async def test_invoke_checkpoint_three( == (await async_checkpointer.aget(thread_1))["id"] ) # total is now 2, so output is 2+3=5 - assert await app.ainvoke(3, thread_1, checkpoint_during=True) == 5 + assert await app.ainvoke(3, thread_1, durability="async") == 5 state = await app.aget_state(thread_1) assert state is not None assert state.values.get("total") == 7 @@ -3650,7 +3628,7 @@ async def test_invoke_checkpoint_three( ) # total is now 2+5=7, so output would be 7+4=11, but raises ValueError with pytest.raises(ValueError): - await app.ainvoke(4, thread_1, checkpoint_during=True) + await app.ainvoke(4, thread_1, durability="async") # checkpoint is not updated state = await app.aget_state(thread_1) assert state is not None @@ -3658,7 +3636,7 @@ async def test_invoke_checkpoint_three( assert state.next == ("one",) """we checkpoint inputs and it failed on "one", so the next node is one""" # we can recover from error by sending new inputs - assert await app.ainvoke(2, thread_1, checkpoint_during=True) == 9 + assert await app.ainvoke(2, thread_1, durability="async") == 9 state = await app.aget_state(thread_1) assert state is not None assert state.values.get("total") == 16, "total is now 7+9=16" @@ -4960,7 +4938,7 @@ async def test_nested_graph(snapshot: SnapshotAssertion) -> None: async def test_subgraph_checkpoint_true( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class InnerState(TypedDict): my_key: Annotated[str, operator.add] @@ -4998,7 +4976,7 @@ async def test_subgraph_checkpoint_true( {"my_key": ""}, config, subgraphs=True, - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ (("inner",), {"inner_1": {"my_key": " got here", "my_other_key": ""}}), @@ -5025,7 +5003,9 @@ async def test_subgraph_checkpoint_true( ] -async def test_subgraph_checkpoint_during_false_inherited() -> None: +async def test_subgraph_durability_inherited( + durability: Durability, +) -> None: async_checkpointer = InMemorySaver() class InnerState(TypedDict): @@ -5056,23 +5036,20 @@ async def test_subgraph_checkpoint_during_false_inherited() -> None: "inner", lambda s: "inner" if s["my_key"].count("there") < 2 else END ) app = graph.compile(checkpointer=async_checkpointer) - for checkpoint_during in [True, False]: - thread_id = str(uuid.uuid4()) - config = {"configurable": {"thread_id": thread_id}} - await app.ainvoke( - {"my_key": ""}, config, subgraphs=True, checkpoint_during=checkpoint_during - ) - if checkpoint_during: - checkpoints = list(async_checkpointer.list(config)) - assert len(checkpoints) == 12 - else: - checkpoints = list(async_checkpointer.list(config)) - assert len(checkpoints) == 1 + thread_id = str(uuid.uuid4()) + config = {"configurable": {"thread_id": thread_id}} + await app.ainvoke({"my_key": ""}, config, subgraphs=True, durability=durability) + if durability != "exit": + checkpoints = list(async_checkpointer.list(config)) + assert len(checkpoints) == 12 + else: + checkpoints = list(async_checkpointer.list(config)) + assert len(checkpoints) == 1 @NEEDS_CONTEXTVARS async def test_subgraph_checkpoint_true_interrupt( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: # Define subgraph class SubgraphState(TypedDict): @@ -5113,9 +5090,7 @@ async def test_subgraph_checkpoint_true_interrupt( graph = builder.compile(checkpointer=async_checkpointer) config = {"configurable": {"thread_id": "1"}} - assert await graph.ainvoke( - {"foo": "foo"}, config, checkpoint_during=checkpoint_during - ) == { + assert await graph.ainvoke({"foo": "foo"}, config, durability=durability) == { "foo": "hi! foo", "__interrupt__": [ Interrupt( @@ -5128,7 +5103,7 @@ async def test_subgraph_checkpoint_true_interrupt( "bar": "hi! foo" } assert await graph.ainvoke( - Command(resume="baz"), config, checkpoint_during=checkpoint_during + Command(resume="baz"), config, durability=durability ) == {"foo": "hi! foobaz"} @@ -5242,7 +5217,7 @@ async def test_stream_buffering_single_node( async def test_nested_graph_interrupts_parallel( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class InnerState(TypedDict): my_key: Annotated[str, operator.add] @@ -5291,13 +5266,11 @@ async def test_nested_graph_interrupts_parallel( # test invoke w/ nested interrupt config = {"configurable": {"thread_id": "1"}} - assert await app.ainvoke( - {"my_key": ""}, config, checkpoint_during=checkpoint_during - ) == { + assert await app.ainvoke({"my_key": ""}, config, durability=durability) == { "my_key": " and parallel", } - assert await app.ainvoke(None, config, checkpoint_during=checkpoint_during) == { + assert await app.ainvoke(None, config, durability=durability) == { "my_key": "got here and there and parallel and back again", } @@ -5312,7 +5285,7 @@ async def test_nested_graph_interrupts_parallel( {"my_key": ""}, config, subgraphs=True, - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ # we got to parallel node first @@ -5323,9 +5296,7 @@ async def test_nested_graph_interrupts_parallel( ), ((), {"__interrupt__": ()}), ] - assert [ - c async for c in app.astream(None, config, checkpoint_during=checkpoint_during) - ] == [ + assert [c async for c in app.astream(None, config, durability=durability)] == [ {"outer_1": {"my_key": " and parallel"}, "__metadata__": {"cached": True}}, {"inner": {"my_key": "got here and there"}}, {"outer_2": {"my_key": " and back again"}}, @@ -5339,7 +5310,7 @@ async def test_nested_graph_interrupts_parallel( {"my_key": ""}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ {"my_key": ""}, @@ -5348,7 +5319,7 @@ async def test_nested_graph_interrupts_parallel( assert [ c async for c in app.astream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during + None, config, stream_mode="values", durability=durability ) ] == [ {"my_key": ""}, @@ -5365,7 +5336,7 @@ async def test_nested_graph_interrupts_parallel( {"my_key": ""}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ {"my_key": ""}, @@ -5374,7 +5345,7 @@ async def test_nested_graph_interrupts_parallel( assert [ c async for c in app.astream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during + None, config, stream_mode="values", durability=durability ) ] == [ {"my_key": ""}, @@ -5383,7 +5354,7 @@ async def test_nested_graph_interrupts_parallel( assert [ c async for c in app.astream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during + None, config, stream_mode="values", durability=durability ) ] == [ {"my_key": ""}, @@ -5400,7 +5371,7 @@ async def test_nested_graph_interrupts_parallel( {"my_key": ""}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ {"my_key": ""}, @@ -5409,7 +5380,7 @@ async def test_nested_graph_interrupts_parallel( assert [ c async for c in app.astream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during + None, config, stream_mode="values", durability=durability ) ] == [ {"my_key": ""}, @@ -5418,7 +5389,7 @@ async def test_nested_graph_interrupts_parallel( assert [ c async for c in app.astream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during + None, config, stream_mode="values", durability=durability ) ] == [ {"my_key": "got here and there and parallel"}, @@ -5427,7 +5398,7 @@ async def test_nested_graph_interrupts_parallel( async def test_doubly_nested_graph_interrupts( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): my_key: str @@ -5480,13 +5451,11 @@ async def test_doubly_nested_graph_interrupts( # test invoke w/ nested interrupt config = {"configurable": {"thread_id": "1"}} - assert await app.ainvoke( - {"my_key": "my value"}, config, checkpoint_during=checkpoint_during - ) == { + assert await app.ainvoke({"my_key": "my value"}, config, durability=durability) == { "my_key": "hi my value", } - assert await app.ainvoke(None, config, checkpoint_during=checkpoint_during) == { + assert await app.ainvoke(None, config, durability=durability) == { "my_key": "hi my value here and there and back again", } @@ -5498,16 +5467,14 @@ async def test_doubly_nested_graph_interrupts( assert [ c async for c in app.astream( - {"my_key": "my value"}, config, checkpoint_during=checkpoint_during + {"my_key": "my value"}, config, durability=durability ) ] == [ {"parent_1": {"my_key": "hi my value"}}, {"__interrupt__": ()}, ] assert nodes == ["parent_1", "grandchild_1"] - assert [ - c async for c in app.astream(None, config, checkpoint_during=checkpoint_during) - ] == [ + assert [c async for c in app.astream(None, config, durability=durability)] == [ {"child": {"my_key": "hi my value here and there"}}, {"parent_2": {"my_key": "hi my value here and there and back again"}}, ] @@ -5528,7 +5495,7 @@ async def test_doubly_nested_graph_interrupts( {"my_key": "my value"}, config, stream_mode="values", - checkpoint_during=checkpoint_during, + durability=durability, ) ] == [ {"my_key": "my value"}, @@ -5537,7 +5504,7 @@ async def test_doubly_nested_graph_interrupts( assert [ c async for c in app.astream( - None, config, stream_mode="values", checkpoint_during=checkpoint_during + None, config, stream_mode="values", durability=durability ) ] == [ {"my_key": "hi my value"}, @@ -5837,7 +5804,7 @@ async def test_debug_retry(async_checkpointer: BaseCheckpointSaver): graph = builder.compile(checkpointer=async_checkpointer) config = {"configurable": {"thread_id": "1"}} - await graph.ainvoke({"messages": []}, config=config, checkpoint_during=True) + await graph.ainvoke({"messages": []}, config=config, durability="async") # re-run step: 1 async for c in async_checkpointer.alist(config): @@ -5851,7 +5818,7 @@ async def test_debug_retry(async_checkpointer: BaseCheckpointSaver): events = [ c async for c in graph.astream( - None, config=update_config, stream_mode="debug", checkpoint_during=True + None, config=update_config, stream_mode="debug", durability="async" ) ] @@ -5884,7 +5851,7 @@ async def test_debug_retry(async_checkpointer: BaseCheckpointSaver): async def test_debug_subgraphs( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ): class State(TypedDict): messages: Annotated[list[str], operator.add] @@ -5919,14 +5886,14 @@ async def test_debug_subgraphs( {"messages": []}, config=config, stream_mode="debug", - checkpoint_during=checkpoint_during, + durability=durability, ) ] checkpoint_events = list( reversed([e["payload"] for e in events if e["type"] == "checkpoint"]) ) - if not checkpoint_during: + if durability == "exit": checkpoint_events = checkpoint_events[:1] checkpoint_history = [c async for c in graph.aget_state_history(config)] @@ -5955,7 +5922,7 @@ async def test_debug_subgraphs( async def test_debug_nested_subgraphs( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: from collections import defaultdict @@ -6000,7 +5967,7 @@ async def test_debug_nested_subgraphs( config=config, stream_mode="debug", subgraphs=True, - checkpoint_during=checkpoint_during, + durability=durability, ) ] @@ -6045,9 +6012,9 @@ async def test_debug_nested_subgraphs( for checkpoint_events, checkpoint_history, ns in zip( stream_ns.values(), history_ns.values(), stream_ns.keys() ): - if not checkpoint_during: + if durability == "exit": checkpoint_events = checkpoint_events[-1:] - if ns: # Save no checkpoints for subgraphs when checkpoint_during=False + if ns: # Save no checkpoints for subgraphs when durability="exit" assert not checkpoint_history continue assert len(checkpoint_events) == len(checkpoint_history) @@ -6101,7 +6068,7 @@ async def test_parent_command( config = {"configurable": {"thread_id": "1"}} assert await graph.ainvoke( - {"messages": [("user", "get user name")]}, config, checkpoint_during=False + {"messages": [("user", "get user name")]}, config, durability="exit" ) == { "messages": [ _AnyIdHumanMessage( @@ -6606,7 +6573,7 @@ async def test_concurrent_execution(): async def test_checkpoint_recovery_async( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: """Test recovery from checkpoints after failures with async nodes.""" @@ -6639,7 +6606,7 @@ async def test_checkpoint_recovery_async( await graph.ainvoke( {"steps": ["start"], "attempt": 1}, config, - checkpoint_during=checkpoint_during, + durability=durability, ) # Verify checkpoint state @@ -6650,13 +6617,13 @@ async def test_checkpoint_recovery_async( # Retry with updated attempt count result = await graph.ainvoke( - {"steps": [], "attempt": 2}, config, checkpoint_during=checkpoint_during + {"steps": [], "attempt": 2}, config, durability=durability ) assert result == {"steps": ["start", "node1", "node2"], "attempt": 2} # Verify checkpoint history shows both attempts history = [c async for c in graph.aget_state_history(config)] - if checkpoint_during: + if durability != "exit": assert len(history) == 6 # Initial + failed attempt + successful attempt else: assert len(history) == 2 # error + success @@ -8112,7 +8079,7 @@ async def test_bulk_state_updates(async_checkpointer: BaseCheckpointSaver) -> No async def test_update_as_input( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): foo: str @@ -8135,13 +8102,13 @@ async def test_update_as_input( assert await graph.ainvoke( {"foo": "input"}, {"configurable": {"thread_id": "1"}}, - checkpoint_during=checkpoint_during, + durability=durability, ) == {"foo": "tool"} assert await graph.ainvoke( {"foo": "input"}, {"configurable": {"thread_id": "1"}}, - checkpoint_during=checkpoint_during, + durability=durability, ) == {"foo": "tool"} def map_snapshot(i: StateSnapshot) -> dict: @@ -8180,14 +8147,14 @@ async def test_update_as_input( async for s in graph.aget_state_history({"configurable": {"thread_id": "2"}}) ] - if checkpoint_during: + if durability != "exit": assert new_history == history else: assert [new_history[0], new_history[4]] == history async def test_batch_update_as_input( - async_checkpointer: BaseCheckpointSaver, checkpoint_during: bool + async_checkpointer: BaseCheckpointSaver, durability: Durability ) -> None: class State(TypedDict): foo: str @@ -8222,7 +8189,7 @@ async def test_batch_update_as_input( assert await graph.ainvoke( {"foo": "input"}, {"configurable": {"thread_id": "1"}}, - checkpoint_during=checkpoint_during, + durability=durability, ) == {"foo": "map", "tasks": [0, 1, 2]} def map_snapshot(i: StateSnapshot) -> dict: @@ -8273,7 +8240,7 @@ async def test_batch_update_as_input( async for s in graph.aget_state_history({"configurable": {"thread_id": "2"}}) ] - if checkpoint_during: + if durability != "exit": assert new_history == history else: assert new_history[:1] == history From 03bec97767197872ccb4a1173e4bf820d922ff25 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Sun, 20 Jul 2025 19:46:38 -0400 Subject: [PATCH 16/22] feat(sdk-py): add `interrupts` to thread state (#5580) --- libs/sdk-py/langgraph_sdk/schema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/sdk-py/langgraph_sdk/schema.py b/libs/sdk-py/langgraph_sdk/schema.py index 13b525bca..d68bf6439 100644 --- a/libs/sdk-py/langgraph_sdk/schema.py +++ b/libs/sdk-py/langgraph_sdk/schema.py @@ -280,6 +280,8 @@ class ThreadState(TypedDict): """The ID of the parent checkpoint. If missing, this is the root checkpoint.""" tasks: Sequence[ThreadTask] """Tasks to execute in this step. If already attempted, may contain an error.""" + interrupts: list[Interrupt] + """Interrupts which were thrown in this thread.""" class ThreadUpdateStateResponse(TypedDict): From 2a86abb8c488adb7e58943fc646f14564d499025 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:43:22 -0400 Subject: [PATCH 17/22] chore: lint v1 branch (due to auto merges) (#5607) --- libs/langgraph/langgraph/graph/state.py | 4 ++-- libs/langgraph/uv.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 559bd021b..23ece0d65 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -1240,7 +1240,7 @@ def _control_branch(value: Any) -> Sequence[tuple[str, Any]]: for command in commands: if command.graph == Command.PARENT: raise ParentCommand(command) - + goto_targets = ( [command.goto] if isinstance(command.goto, (Send, str)) else command.goto ) @@ -1251,7 +1251,7 @@ def _control_branch(value: Any) -> Sequence[tuple[str, Any]]: elif isinstance(go, str) and go != END: # END is a special case, it's not actually a node in a practical sense # but rather a special terminal node that we don't need to branch to - rtn.append((CHANNEL_BRANCH_TO.format(go), None)) + rtn.append((_CHANNEL_BRANCH_TO.format(go), None)) return rtn diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index 49c7906c1..121887e8f 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -1394,7 +1394,7 @@ dev = [ [[package]] name = "langgraph-cli" -version = "0.3.4" +version = "0.3.5" source = { editable = "../cli" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, From 9e3cb1f03414d1f70e48dbf8b328713aa4854d24 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:48:12 -0400 Subject: [PATCH 18/22] feat(sdk-py): sdk support for `context` API (#5566) Adding support for the `context` arg to `invoke/stream` to the sdk. This is paired with an update to the API as well that adds `context` support to the `assistants` and `runs` endpoints. Bumping version to v0.2.0 on the `v1` branch given this and the interrupt schema changes. --- libs/langgraph/pyproject.toml | 2 +- libs/langgraph/uv.lock | 2 +- libs/prebuilt/uv.lock | 2 +- libs/sdk-py/langgraph_sdk/auth/types.py | 15 ++- libs/sdk-py/langgraph_sdk/client.py | 135 ++++++++++++++++++++---- libs/sdk-py/langgraph_sdk/schema.py | 11 ++ libs/sdk-py/pyproject.toml | 2 +- libs/sdk-py/uv.lock | 2 +- 8 files changed, 141 insertions(+), 30 deletions(-) diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index 661fe3007..065815c66 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -14,7 +14,7 @@ license-files = ['LICENSE'] dependencies = [ "langchain-core>=0.1", "langgraph-checkpoint>=2.1.0,<3.0.0", - "langgraph-sdk>=0.1.42,<0.2.0", + "langgraph-sdk>=0.2.0,<0.3.0", "langgraph-prebuilt>=0.5.0,<0.6.0", "xxhash>=3.5.0", "pydantic>=2.7.4", diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index 121887e8f..f8e4284c5 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -1481,7 +1481,7 @@ wheels = [ [[package]] name = "langgraph-sdk" -version = "0.1.74" +version = "0.2.0" source = { editable = "../sdk-py" } dependencies = [ { name = "httpx" }, diff --git a/libs/prebuilt/uv.lock b/libs/prebuilt/uv.lock index 1d39ba961..494facc31 100644 --- a/libs/prebuilt/uv.lock +++ b/libs/prebuilt/uv.lock @@ -507,7 +507,7 @@ dev = [ [[package]] name = "langgraph-sdk" -version = "0.1.74" +version = "0.2.0" source = { editable = "../sdk-py" } dependencies = [ { name = "httpx" }, diff --git a/libs/sdk-py/langgraph_sdk/auth/types.py b/libs/sdk-py/langgraph_sdk/auth/types.py index c9a5bd523..c0260ee72 100644 --- a/libs/sdk-py/langgraph_sdk/auth/types.py +++ b/libs/sdk-py/langgraph_sdk/auth/types.py @@ -556,7 +556,8 @@ class AssistantsCreate(typing.TypedDict, total=False): create_params = { "assistant_id": UUID("123e4567-e89b-12d3-a456-426614174000"), "graph_id": "graph123", - "config": {"key": "value"}, + "config": {"tags": ["tag1", "tag2"]}, + "context": {"key": "value"}, "metadata": {"owner": "user123"}, "if_exists": "do_nothing", "name": "Assistant 1" @@ -570,9 +571,11 @@ class AssistantsCreate(typing.TypedDict, total=False): graph_id: str """Graph ID to use for this assistant.""" - config: dict[str, typing.Any] | typing.Any | None + config: dict[str, typing.Any] """typing.Optional configuration for the assistant.""" + context: dict[str, typing.Any] + metadata: MetadataInput """typing.Optional metadata to attach to the assistant.""" @@ -610,7 +613,8 @@ class AssistantsUpdate(typing.TypedDict, total=False): update_params = { "assistant_id": UUID("123e4567-e89b-12d3-a456-426614174000"), "graph_id": "graph123", - "config": {"key": "value"}, + "config": {"tags": ["tag1", "tag2"]}, + "context": {"key": "value"}, "metadata": {"owner": "user123"}, "name": "Assistant 1", "version": 1 @@ -624,9 +628,12 @@ class AssistantsUpdate(typing.TypedDict, total=False): graph_id: str | None """typing.Optional graph ID to update.""" - config: dict[str, typing.Any] | typing.Any | None + config: dict[str, typing.Any] """typing.Optional configuration to update.""" + context: dict[str, typing.Any] + """The static context of the assistant.""" + metadata: MetadataInput """typing.Optional metadata to update.""" diff --git a/libs/sdk-py/langgraph_sdk/client.py b/libs/sdk-py/langgraph_sdk/client.py index 00ace666c..32447e863 100644 --- a/libs/sdk-py/langgraph_sdk/client.py +++ b/libs/sdk-py/langgraph_sdk/client.py @@ -39,6 +39,7 @@ from langgraph_sdk.schema import ( Checkpoint, Command, Config, + Context, Cron, CronSortBy, DisconnectMode, @@ -646,9 +647,9 @@ class AssistantsClient: } } }, - 'config_schema': + 'context_schema': { - 'title': 'Configurable', + 'title': 'Context', 'type': 'object', 'properties': { @@ -706,6 +707,7 @@ class AssistantsClient: graph_id: str | None, config: Config | None = None, *, + context: Context | None = None, metadata: Json = None, assistant_id: str | None = None, if_exists: OnConflictBehavior | None = None, @@ -721,6 +723,8 @@ class AssistantsClient: graph_id: The ID of the graph the assistant should use. The graph ID is normally set in your langgraph.json configuration. config: Configuration to use for the graph. metadata: Metadata to add to assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" assistant_id: Assistant ID to use, will default to a random UUID if not provided. if_exists: How to handle duplicate creation. Defaults to 'raise' under the hood. Must be either 'raise' (raise error if duplicate), or 'do_nothing' (return existing assistant). @@ -738,7 +742,7 @@ class AssistantsClient: client = get_client(url="http://localhost:2024") assistant = await client.assistants.create( graph_id="agent", - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, metadata={"number":1}, assistant_id="my-assistant-id", if_exists="do_nothing", @@ -751,6 +755,8 @@ class AssistantsClient: } if config: payload["config"] = config + if context: + payload["context"] = context if metadata: payload["metadata"] = metadata if assistant_id: @@ -769,6 +775,7 @@ class AssistantsClient: *, graph_id: str | None = None, config: Config | None = None, + context: Context | None = None, metadata: Json = None, name: str | None = None, headers: dict[str, str] | None = None, @@ -783,6 +790,8 @@ class AssistantsClient: graph_id: The ID of the graph the assistant should use. The graph ID is normally set in your langgraph.json configuration. If None, assistant will keep pointing to same graph. config: Configuration to use for the graph. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" metadata: Metadata to merge with existing assistant metadata. name: The new name for the assistant. headers: Optional custom headers to include with the request. @@ -799,7 +808,7 @@ class AssistantsClient: assistant = await client.assistants.update( assistant_id='e280dad7-8618-443f-87f1-8e41841c180f', graph_id="other-graph", - config={"configurable": {"model_name": "anthropic"}}, + context={"model_name": "anthropic"}, metadata={"number":2} ) ``` @@ -810,6 +819,8 @@ class AssistantsClient: payload["graph_id"] = graph_id if config: payload["config"] = config + if context: + payload["context"] = context if metadata: payload["metadata"] = metadata if name: @@ -1482,7 +1493,7 @@ class ThreadsClient: class RunsClient: """Client for managing runs in LangGraph. - A run is a single assistant invocation with optional input, config, and metadata. + A run is a single assistant invocation with optional input, config, context, and metadata. This client manages runs, which can be stateful (on threads) or stateless. ???+ example "Example" @@ -1509,6 +1520,7 @@ class RunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -1562,6 +1574,7 @@ class RunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -1592,6 +1605,8 @@ class RunsClient: If true, the stream can be resumed and replayed in its entirety even after disconnection. metadata: Metadata to assign to the run. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint: The checkpoint to resume from. checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. @@ -1623,7 +1638,7 @@ class RunsClient: input={"messages": [{"role": "user", "content": "how are you?"}]}, stream_mode=["values","debug"], metadata={"name":"my_run"}, - config={"configurable": {"model_name": "anthropic"}}, + context={"model_name": "anthropic"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], feedback_keys=["my_feedback_key_1","my_feedback_key_2"], @@ -1650,6 +1665,7 @@ class RunsClient: {k: v for k, v in command.items() if v is not None} if command else None ), "config": config, + "context": context, "metadata": metadata, "stream_mode": stream_mode, "stream_subgraphs": stream_subgraphs, @@ -1701,6 +1717,7 @@ class RunsClient: metadata: dict | None = None, checkpoint_during: bool | None = None, config: Config | None = None, + context: Context | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, webhook: str | None = None, @@ -1724,6 +1741,7 @@ class RunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -1749,6 +1767,7 @@ class RunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -1777,6 +1796,8 @@ class RunsClient: If true, the stream can be resumed and replayed in its entirety even after disconnection. metadata: Metadata to assign to the run. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint: The checkpoint to resume from. checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. @@ -1805,7 +1826,7 @@ class RunsClient: assistant_id="my_assistant_id", input={"messages": [{"role": "user", "content": "hello!"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", @@ -1849,10 +1870,13 @@ class RunsClient: 'graph_id': 'agent', 'thread_id': 'my_thread_id', 'checkpoint_id': None, - 'model_name': "openai", 'assistant_id': 'my_assistant_id' - } + }, }, + 'context': + { + 'model_name': 'openai' + } 'webhook': "https://my.fake.webhook.com", 'temporary': False, 'stream_mode': ['values'], @@ -1873,6 +1897,7 @@ class RunsClient: "stream_subgraphs": stream_subgraphs, "stream_resumable": stream_resumable, "config": config, + "context": context, "metadata": metadata, "assistant_id": assistant_id, "interrupt_before": interrupt_before, @@ -1919,6 +1944,7 @@ class RunsClient: command: Command | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -1944,6 +1970,7 @@ class RunsClient: command: Command | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, @@ -1966,6 +1993,7 @@ class RunsClient: command: Command | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -1992,6 +2020,8 @@ class RunsClient: command: A command to execute. Cannot be combined with input. metadata: Metadata to assign to the run. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint: The checkpoint to resume from. checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. @@ -2022,7 +2052,7 @@ class RunsClient: assistant_id="agent", input={"messages": [{"role": "user", "content": "how are you?"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "anthropic"}}, + context={"model_name": "anthropic"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", @@ -2068,6 +2098,7 @@ class RunsClient: {k: v for k, v in command.items() if v is not None} if command else None ), "config": config, + "context": context, "metadata": metadata, "assistant_id": assistant_id, "interrupt_before": interrupt_before, @@ -2332,7 +2363,7 @@ class RunsClient: class CronClient: """Client for managing recurrent runs (cron jobs) in LangGraph. - A run is a single invocation of an assistant with optional input and config. + A run is a single invocation of an assistant with optional input, config, and context. This client allows scheduling recurring runs to occur automatically. ???+ example "Example Usage" @@ -2365,6 +2396,7 @@ class CronClient: input: dict | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, interrupt_before: All | list[str] | None = None, interrupt_after: All | list[str] | None = None, @@ -2382,6 +2414,8 @@ class CronClient: input: The input to the graph. metadata: Metadata to assign to the cron job runs. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. @@ -2405,7 +2439,7 @@ class CronClient: schedule="27 15 * * *", input={"messages": [{"role": "user", "content": "hello!"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", @@ -2418,6 +2452,7 @@ class CronClient: "input": input, "config": config, "metadata": metadata, + "context": context, "assistant_id": assistant_id, "checkpoint_during": checkpoint_during, "interrupt_before": interrupt_before, @@ -2439,6 +2474,7 @@ class CronClient: input: dict | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, interrupt_before: All | list[str] | None = None, interrupt_after: All | list[str] | None = None, @@ -2455,6 +2491,8 @@ class CronClient: input: The input to the graph. metadata: Metadata to assign to the cron job runs. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. interrupt_after: Nodes to Nodes to interrupt immediately after they get executed. @@ -2475,7 +2513,7 @@ class CronClient: schedule="27 15 * * *", input={"messages": [{"role": "user", "content": "hello!"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", @@ -2489,6 +2527,7 @@ class CronClient: "input": input, "config": config, "metadata": metadata, + "context": context, "assistant_id": assistant_id, "checkpoint_during": checkpoint_during, "interrupt_before": interrupt_before, @@ -3216,6 +3255,7 @@ class SyncAssistantsClient: 'created_at': '2024-06-25T17:10:33.109781+00:00', 'updated_at': '2024-06-25T17:10:33.109781+00:00', 'config': {}, + 'context': {}, 'metadata': {'created_by': 'system'} } ``` @@ -3379,6 +3419,20 @@ class SyncAssistantsClient: 'type': 'string' } } + }, + 'context_schema': + { + 'title': 'Context', + 'type': 'object', + 'properties': + { + 'model_name': + { + 'title': 'Model Name', + 'enum': ['anthropic', 'openai'], + 'type': 'string' + } + } } } ``` @@ -3422,6 +3476,7 @@ class SyncAssistantsClient: graph_id: str | None, config: Config | None = None, *, + context: Context | None = None, metadata: Json = None, assistant_id: str | None = None, if_exists: OnConflictBehavior | None = None, @@ -3436,6 +3491,8 @@ class SyncAssistantsClient: Args: graph_id: The ID of the graph the assistant should use. The graph ID is normally set in your langgraph.json configuration. config: Configuration to use for the graph. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" metadata: Metadata to add to assistant. assistant_id: Assistant ID to use, will default to a random UUID if not provided. if_exists: How to handle duplicate creation. Defaults to 'raise' under the hood. @@ -3454,7 +3511,7 @@ class SyncAssistantsClient: client = get_sync_client(url="http://localhost:2024") assistant = client.assistants.create( graph_id="agent", - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, metadata={"number":1}, assistant_id="my-assistant-id", if_exists="do_nothing", @@ -3467,6 +3524,8 @@ class SyncAssistantsClient: } if config: payload["config"] = config + if context: + payload["context"] = context if metadata: payload["metadata"] = metadata if assistant_id: @@ -3485,6 +3544,7 @@ class SyncAssistantsClient: *, graph_id: str | None = None, config: Config | None = None, + context: Context | None = None, metadata: Json = None, name: str | None = None, headers: dict[str, str] | None = None, @@ -3499,6 +3559,8 @@ class SyncAssistantsClient: graph_id: The ID of the graph the assistant should use. The graph ID is normally set in your langgraph.json configuration. If None, assistant will keep pointing to same graph. config: Configuration to use for the graph. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" metadata: Metadata to merge with existing assistant metadata. name: The new name for the assistant. headers: Optional custom headers to include with the request. @@ -3515,7 +3577,7 @@ class SyncAssistantsClient: assistant = client.assistants.update( assistant_id='e280dad7-8618-443f-87f1-8e41841c180f', graph_id="other-graph", - config={"configurable": {"model_name": "anthropic"}}, + context={"model_name": "anthropic"}, metadata={"number":2} ) ``` @@ -3525,6 +3587,8 @@ class SyncAssistantsClient: payload["graph_id"] = graph_id if config: payload["config"] = config + if context: + payload["context"] = context if metadata: payload["metadata"] = metadata if name: @@ -4220,6 +4284,7 @@ class SyncRunsClient: stream_subgraphs: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -4248,6 +4313,7 @@ class SyncRunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, @@ -4273,6 +4339,7 @@ class SyncRunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -4303,6 +4370,8 @@ class SyncRunsClient: If true, the stream can be resumed and replayed in its entirety even after disconnection. metadata: Metadata to assign to the run. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint: The checkpoint to resume from. checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. @@ -4335,7 +4404,7 @@ class SyncRunsClient: input={"messages": [{"role": "user", "content": "how are you?"}]}, stream_mode=["values","debug"], metadata={"name":"my_run"}, - config={"configurable": {"model_name": "anthropic"}}, + context={"model_name": "anthropic"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], feedback_keys=["my_feedback_key_1","my_feedback_key_2"], @@ -4359,6 +4428,7 @@ class SyncRunsClient: {k: v for k, v in command.items() if v is not None} if command else None ), "config": config, + "context": context, "metadata": metadata, "stream_mode": stream_mode, "stream_subgraphs": stream_subgraphs, @@ -4409,6 +4479,7 @@ class SyncRunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, @@ -4433,6 +4504,7 @@ class SyncRunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -4458,6 +4530,7 @@ class SyncRunsClient: stream_resumable: bool = False, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -4486,6 +4559,8 @@ class SyncRunsClient: If true, the stream can be resumed and replayed in its entirety even after disconnection. metadata: Metadata to assign to the run. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint: The checkpoint to resume from. checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. @@ -4514,7 +4589,7 @@ class SyncRunsClient: assistant_id="my_assistant_id", input={"messages": [{"role": "user", "content": "hello!"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", @@ -4558,10 +4633,13 @@ class SyncRunsClient: 'graph_id': 'agent', 'thread_id': 'my_thread_id', 'checkpoint_id': None, - 'model_name': "openai", 'assistant_id': 'my_assistant_id' } }, + 'context': + { + 'model_name': 'openai' + }, 'webhook': "https://my.fake.webhook.com", 'temporary': False, 'stream_mode': ['values'], @@ -4582,6 +4660,7 @@ class SyncRunsClient: "stream_subgraphs": stream_subgraphs, "stream_resumable": stream_resumable, "config": config, + "context": context, "metadata": metadata, "assistant_id": assistant_id, "interrupt_before": interrupt_before, @@ -4630,6 +4709,7 @@ class SyncRunsClient: command: Command | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, checkpoint_during: bool | None = None, @@ -4654,6 +4734,7 @@ class SyncRunsClient: command: Command | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, interrupt_before: All | Sequence[str] | None = None, interrupt_after: All | Sequence[str] | None = None, @@ -4675,6 +4756,7 @@ class SyncRunsClient: command: Command | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, checkpoint: Checkpoint | None = None, checkpoint_id: str | None = None, @@ -4700,6 +4782,8 @@ class SyncRunsClient: command: The command to execute. metadata: Metadata to assign to the run. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint: The checkpoint to resume from. checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. @@ -4730,7 +4814,7 @@ class SyncRunsClient: assistant_id="agent", input={"messages": [{"role": "user", "content": "how are you?"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "anthropic"}}, + context={"model_name": "anthropic"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", @@ -4777,6 +4861,7 @@ class SyncRunsClient: {k: v for k, v in command.items() if v is not None} if command else None ), "config": config, + "context": context, "metadata": metadata, "assistant_id": assistant_id, "interrupt_before": interrupt_before, @@ -5059,6 +5144,7 @@ class SyncCronClient: metadata: dict | None = None, checkpoint_during: bool | None = None, config: Config | None = None, + context: Context | None = None, interrupt_before: All | list[str] | None = None, interrupt_after: All | list[str] | None = None, webhook: str | None = None, @@ -5075,6 +5161,8 @@ class SyncCronClient: input: The input to the graph. metadata: Metadata to assign to the cron job runs. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. interrupt_after: Nodes to Nodes to interrupt immediately after they get executed. @@ -5096,7 +5184,7 @@ class SyncCronClient: schedule="27 15 * * *", input={"messages": [{"role": "user", "content": "hello!"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", @@ -5109,6 +5197,7 @@ class SyncCronClient: "input": input, "config": config, "metadata": metadata, + "context": context, "assistant_id": assistant_id, "interrupt_before": interrupt_before, "interrupt_after": interrupt_after, @@ -5129,6 +5218,7 @@ class SyncCronClient: input: dict | None = None, metadata: dict | None = None, config: Config | None = None, + context: Context | None = None, checkpoint_during: bool | None = None, interrupt_before: All | list[str] | None = None, interrupt_after: All | list[str] | None = None, @@ -5145,6 +5235,8 @@ class SyncCronClient: input: The input to the graph. metadata: Metadata to assign to the cron job runs. config: The configuration for the assistant. + context: Static context to add to the assistant. + !!! version-added "Supported with langgraph>=0.6.0" checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption). interrupt_before: Nodes to interrupt immediately before they get executed. interrupt_after: Nodes to Nodes to interrupt immediately after they get executed. @@ -5165,7 +5257,7 @@ class SyncCronClient: schedule="27 15 * * *", input={"messages": [{"role": "user", "content": "hello!"}]}, metadata={"name":"my_run"}, - config={"configurable": {"model_name": "openai"}}, + context={"model_name": "openai"}, checkpoint_during=True, interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], @@ -5180,6 +5272,7 @@ class SyncCronClient: "input": input, "config": config, "metadata": metadata, + "context": context, "assistant_id": assistant_id, "interrupt_before": interrupt_before, "interrupt_after": interrupt_after, diff --git a/libs/sdk-py/langgraph_sdk/schema.py b/libs/sdk-py/langgraph_sdk/schema.py index ee3e74d95..18759c30b 100644 --- a/libs/sdk-py/langgraph_sdk/schema.py +++ b/libs/sdk-py/langgraph_sdk/schema.py @@ -12,6 +12,8 @@ from typing import ( TypedDict, ) +from typing_extensions import TypeAlias + Json = Optional[dict[str, Any]] """Represents a JSON-like structure, which can be None or a dictionary with string keys and any values.""" @@ -129,6 +131,8 @@ SortOrder = Literal["asc", "desc"] The order to sort by. """ +Context: TypeAlias = dict[str, Any] + class Config(TypedDict, total=False): """Configuration options for a call.""" @@ -183,6 +187,9 @@ class GraphSchema(TypedDict): config_schema: dict | None """The schema for the graph config. Missing if unable to generate JSON schema from graph.""" + context_schema: dict | None + """The schema for the graph context. + Missing if unable to generate JSON schema from graph.""" Subgraphs = dict[str, GraphSchema] @@ -197,6 +204,8 @@ class AssistantBase(TypedDict): """The ID of the graph.""" config: Config """The assistant config.""" + context: Context + """The static context of the assistant.""" created_at: datetime """The time the assistant was created.""" metadata: Json @@ -352,6 +361,8 @@ class RunCreate(TypedDict): """Additional metadata to associate with the run.""" config: Config | None """Configuration options for the run.""" + context: Context | None + """The static context of the run.""" checkpoint_id: str | None """The identifier of a checkpoint to resume from.""" interrupt_before: list[str] | None diff --git a/libs/sdk-py/pyproject.toml b/libs/sdk-py/pyproject.toml index 68ef541a1..48f19a2a7 100644 --- a/libs/sdk-py/pyproject.toml +++ b/libs/sdk-py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "langgraph-sdk" -version = "0.1.74" +version = "0.2.0" description = "SDK for interacting with LangGraph API" authors = [] requires-python = ">=3.9" diff --git a/libs/sdk-py/uv.lock b/libs/sdk-py/uv.lock index 246dc37ba..bdf81803f 100644 --- a/libs/sdk-py/uv.lock +++ b/libs/sdk-py/uv.lock @@ -119,7 +119,7 @@ wheels = [ [[package]] name = "langgraph-sdk" -version = "0.1.74" +version = "0.2.0" source = { editable = "." } dependencies = [ { name = "httpx" }, From 29c3a579b37fab6a14e192ac27a90ee9bf8f9415 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:08:07 -0400 Subject: [PATCH 19/22] release(sdk-py): use v0.2.0a1 for testing with sdk (#5621) --- docs/docs/tutorials/tot/tot.ipynb | 20 ++++++++++------ docs/uv.lock | 40 +++++++++++++++---------------- libs/langgraph/uv.lock | 2 +- libs/prebuilt/uv.lock | 2 +- libs/sdk-py/pyproject.toml | 2 +- libs/sdk-py/uv.lock | 2 +- 6 files changed, 37 insertions(+), 31 deletions(-) diff --git a/docs/docs/tutorials/tot/tot.ipynb b/docs/docs/tutorials/tot/tot.ipynb index 360375ba7..137a324a3 100644 --- a/docs/docs/tutorials/tot/tot.ipynb +++ b/docs/docs/tutorials/tot/tot.ipynb @@ -313,19 +313,21 @@ " k: int\n", " beam_size: int\n", "\n", + "\n", "class EnsuredContext(TypedDict):\n", " max_depth: int\n", " threshold: float\n", " k: int\n", " beam_size: int\n", "\n", + "\n", "def _ensure_context(ctx: Context) -> EnsuredContext:\n", " \"\"\"Get params that configure the search algorithm.\"\"\"\n", " return {\n", " \"max_depth\": ctx.get(\"max_depth\", 10),\n", " \"threshold\": ctx.get(\"threshold\", 0.9),\n", " \"k\": ctx.get(\"k\", 5),\n", - " \"beam_size\": ctx.get(\"beam_size\", 3)\n", + " \"beam_size\": ctx.get(\"beam_size\", 3),\n", " }\n", "\n", "\n", @@ -333,7 +335,9 @@ " seed: Optional[Candidate]\n", "\n", "\n", - "def expand(state: ExpansionState, *, runtime: Runtime[Context]) -> Dict[str, List[Candidate]]:\n", + "def expand(\n", + " state: ExpansionState, *, runtime: Runtime[Context]\n", + ") -> Dict[str, List[Candidate]]:\n", " \"\"\"Generate the next state.\"\"\"\n", " ctx = _ensure_context(runtime.context)\n", " if not state.get(\"seed\"):\n", @@ -365,9 +369,7 @@ " return {\"scored_candidates\": scored, \"candidates\": \"clear\"}\n", "\n", "\n", - "def prune(\n", - " state: ToTState, *, runtime: Runtime[Context]\n", - ") -> Dict[str, Any]:\n", + "def prune(state: ToTState, *, runtime: Runtime[Context]) -> Dict[str, Any]:\n", " scored_candidates = state[\"scored_candidates\"]\n", " beam_size = _ensure_context(runtime.context)[\"beam_size\"]\n", " organized = sorted(\n", @@ -469,7 +471,11 @@ } ], "source": [ - "for step in graph.stream({\"problem\": puzzles[42]}, config={\"configurable\": {\"thread_id\": \"test_1\"}}, context={\"depth\": 10}):\n", + "for step in graph.stream(\n", + " {\"problem\": puzzles[42]},\n", + " config={\"configurable\": {\"thread_id\": \"test_1\"}},\n", + " context={\"depth\": 10},\n", + "):\n", " print(step)" ] }, @@ -487,7 +493,7 @@ } ], "source": [ - "final_state = graph.get_state({'configurable': {'thread_id': 'test_1'}})\n", + "final_state = graph.get_state({\"configurable\": {\"thread_id\": \"test_1\"}})\n", "winning_solution = final_state.values[\"candidates\"][0]\n", "search_depth = final_state.values[\"depth\"]\n", "if winning_solution[1] == 1:\n", diff --git a/docs/uv.lock b/docs/uv.lock index 9f90723ac..11eb9f695 100644 --- a/docs/uv.lock +++ b/docs/uv.lock @@ -15,16 +15,16 @@ name = "ag2" version = "0.9.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio" }, - { name = "asyncer" }, - { name = "diskcache" }, - { name = "docker" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "termcolor" }, - { name = "tiktoken" }, + { name = "anyio", marker = "python_full_version < '3.13'" }, + { name = "asyncer", marker = "python_full_version < '3.13'" }, + { name = "diskcache", marker = "python_full_version < '3.13'" }, + { name = "docker", marker = "python_full_version < '3.13'" }, + { name = "httpx", marker = "python_full_version < '3.13'" }, + { name = "packaging", marker = "python_full_version < '3.13'" }, + { name = "pydantic", marker = "python_full_version < '3.13'" }, + { name = "python-dotenv", marker = "python_full_version < '3.13'" }, + { name = "termcolor", marker = "python_full_version < '3.13'" }, + { name = "tiktoken", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/15/edfbbf217e19ea647225b3ab72a6e3755d2677665f1a7f8e5108da3feabd/ag2-0.9.6.tar.gz", hash = "sha256:d6f7812b1a49654d14113fa3c13ccb593115dee1193744ca428d7178d2b32090", size = 3356270, upload-time = "2025-07-08T14:56:21.63Z" } wheels = [ @@ -267,7 +267,7 @@ name = "asyncer" version = "0.0.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio" }, + { name = "anyio", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217, upload-time = "2024-08-24T23:15:36.449Z" } wheels = [ @@ -288,7 +288,7 @@ name = "autogen" version = "0.9.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ag2" }, + { name = "ag2", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/67/b9/dc958031b7e08ee50e3d40f5991f4c0bc21538df8d53aa3e9a9f2e2f7818/autogen-0.9.6.tar.gz", hash = "sha256:dc2efbeef61002608983afb120e62f8a109815eb741bcbc9ef398dcff7424a30", size = 43422, upload-time = "2025-07-08T14:56:17.6Z" } wheels = [ @@ -914,9 +914,9 @@ name = "docker" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "requests" }, - { name = "urllib3" }, + { name = "pywin32", marker = "python_full_version < '3.13' and sys_platform == 'win32'" }, + { name = "requests", marker = "python_full_version < '3.13'" }, + { name = "urllib3", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } wheels = [ @@ -2337,7 +2337,7 @@ wheels = [ [[package]] name = "langgraph" -version = "0.5.2" +version = "0.6.0a1" source = { editable = "../libs/langgraph" } dependencies = [ { name = "langchain-core" }, @@ -2365,7 +2365,7 @@ dev = [ { name = "langgraph-checkpoint", editable = "../libs/checkpoint" }, { name = "langgraph-checkpoint-postgres", editable = "../libs/checkpoint-postgres" }, { name = "langgraph-checkpoint-sqlite", editable = "../libs/checkpoint-sqlite" }, - { name = "langgraph-cli", extras = ["inmem"] }, + { name = "langgraph-cli", extras = ["inmem"], editable = "../libs/cli" }, { name = "langgraph-prebuilt", editable = "../libs/prebuilt" }, { name = "langgraph-sdk", editable = "../libs/sdk-py" }, { name = "mypy" }, @@ -2388,7 +2388,7 @@ dev = [ [[package]] name = "langgraph-checkpoint" -version = "2.1.0" +version = "2.1.1" source = { editable = "../libs/checkpoint" } dependencies = [ { name = "langchain-core" }, @@ -2433,7 +2433,7 @@ wheels = [ [[package]] name = "langgraph-checkpoint-postgres" -version = "2.0.21" +version = "2.0.23" source = { editable = "../libs/checkpoint-postgres" } dependencies = [ { name = "langgraph-checkpoint" }, @@ -2674,7 +2674,7 @@ dev = [ [[package]] name = "langgraph-sdk" -version = "0.1.72" +version = "0.2.0a1" source = { editable = "../libs/sdk-py" } dependencies = [ { name = "httpx" }, diff --git a/libs/langgraph/uv.lock b/libs/langgraph/uv.lock index f8e4284c5..f979cf4d7 100644 --- a/libs/langgraph/uv.lock +++ b/libs/langgraph/uv.lock @@ -1481,7 +1481,7 @@ wheels = [ [[package]] name = "langgraph-sdk" -version = "0.2.0" +version = "0.2.0a1" source = { editable = "../sdk-py" } dependencies = [ { name = "httpx" }, diff --git a/libs/prebuilt/uv.lock b/libs/prebuilt/uv.lock index 494facc31..a300dc1a3 100644 --- a/libs/prebuilt/uv.lock +++ b/libs/prebuilt/uv.lock @@ -507,7 +507,7 @@ dev = [ [[package]] name = "langgraph-sdk" -version = "0.2.0" +version = "0.2.0a1" source = { editable = "../sdk-py" } dependencies = [ { name = "httpx" }, diff --git a/libs/sdk-py/pyproject.toml b/libs/sdk-py/pyproject.toml index 48f19a2a7..df8c94fe7 100644 --- a/libs/sdk-py/pyproject.toml +++ b/libs/sdk-py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "langgraph-sdk" -version = "0.2.0" +version = "0.2.0a1" description = "SDK for interacting with LangGraph API" authors = [] requires-python = ">=3.9" diff --git a/libs/sdk-py/uv.lock b/libs/sdk-py/uv.lock index bdf81803f..8e9b727e0 100644 --- a/libs/sdk-py/uv.lock +++ b/libs/sdk-py/uv.lock @@ -119,7 +119,7 @@ wheels = [ [[package]] name = "langgraph-sdk" -version = "0.2.0" +version = "0.2.0a1" source = { editable = "." } dependencies = [ { name = "httpx" }, From d1ee1cf1f16339c0ceaaa96bf676f498a5c1d730 Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Tue, 22 Jul 2025 11:19:08 -0400 Subject: [PATCH 20/22] docs fix --- docs/docs/tutorials/tot/tot.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tutorials/tot/tot.ipynb b/docs/docs/tutorials/tot/tot.ipynb index 137a324a3..f8b8fbd0f 100644 --- a/docs/docs/tutorials/tot/tot.ipynb +++ b/docs/docs/tutorials/tot/tot.ipynb @@ -272,7 +272,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -280,7 +280,7 @@ "from typing import Optional, Dict, Any\n", "from typing_extensions import Annotated, TypedDict\n", "from langgraph.graph import StateGraph\n", - "from langgraph.types import Runtime\n", + "from langgraph.runtime import Runtime\n", "\n", "from langgraph.checkpoint.memory import InMemorySaver\n", "from langgraph.types import Send\n", From 56a9ce57b10991f69f7734dcbf3b953bdc5877b9 Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Tue, 22 Jul 2025 12:46:56 -0400 Subject: [PATCH 21/22] docs build fixes --- .../docs/cloud/how-tos/configuration_cloud.md | 4 +-- docs/docs/concepts/assistants.md | 8 ++--- docs/docs/concepts/low_level.md | 33 +++++++++++++------ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/docs/cloud/how-tos/configuration_cloud.md b/docs/docs/cloud/how-tos/configuration_cloud.md index 77f2a0aa6..1d1d62b61 100644 --- a/docs/docs/cloud/how-tos/configuration_cloud.md +++ b/docs/docs/cloud/how-tos/configuration_cloud.md @@ -2,7 +2,7 @@ In this guide we will show how to create, configure, and manage an [assistant](../../concepts/assistants.md). -First, as a brief refresher on the concept of runtime context, consider the following simple `call_model` node and configuration schema. Observe that this node tries to read and use the `model_provider` as defined by the `Runtime` object's `context` property. +First, as a brief refresher on the concept of runtime context, consider the following simple `call_model` node and context schema. Observe that this node tries to read and use the `model_provider` as defined by the `Runtime` object's `context` property. === "Python" @@ -43,7 +43,7 @@ First, as a brief refresher on the concept of runtime context, consider the foll } ``` -For more information on configurations, [see here](../../concepts/low_level.md#configuration). +For more information on runtime context, [see here](../../concepts/low_level.md#runtime-context). ## Create an assistant diff --git a/docs/docs/concepts/assistants.md b/docs/docs/concepts/assistants.md index feb79641b..c43af6616 100644 --- a/docs/docs/concepts/assistants.md +++ b/docs/docs/concepts/assistants.md @@ -1,6 +1,6 @@ # Assistants -**Assistants** allow you to manage configurations (like prompts, LLM selection, tools) separately from your graph's core logic, enabling rapid changes that don't alter the graph architecture. It is a way to create multiple specialized versions of the same graph architecture, each optimized for different use cases through configuration variations rather than structural changes. +**Assistants** allow you to manage configurations (like prompts, LLM selection, tools) separately from your graph's core logic, enabling rapid changes that don't alter the graph architecture. It is a way to create multiple specialized versions of the same graph architecture, each optimized for different use cases through context/configuration variations rather than structural changes. For example, imagine a general-purpose writing agent built on a common graph architecture. While the structure remains the same, different writing styles—such as blog posts and tweets—require tailored configurations to optimize performance. To support these variations, you can create multiple assistants (e.g., one for blogs and another for tweets) that share the underlying graph but differ in model selection and system prompt. @@ -14,8 +14,8 @@ The LangGraph Cloud API provides several endpoints for creating and managing ass ## Configuration -Assistants build on the LangGraph open source concept of [configuration](low_level.md#configuration). -While configuration is available in the open source LangGraph library, assistants are only present in [LangGraph Platform](langgraph_platform.md). This is due to the fact that assistants are tightly coupled to your deployed graph. Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default configuration settings. +Assistants build on the LangGraph open source concepts of configuration and [runtime context](low_level.md#runtime-context). +While these features are available in the open source LangGraph library, assistants are only present in [LangGraph Platform](langgraph_platform.md). This is due to the fact that assistants are tightly coupled to your deployed graph. Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default context and configuration settings. In practice, an assistant is just an _instance_ of a graph with a specific configuration. Therefore, multiple assistants can reference the same graph but can contain different configurations (e.g. prompts, models, tools). The LangGraph Server API provides several endpoints for creating and managing assistants. See the [API reference](../cloud/reference/api/api_ref.html) and [this how-to](../cloud/how-tos/configuration_cloud.md) for more details on how to create assistants. @@ -26,6 +26,6 @@ Once you've created an assistant, subsequent edits to that assistant will create ## Execution -A **run** is an invocation of an assistant. Each run may have its own input, configuration, and metadata, which may affect execution and output of the underlying graph. A run can optionally be executed on a [thread](./persistence.md#threads). +A **run** is an invocation of an assistant. Each run may have its own input, configuration, context, and metadata, which may affect execution and output of the underlying graph. A run can optionally be executed on a [thread](./persistence.md#threads). The LangGraph Platform API provides several endpoints for creating and managing runs. See the [API reference](../cloud/reference/api/api_ref.html#tag/thread-runs/) for more details. diff --git a/docs/docs/concepts/low_level.md b/docs/docs/concepts/low_level.md index 62b02125c..15d5db4e7 100644 --- a/docs/docs/concepts/low_level.md +++ b/docs/docs/concepts/low_level.md @@ -192,35 +192,48 @@ class State(MessagesState): ## Nodes -In LangGraph, nodes are typically python functions (sync or async) where the **first** positional argument is the [state](#state), and (optionally), the **second** positional argument is a "config", containing optional [configurable parameters](#configuration) (such as a `thread_id`). +In LangGraph, nodes are Python functions (either synchronous or asynchronous) that accept the following arguments: + +1. `state`: The [state](#state) of the graph +2. `config`: A `RunnableConfig` object that contains configuration information like `thread_id` and tracing information like `tags` +3. `runtime`: A `Runtime` object that contains [runtime `context`](#runtime-context) and other information like `store` and `stream_writer` + Similar to `NetworkX`, you add these nodes to a graph using the [add_node][langgraph.graph.StateGraph.add_node] method: ```python +from dataclasses import dataclass from typing_extensions import TypedDict from langchain_core.runnables import RunnableConfig from langgraph.graph import StateGraph +from langgraph.runtime import Runtime class State(TypedDict): input: str results: str +@dataclass +class Context: + user_id: str + builder = StateGraph(State) +def plain_node(state: State): + return state -def my_node(state: State, config: RunnableConfig): - print("In node: ", config["configurable"]["user_id"]) +def node_with_runtime(state: State, runtime: Runtime[Context]): + print("In node: ", runtime.context.user_id) + return {"results": f"Hello, {state['input']}!"} + +def node_with_config(state: State, config: RunnableConfig): + print("In node with thread_id: ", config["configurable"]["thread_id"]) return {"results": f"Hello, {state['input']}!"} -# The second argument is optional -def my_other_node(state: State): - return state - - -builder.add_node("my_node", my_node) -builder.add_node("other_node", my_other_node) +builder.add_node("plain_node", plain_node) +builder.add_node("node_with_runtime", node_with_runtime) +builder.add_node("node_with_config", node_with_config) ... ``` From 4a4c8db635196c5537ad8452a293cddcfb9456b4 Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Tue, 22 Jul 2025 12:54:21 -0400 Subject: [PATCH 22/22] fix header --- docs/docs/agents/context.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/docs/agents/context.md b/docs/docs/agents/context.md index c9338d50f..a84c28a4e 100644 --- a/docs/docs/agents/context.md +++ b/docs/docs/agents/context.md @@ -16,8 +16,6 @@ LangGraph provides **three** primary ways to supply context: | [**Short-term memory (State)**](#short-term-memory-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation | | [**Long-term memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations | -## Provide runtime context - ### Runtime Context !!! note "`config['configurable']` -> `runtime.context`"